Fossil SCM
Merge from trunk.
Commit
3eac814daa37e7dc3c0521b00cc6bf8167d7a4cf1644b67b35b62dfed19928b8
Parent
4850a59591a5301…
53 files changed
+2
+1
-1
+31
-16
+1
-1
+19
-8
+486
-287
+4
-2
+1
-1
+27
-6
+2
-1
+1
-1
+25
-5
+252
-70
+1
-1
+1
+194
-73
+1
+7
-1
+36
-7
+69
-30
+31
-1
+13
-8
+7
+2
-2
+1
-1
+2
-2
+89
-91
+17
-6
+51
-17
+5
-1
+25
+9
-9
+126
-237
+26
-26
+116
-12
+1
+1
-1
+1
-1
+12
-3
+183
-60
+2
-2
+1
-1
+1
-1
+63
-17
+19
-13
+37
-7
+1
-1
+18
-17
+5
+48
-17
+4
-1
+3
-3
~
.fossil-settings/crlf-glob
~
extsrc/pikchr-worker.js
~
extsrc/pikchr.c
~
extsrc/pikchr.js
~
extsrc/pikchr.wasm
~
extsrc/shell.c
~
extsrc/sqlite3.c
~
extsrc/sqlite3.h
~
src/add.c
~
src/alerts.c
~
src/allrepo.c
~
src/cache.c
~
src/cgi.c
~
src/checkin.c
~
src/checkout.c
~
src/configure.c
~
src/db.c
~
src/default.css
~
src/diff.c
~
src/diff.tcl
~
src/diffcmd.c
~
src/dispatch.c
~
src/export.c
~
src/file.c
~
src/finfo.c
~
src/graph.c
~
src/http_ssl.c
~
src/info.c
~
src/main.c
~
src/name.c
~
src/patch.c
~
src/printf.c
~
src/search.c
~
src/security_audit.c
~
src/setup.c
~
src/setupuser.c
~
src/sqlcmd.c
~
src/stash.c
~
src/tar.c
~
src/terminal.c
~
src/timeline.c
~
src/unversioned.c
~
src/update.c
~
src/user.c
~
src/util.c
~
src/wiki.c
~
src/wikiformat.c
~
src/zip.c
~
tools/mkindex.c
~
www/alerts.md
~
www/changes.wiki
~
www/fileformat.wiki
~
www/settings.wiki
| --- .fossil-settings/crlf-glob | ||
| +++ .fossil-settings/crlf-glob | ||
| @@ -1,5 +1,7 @@ | ||
| 1 | 1 | compat/zlib/* |
| 2 | 2 | setup/fossil.iss |
| 3 | 3 | test/th1-docs-input.txt |
| 4 | 4 | test/th1-hooks-input.txt |
| 5 | +win/build32.bat | |
| 6 | +win/build64.bat | |
| 5 | 7 | win/buildmsvc.bat |
| 6 | 8 |
| --- .fossil-settings/crlf-glob | |
| +++ .fossil-settings/crlf-glob | |
| @@ -1,5 +1,7 @@ | |
| 1 | compat/zlib/* |
| 2 | setup/fossil.iss |
| 3 | test/th1-docs-input.txt |
| 4 | test/th1-hooks-input.txt |
| 5 | win/buildmsvc.bat |
| 6 |
| --- .fossil-settings/crlf-glob | |
| +++ .fossil-settings/crlf-glob | |
| @@ -1,5 +1,7 @@ | |
| 1 | compat/zlib/* |
| 2 | setup/fossil.iss |
| 3 | test/th1-docs-input.txt |
| 4 | test/th1-hooks-input.txt |
| 5 | win/build32.bat |
| 6 | win/build64.bat |
| 7 | win/buildmsvc.bat |
| 8 |
+1
-1
| --- extsrc/pikchr-worker.js | ||
| +++ extsrc/pikchr-worker.js | ||
| @@ -206,11 +206,11 @@ | ||
| 206 | 206 | data:{step: ++f.last.step, text: text||null} |
| 207 | 207 | }); |
| 208 | 208 | } |
| 209 | 209 | }; |
| 210 | 210 | |
| 211 | - importScripts('pikchr-v7583078860.js'); | |
| 211 | + importScripts('pikchr-v2813665466.js'); | |
| 212 | 212 | /** |
| 213 | 213 | initPikchrModule() is installed via pikchr.js due to |
| 214 | 214 | building with: |
| 215 | 215 | |
| 216 | 216 | emcc ... -sMODULARIZE=1 -sEXPORT_NAME=initPikchrModule |
| 217 | 217 |
| --- extsrc/pikchr-worker.js | |
| +++ extsrc/pikchr-worker.js | |
| @@ -206,11 +206,11 @@ | |
| 206 | data:{step: ++f.last.step, text: text||null} |
| 207 | }); |
| 208 | } |
| 209 | }; |
| 210 | |
| 211 | importScripts('pikchr-v7583078860.js'); |
| 212 | /** |
| 213 | initPikchrModule() is installed via pikchr.js due to |
| 214 | building with: |
| 215 | |
| 216 | emcc ... -sMODULARIZE=1 -sEXPORT_NAME=initPikchrModule |
| 217 |
| --- extsrc/pikchr-worker.js | |
| +++ extsrc/pikchr-worker.js | |
| @@ -206,11 +206,11 @@ | |
| 206 | data:{step: ++f.last.step, text: text||null} |
| 207 | }); |
| 208 | } |
| 209 | }; |
| 210 | |
| 211 | importScripts('pikchr-v2813665466.js'); |
| 212 | /** |
| 213 | initPikchrModule() is installed via pikchr.js due to |
| 214 | building with: |
| 215 | |
| 216 | emcc ... -sMODULARIZE=1 -sEXPORT_NAME=initPikchrModule |
| 217 |
+31
-16
| --- extsrc/pikchr.c | ||
| +++ extsrc/pikchr.c | ||
| @@ -25,17 +25,17 @@ | ||
| 25 | 25 | ** The following is the concatenation of all %include directives from the |
| 26 | 26 | ** input grammar file: |
| 27 | 27 | */ |
| 28 | 28 | /************ Begin %include sections from the grammar ************************/ |
| 29 | 29 | #line 1 "VERSION.h" |
| 30 | -#define MANIFEST_UUID "052f07296e76ab2312caf2a4bf6237e574b3e533c7a36ee8f34db833baa3efb4" | |
| 31 | -#define MANIFEST_VERSION "[052f07296e]" | |
| 32 | -#define MANIFEST_DATE "2025-03-05 10:54:16" | |
| 30 | +#define MANIFEST_UUID "8a43b020141f772a0ac45291a7fd73041d2efba5e3665c6bd2f334ad9b2e9845" | |
| 31 | +#define MANIFEST_VERSION "[8a43b02014]" | |
| 32 | +#define MANIFEST_DATE "2025-03-19 16:19:43" | |
| 33 | 33 | #define MANIFEST_YEAR "2025" |
| 34 | -#define MANIFEST_ISODATE "20250305105416" | |
| 35 | -#define MANIFEST_NUMERIC_DATE 20250305 | |
| 36 | -#define MANIFEST_NUMERIC_TIME 105416 | |
| 34 | +#define MANIFEST_ISODATE "20250319161943" | |
| 35 | +#define MANIFEST_NUMERIC_DATE 20250319 | |
| 36 | +#define MANIFEST_NUMERIC_TIME 161943 | |
| 37 | 37 | #define RELEASE_VERSION "1.0" |
| 38 | 38 | #define RELEASE_VERSION_NUMBER 10000 |
| 39 | 39 | #define RELEASE_RESOURCE_VERSION 1,0,0,0 |
| 40 | 40 | #define COMPILER "gcc-13.3.0" |
| 41 | 41 | #line 2 "pikchr.y" |
| @@ -3687,42 +3687,57 @@ | ||
| 3687 | 3687 | ** than true arcs. Multiple reasons: (1) the legacy-PIC parameters |
| 3688 | 3688 | ** that control arcs are obscure and I could not figure out what they |
| 3689 | 3689 | ** mean based on available documentation. (2) Arcs are rarely used, |
| 3690 | 3690 | ** and so do not seem that important. |
| 3691 | 3691 | */ |
| 3692 | -static PPoint arcControlPoint(int cw, PPoint f, PPoint t, PNum rScale){ | |
| 3692 | +static PPoint arcControlPoint(int cw, PPoint f, PPoint t){ | |
| 3693 | 3693 | PPoint m; |
| 3694 | 3694 | PNum dx, dy; |
| 3695 | 3695 | m.x = 0.5*(f.x+t.x); |
| 3696 | 3696 | m.y = 0.5*(f.y+t.y); |
| 3697 | 3697 | dx = t.x - f.x; |
| 3698 | 3698 | dy = t.y - f.y; |
| 3699 | 3699 | if( cw ){ |
| 3700 | - m.x -= 0.5*rScale*dy; | |
| 3701 | - m.y += 0.5*rScale*dx; | |
| 3700 | + m.x -= 0.5*dy; | |
| 3701 | + m.y += 0.5*dx; | |
| 3702 | 3702 | }else{ |
| 3703 | - m.x += 0.5*rScale*dy; | |
| 3704 | - m.y -= 0.5*rScale*dx; | |
| 3703 | + m.x += 0.5*dy; | |
| 3704 | + m.y -= 0.5*dx; | |
| 3705 | 3705 | } |
| 3706 | 3706 | return m; |
| 3707 | 3707 | } |
| 3708 | 3708 | static void arcCheck(Pik *p, PObj *pObj){ |
| 3709 | - PPoint m; | |
| 3709 | + PPoint f, m, t; | |
| 3710 | + PNum sw; | |
| 3711 | + int i; | |
| 3710 | 3712 | if( p->nTPath>2 ){ |
| 3711 | 3713 | pik_error(p, &pObj->errTok, "arc geometry error"); |
| 3712 | 3714 | return; |
| 3713 | 3715 | } |
| 3714 | - m = arcControlPoint(pObj->cw, p->aTPath[0], p->aTPath[1], 0.5); | |
| 3715 | - pik_bbox_add_xy(&pObj->bbox, m.x, m.y); | |
| 3716 | + f = p->aTPath[0]; | |
| 3717 | + t = p->aTPath[1]; | |
| 3718 | + m = arcControlPoint(pObj->cw, f, t); | |
| 3719 | + sw = pObj->sw; | |
| 3720 | + for(i=1; i<16; i++){ | |
| 3721 | + PNum t1, t2, a, b, c, x, y; | |
| 3722 | + t1 = 0.0625*i; | |
| 3723 | + t2 = 1.0 - t1; | |
| 3724 | + a = t2*t2; | |
| 3725 | + b = 2*t1*t2; | |
| 3726 | + c = t1*t1; | |
| 3727 | + x = a*f.x + b*m.x + c*t.x; | |
| 3728 | + y = a*f.y + b*m.y + c*t.y; | |
| 3729 | + pik_bbox_addellipse(&pObj->bbox, x, y, sw, sw); | |
| 3730 | + } | |
| 3716 | 3731 | } |
| 3717 | 3732 | static void arcRender(Pik *p, PObj *pObj){ |
| 3718 | 3733 | PPoint f, m, t; |
| 3719 | 3734 | if( pObj->nPath<2 ) return; |
| 3720 | 3735 | if( pObj->sw<0.0 ) return; |
| 3721 | 3736 | f = pObj->aPath[0]; |
| 3722 | 3737 | t = pObj->aPath[1]; |
| 3723 | - m = arcControlPoint(pObj->cw,f,t,1.0); | |
| 3738 | + m = arcControlPoint(pObj->cw,f,t); | |
| 3724 | 3739 | if( pObj->larrow ){ |
| 3725 | 3740 | pik_draw_arrowhead(p,&m,&f,pObj); |
| 3726 | 3741 | } |
| 3727 | 3742 | if( pObj->rarrow ){ |
| 3728 | 3743 | pik_draw_arrowhead(p,&m,&t,pObj); |
| @@ -8315,6 +8330,6 @@ | ||
| 8315 | 8330 | |
| 8316 | 8331 | |
| 8317 | 8332 | #endif /* PIKCHR_TCL */ |
| 8318 | 8333 | |
| 8319 | 8334 | |
| 8320 | -#line 8320 "pikchr.c" | |
| 8335 | +#line 8335 "pikchr.c" | |
| 8321 | 8336 |
| --- extsrc/pikchr.c | |
| +++ extsrc/pikchr.c | |
| @@ -25,17 +25,17 @@ | |
| 25 | ** The following is the concatenation of all %include directives from the |
| 26 | ** input grammar file: |
| 27 | */ |
| 28 | /************ Begin %include sections from the grammar ************************/ |
| 29 | #line 1 "VERSION.h" |
| 30 | #define MANIFEST_UUID "052f07296e76ab2312caf2a4bf6237e574b3e533c7a36ee8f34db833baa3efb4" |
| 31 | #define MANIFEST_VERSION "[052f07296e]" |
| 32 | #define MANIFEST_DATE "2025-03-05 10:54:16" |
| 33 | #define MANIFEST_YEAR "2025" |
| 34 | #define MANIFEST_ISODATE "20250305105416" |
| 35 | #define MANIFEST_NUMERIC_DATE 20250305 |
| 36 | #define MANIFEST_NUMERIC_TIME 105416 |
| 37 | #define RELEASE_VERSION "1.0" |
| 38 | #define RELEASE_VERSION_NUMBER 10000 |
| 39 | #define RELEASE_RESOURCE_VERSION 1,0,0,0 |
| 40 | #define COMPILER "gcc-13.3.0" |
| 41 | #line 2 "pikchr.y" |
| @@ -3687,42 +3687,57 @@ | |
| 3687 | ** than true arcs. Multiple reasons: (1) the legacy-PIC parameters |
| 3688 | ** that control arcs are obscure and I could not figure out what they |
| 3689 | ** mean based on available documentation. (2) Arcs are rarely used, |
| 3690 | ** and so do not seem that important. |
| 3691 | */ |
| 3692 | static PPoint arcControlPoint(int cw, PPoint f, PPoint t, PNum rScale){ |
| 3693 | PPoint m; |
| 3694 | PNum dx, dy; |
| 3695 | m.x = 0.5*(f.x+t.x); |
| 3696 | m.y = 0.5*(f.y+t.y); |
| 3697 | dx = t.x - f.x; |
| 3698 | dy = t.y - f.y; |
| 3699 | if( cw ){ |
| 3700 | m.x -= 0.5*rScale*dy; |
| 3701 | m.y += 0.5*rScale*dx; |
| 3702 | }else{ |
| 3703 | m.x += 0.5*rScale*dy; |
| 3704 | m.y -= 0.5*rScale*dx; |
| 3705 | } |
| 3706 | return m; |
| 3707 | } |
| 3708 | static void arcCheck(Pik *p, PObj *pObj){ |
| 3709 | PPoint m; |
| 3710 | if( p->nTPath>2 ){ |
| 3711 | pik_error(p, &pObj->errTok, "arc geometry error"); |
| 3712 | return; |
| 3713 | } |
| 3714 | m = arcControlPoint(pObj->cw, p->aTPath[0], p->aTPath[1], 0.5); |
| 3715 | pik_bbox_add_xy(&pObj->bbox, m.x, m.y); |
| 3716 | } |
| 3717 | static void arcRender(Pik *p, PObj *pObj){ |
| 3718 | PPoint f, m, t; |
| 3719 | if( pObj->nPath<2 ) return; |
| 3720 | if( pObj->sw<0.0 ) return; |
| 3721 | f = pObj->aPath[0]; |
| 3722 | t = pObj->aPath[1]; |
| 3723 | m = arcControlPoint(pObj->cw,f,t,1.0); |
| 3724 | if( pObj->larrow ){ |
| 3725 | pik_draw_arrowhead(p,&m,&f,pObj); |
| 3726 | } |
| 3727 | if( pObj->rarrow ){ |
| 3728 | pik_draw_arrowhead(p,&m,&t,pObj); |
| @@ -8315,6 +8330,6 @@ | |
| 8315 | |
| 8316 | |
| 8317 | #endif /* PIKCHR_TCL */ |
| 8318 | |
| 8319 | |
| 8320 | #line 8320 "pikchr.c" |
| 8321 |
| --- extsrc/pikchr.c | |
| +++ extsrc/pikchr.c | |
| @@ -25,17 +25,17 @@ | |
| 25 | ** The following is the concatenation of all %include directives from the |
| 26 | ** input grammar file: |
| 27 | */ |
| 28 | /************ Begin %include sections from the grammar ************************/ |
| 29 | #line 1 "VERSION.h" |
| 30 | #define MANIFEST_UUID "8a43b020141f772a0ac45291a7fd73041d2efba5e3665c6bd2f334ad9b2e9845" |
| 31 | #define MANIFEST_VERSION "[8a43b02014]" |
| 32 | #define MANIFEST_DATE "2025-03-19 16:19:43" |
| 33 | #define MANIFEST_YEAR "2025" |
| 34 | #define MANIFEST_ISODATE "20250319161943" |
| 35 | #define MANIFEST_NUMERIC_DATE 20250319 |
| 36 | #define MANIFEST_NUMERIC_TIME 161943 |
| 37 | #define RELEASE_VERSION "1.0" |
| 38 | #define RELEASE_VERSION_NUMBER 10000 |
| 39 | #define RELEASE_RESOURCE_VERSION 1,0,0,0 |
| 40 | #define COMPILER "gcc-13.3.0" |
| 41 | #line 2 "pikchr.y" |
| @@ -3687,42 +3687,57 @@ | |
| 3687 | ** than true arcs. Multiple reasons: (1) the legacy-PIC parameters |
| 3688 | ** that control arcs are obscure and I could not figure out what they |
| 3689 | ** mean based on available documentation. (2) Arcs are rarely used, |
| 3690 | ** and so do not seem that important. |
| 3691 | */ |
| 3692 | static PPoint arcControlPoint(int cw, PPoint f, PPoint t){ |
| 3693 | PPoint m; |
| 3694 | PNum dx, dy; |
| 3695 | m.x = 0.5*(f.x+t.x); |
| 3696 | m.y = 0.5*(f.y+t.y); |
| 3697 | dx = t.x - f.x; |
| 3698 | dy = t.y - f.y; |
| 3699 | if( cw ){ |
| 3700 | m.x -= 0.5*dy; |
| 3701 | m.y += 0.5*dx; |
| 3702 | }else{ |
| 3703 | m.x += 0.5*dy; |
| 3704 | m.y -= 0.5*dx; |
| 3705 | } |
| 3706 | return m; |
| 3707 | } |
| 3708 | static void arcCheck(Pik *p, PObj *pObj){ |
| 3709 | PPoint f, m, t; |
| 3710 | PNum sw; |
| 3711 | int i; |
| 3712 | if( p->nTPath>2 ){ |
| 3713 | pik_error(p, &pObj->errTok, "arc geometry error"); |
| 3714 | return; |
| 3715 | } |
| 3716 | f = p->aTPath[0]; |
| 3717 | t = p->aTPath[1]; |
| 3718 | m = arcControlPoint(pObj->cw, f, t); |
| 3719 | sw = pObj->sw; |
| 3720 | for(i=1; i<16; i++){ |
| 3721 | PNum t1, t2, a, b, c, x, y; |
| 3722 | t1 = 0.0625*i; |
| 3723 | t2 = 1.0 - t1; |
| 3724 | a = t2*t2; |
| 3725 | b = 2*t1*t2; |
| 3726 | c = t1*t1; |
| 3727 | x = a*f.x + b*m.x + c*t.x; |
| 3728 | y = a*f.y + b*m.y + c*t.y; |
| 3729 | pik_bbox_addellipse(&pObj->bbox, x, y, sw, sw); |
| 3730 | } |
| 3731 | } |
| 3732 | static void arcRender(Pik *p, PObj *pObj){ |
| 3733 | PPoint f, m, t; |
| 3734 | if( pObj->nPath<2 ) return; |
| 3735 | if( pObj->sw<0.0 ) return; |
| 3736 | f = pObj->aPath[0]; |
| 3737 | t = pObj->aPath[1]; |
| 3738 | m = arcControlPoint(pObj->cw,f,t); |
| 3739 | if( pObj->larrow ){ |
| 3740 | pik_draw_arrowhead(p,&m,&f,pObj); |
| 3741 | } |
| 3742 | if( pObj->rarrow ){ |
| 3743 | pik_draw_arrowhead(p,&m,&t,pObj); |
| @@ -8315,6 +8330,6 @@ | |
| 8330 | |
| 8331 | |
| 8332 | #endif /* PIKCHR_TCL */ |
| 8333 | |
| 8334 | |
| 8335 | #line 8335 "pikchr.c" |
| 8336 |
+1
-1
| --- extsrc/pikchr.js | ||
| +++ extsrc/pikchr.js | ||
| @@ -300,11 +300,11 @@ | ||
| 300 | 300 | |
| 301 | 301 | // end include: URIUtils.js |
| 302 | 302 | // include: runtime_exceptions.js |
| 303 | 303 | // end include: runtime_exceptions.js |
| 304 | 304 | function findWasmBinary() { |
| 305 | - var f = "pikchr-v7583078860.wasm"; | |
| 305 | + var f = "pikchr-v2813665466.wasm"; | |
| 306 | 306 | if (!isDataURI(f)) { |
| 307 | 307 | return locateFile(f); |
| 308 | 308 | } |
| 309 | 309 | return f; |
| 310 | 310 | } |
| 311 | 311 |
| --- extsrc/pikchr.js | |
| +++ extsrc/pikchr.js | |
| @@ -300,11 +300,11 @@ | |
| 300 | |
| 301 | // end include: URIUtils.js |
| 302 | // include: runtime_exceptions.js |
| 303 | // end include: runtime_exceptions.js |
| 304 | function findWasmBinary() { |
| 305 | var f = "pikchr-v7583078860.wasm"; |
| 306 | if (!isDataURI(f)) { |
| 307 | return locateFile(f); |
| 308 | } |
| 309 | return f; |
| 310 | } |
| 311 |
| --- extsrc/pikchr.js | |
| +++ extsrc/pikchr.js | |
| @@ -300,11 +300,11 @@ | |
| 300 | |
| 301 | // end include: URIUtils.js |
| 302 | // include: runtime_exceptions.js |
| 303 | // end include: runtime_exceptions.js |
| 304 | function findWasmBinary() { |
| 305 | var f = "pikchr-v2813665466.wasm"; |
| 306 | if (!isDataURI(f)) { |
| 307 | return locateFile(f); |
| 308 | } |
| 309 | return f; |
| 310 | } |
| 311 |
| --- extsrc/pikchr.wasm | ||
| +++ extsrc/pikchr.wasm | ||
| cannot compute difference between binary files | ||
| 1 | 1 |
| --- extsrc/pikchr.wasm | |
| +++ extsrc/pikchr.wasm | |
| 0 | annot compute difference between binary files |
| 1 |
| --- extsrc/pikchr.wasm | |
| +++ extsrc/pikchr.wasm | |
| 0 | annot compute difference between binary files |
| 1 |
+19
-8
| --- extsrc/shell.c | ||
| +++ extsrc/shell.c | ||
| @@ -6729,23 +6729,21 @@ | ||
| 6729 | 6729 | if( pCur->ss.iBase<iMin ){ |
| 6730 | 6730 | sqlite3_uint64 d = iMin - pCur->ss.iBase; |
| 6731 | 6731 | pCur->ss.iBase += ((d+szStep-1)/szStep)*szStep; |
| 6732 | 6732 | } |
| 6733 | 6733 | if( pCur->ss.iTerm>iMax ){ |
| 6734 | - sqlite3_uint64 d = pCur->ss.iTerm - iMax; | |
| 6735 | - pCur->ss.iTerm -= ((d+szStep-1)/szStep)*szStep; | |
| 6734 | + pCur->ss.iTerm = iMax; | |
| 6736 | 6735 | } |
| 6737 | 6736 | }else{ |
| 6738 | 6737 | sqlite3_int64 szStep = -pCur->ss.iStep; |
| 6739 | 6738 | assert( szStep>0 ); |
| 6740 | 6739 | if( pCur->ss.iBase>iMax ){ |
| 6741 | 6740 | sqlite3_uint64 d = pCur->ss.iBase - iMax; |
| 6742 | 6741 | pCur->ss.iBase -= ((d+szStep-1)/szStep)*szStep; |
| 6743 | 6742 | } |
| 6744 | 6743 | if( pCur->ss.iTerm<iMin ){ |
| 6745 | - sqlite3_uint64 d = iMin - pCur->ss.iTerm; | |
| 6746 | - pCur->ss.iTerm += ((d+szStep-1)/szStep)*szStep; | |
| 6744 | + pCur->ss.iTerm = iMin; | |
| 6747 | 6745 | } |
| 6748 | 6746 | } |
| 6749 | 6747 | } |
| 6750 | 6748 | |
| 6751 | 6749 | /* Apply LIMIT and OFFSET constraints, if any */ |
| @@ -18710,10 +18708,20 @@ | ||
| 18710 | 18708 | |
| 18711 | 18709 | /* typedef unsigned int u32; */ |
| 18712 | 18710 | /* typedef unsigned char u8; */ |
| 18713 | 18711 | /* typedef sqlite3_int64 i64; */ |
| 18714 | 18712 | |
| 18713 | +/* | |
| 18714 | +** Work around C99 "flex-array" syntax for pre-C99 compilers, so as | |
| 18715 | +** to avoid complaints from -fsanitize=strict-bounds. | |
| 18716 | +*/ | |
| 18717 | +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) | |
| 18718 | +# define FLEXARRAY | |
| 18719 | +#else | |
| 18720 | +# define FLEXARRAY 1 | |
| 18721 | +#endif | |
| 18722 | + | |
| 18715 | 18723 | typedef struct RecoverTable RecoverTable; |
| 18716 | 18724 | typedef struct RecoverColumn RecoverColumn; |
| 18717 | 18725 | |
| 18718 | 18726 | /* |
| 18719 | 18727 | ** When recovering rows of data that can be associated with table |
| @@ -18817,12 +18825,15 @@ | ||
| 18817 | 18825 | ** (aElem[iKey/32] & (1 << (iKey%32))) ? 1 : 0 |
| 18818 | 18826 | */ |
| 18819 | 18827 | typedef struct RecoverBitmap RecoverBitmap; |
| 18820 | 18828 | struct RecoverBitmap { |
| 18821 | 18829 | i64 nPg; /* Size of bitmap */ |
| 18822 | - u32 aElem[1]; /* Array of 32-bit bitmasks */ | |
| 18830 | + u32 aElem[FLEXARRAY]; /* Array of 32-bit bitmasks */ | |
| 18823 | 18831 | }; |
| 18832 | + | |
| 18833 | +/* Size in bytes of a RecoverBitmap object sufficient to cover 32 pages */ | |
| 18834 | +#define SZ_RECOVERBITMAP_32 (16) | |
| 18824 | 18835 | |
| 18825 | 18836 | /* |
| 18826 | 18837 | ** State variables (part of the sqlite3_recover structure) used while |
| 18827 | 18838 | ** recovering data for tables identified in the recovered schema (state |
| 18828 | 18839 | ** RECOVER_STATE_WRITING). |
| @@ -19059,11 +19070,11 @@ | ||
| 19059 | 19070 | ** large enough to store a bit for all page numbers between 1 and nPg, |
| 19060 | 19071 | ** inclusive. The bitmap is initially zeroed. |
| 19061 | 19072 | */ |
| 19062 | 19073 | static RecoverBitmap *recoverBitmapAlloc(sqlite3_recover *p, i64 nPg){ |
| 19063 | 19074 | int nElem = (nPg+1+31) / 32; |
| 19064 | - int nByte = sizeof(RecoverBitmap) + nElem*sizeof(u32); | |
| 19075 | + int nByte = SZ_RECOVERBITMAP_32 + nElem*sizeof(u32); | |
| 19065 | 19076 | RecoverBitmap *pRet = (RecoverBitmap*)recoverMalloc(p, nByte); |
| 19066 | 19077 | |
| 19067 | 19078 | if( pRet ){ |
| 19068 | 19079 | pRet->nPg = nPg; |
| 19069 | 19080 | } |
| @@ -32463,11 +32474,11 @@ | ||
| 32463 | 32474 | /* |
| 32464 | 32475 | ** The CLI needs a working sqlite3_complete() to work properly. So error |
| 32465 | 32476 | ** out of the build if compiling with SQLITE_OMIT_COMPLETE. |
| 32466 | 32477 | */ |
| 32467 | 32478 | #ifdef SQLITE_OMIT_COMPLETE |
| 32468 | -# error the CLI application is incompatable with SQLITE_OMIT_COMPLETE. | |
| 32479 | +# error the CLI application is incompatible with SQLITE_OMIT_COMPLETE. | |
| 32469 | 32480 | #endif |
| 32470 | 32481 | |
| 32471 | 32482 | /* |
| 32472 | 32483 | ** Return true if zSql is a complete SQL statement. Return false if it |
| 32473 | 32484 | ** ends in the middle of a string literal or C-style comment. |
| @@ -33663,19 +33674,19 @@ | ||
| 33663 | 33674 | /* Run all arguments that do not begin with '-' as if they were separate |
| 33664 | 33675 | ** command-line inputs, except for the argToSkip argument which contains |
| 33665 | 33676 | ** the database filename. |
| 33666 | 33677 | */ |
| 33667 | 33678 | for(i=0; i<nCmd; i++){ |
| 33679 | + echo_group_input(&data, azCmd[i]); | |
| 33668 | 33680 | if( azCmd[i][0]=='.' ){ |
| 33669 | 33681 | rc = do_meta_command(azCmd[i], &data); |
| 33670 | 33682 | if( rc ){ |
| 33671 | 33683 | if( rc==2 ) rc = 0; |
| 33672 | 33684 | goto shell_main_exit; |
| 33673 | 33685 | } |
| 33674 | 33686 | }else{ |
| 33675 | 33687 | open_db(&data, 0); |
| 33676 | - echo_group_input(&data, azCmd[i]); | |
| 33677 | 33688 | rc = shell_exec(&data, azCmd[i], &zErrMsg); |
| 33678 | 33689 | if( zErrMsg || rc ){ |
| 33679 | 33690 | if( zErrMsg!=0 ){ |
| 33680 | 33691 | shellEmitError(zErrMsg); |
| 33681 | 33692 | }else{ |
| 33682 | 33693 |
| --- extsrc/shell.c | |
| +++ extsrc/shell.c | |
| @@ -6729,23 +6729,21 @@ | |
| 6729 | if( pCur->ss.iBase<iMin ){ |
| 6730 | sqlite3_uint64 d = iMin - pCur->ss.iBase; |
| 6731 | pCur->ss.iBase += ((d+szStep-1)/szStep)*szStep; |
| 6732 | } |
| 6733 | if( pCur->ss.iTerm>iMax ){ |
| 6734 | sqlite3_uint64 d = pCur->ss.iTerm - iMax; |
| 6735 | pCur->ss.iTerm -= ((d+szStep-1)/szStep)*szStep; |
| 6736 | } |
| 6737 | }else{ |
| 6738 | sqlite3_int64 szStep = -pCur->ss.iStep; |
| 6739 | assert( szStep>0 ); |
| 6740 | if( pCur->ss.iBase>iMax ){ |
| 6741 | sqlite3_uint64 d = pCur->ss.iBase - iMax; |
| 6742 | pCur->ss.iBase -= ((d+szStep-1)/szStep)*szStep; |
| 6743 | } |
| 6744 | if( pCur->ss.iTerm<iMin ){ |
| 6745 | sqlite3_uint64 d = iMin - pCur->ss.iTerm; |
| 6746 | pCur->ss.iTerm += ((d+szStep-1)/szStep)*szStep; |
| 6747 | } |
| 6748 | } |
| 6749 | } |
| 6750 | |
| 6751 | /* Apply LIMIT and OFFSET constraints, if any */ |
| @@ -18710,10 +18708,20 @@ | |
| 18710 | |
| 18711 | /* typedef unsigned int u32; */ |
| 18712 | /* typedef unsigned char u8; */ |
| 18713 | /* typedef sqlite3_int64 i64; */ |
| 18714 | |
| 18715 | typedef struct RecoverTable RecoverTable; |
| 18716 | typedef struct RecoverColumn RecoverColumn; |
| 18717 | |
| 18718 | /* |
| 18719 | ** When recovering rows of data that can be associated with table |
| @@ -18817,12 +18825,15 @@ | |
| 18817 | ** (aElem[iKey/32] & (1 << (iKey%32))) ? 1 : 0 |
| 18818 | */ |
| 18819 | typedef struct RecoverBitmap RecoverBitmap; |
| 18820 | struct RecoverBitmap { |
| 18821 | i64 nPg; /* Size of bitmap */ |
| 18822 | u32 aElem[1]; /* Array of 32-bit bitmasks */ |
| 18823 | }; |
| 18824 | |
| 18825 | /* |
| 18826 | ** State variables (part of the sqlite3_recover structure) used while |
| 18827 | ** recovering data for tables identified in the recovered schema (state |
| 18828 | ** RECOVER_STATE_WRITING). |
| @@ -19059,11 +19070,11 @@ | |
| 19059 | ** large enough to store a bit for all page numbers between 1 and nPg, |
| 19060 | ** inclusive. The bitmap is initially zeroed. |
| 19061 | */ |
| 19062 | static RecoverBitmap *recoverBitmapAlloc(sqlite3_recover *p, i64 nPg){ |
| 19063 | int nElem = (nPg+1+31) / 32; |
| 19064 | int nByte = sizeof(RecoverBitmap) + nElem*sizeof(u32); |
| 19065 | RecoverBitmap *pRet = (RecoverBitmap*)recoverMalloc(p, nByte); |
| 19066 | |
| 19067 | if( pRet ){ |
| 19068 | pRet->nPg = nPg; |
| 19069 | } |
| @@ -32463,11 +32474,11 @@ | |
| 32463 | /* |
| 32464 | ** The CLI needs a working sqlite3_complete() to work properly. So error |
| 32465 | ** out of the build if compiling with SQLITE_OMIT_COMPLETE. |
| 32466 | */ |
| 32467 | #ifdef SQLITE_OMIT_COMPLETE |
| 32468 | # error the CLI application is incompatable with SQLITE_OMIT_COMPLETE. |
| 32469 | #endif |
| 32470 | |
| 32471 | /* |
| 32472 | ** Return true if zSql is a complete SQL statement. Return false if it |
| 32473 | ** ends in the middle of a string literal or C-style comment. |
| @@ -33663,19 +33674,19 @@ | |
| 33663 | /* Run all arguments that do not begin with '-' as if they were separate |
| 33664 | ** command-line inputs, except for the argToSkip argument which contains |
| 33665 | ** the database filename. |
| 33666 | */ |
| 33667 | for(i=0; i<nCmd; i++){ |
| 33668 | if( azCmd[i][0]=='.' ){ |
| 33669 | rc = do_meta_command(azCmd[i], &data); |
| 33670 | if( rc ){ |
| 33671 | if( rc==2 ) rc = 0; |
| 33672 | goto shell_main_exit; |
| 33673 | } |
| 33674 | }else{ |
| 33675 | open_db(&data, 0); |
| 33676 | echo_group_input(&data, azCmd[i]); |
| 33677 | rc = shell_exec(&data, azCmd[i], &zErrMsg); |
| 33678 | if( zErrMsg || rc ){ |
| 33679 | if( zErrMsg!=0 ){ |
| 33680 | shellEmitError(zErrMsg); |
| 33681 | }else{ |
| 33682 |
| --- extsrc/shell.c | |
| +++ extsrc/shell.c | |
| @@ -6729,23 +6729,21 @@ | |
| 6729 | if( pCur->ss.iBase<iMin ){ |
| 6730 | sqlite3_uint64 d = iMin - pCur->ss.iBase; |
| 6731 | pCur->ss.iBase += ((d+szStep-1)/szStep)*szStep; |
| 6732 | } |
| 6733 | if( pCur->ss.iTerm>iMax ){ |
| 6734 | pCur->ss.iTerm = iMax; |
| 6735 | } |
| 6736 | }else{ |
| 6737 | sqlite3_int64 szStep = -pCur->ss.iStep; |
| 6738 | assert( szStep>0 ); |
| 6739 | if( pCur->ss.iBase>iMax ){ |
| 6740 | sqlite3_uint64 d = pCur->ss.iBase - iMax; |
| 6741 | pCur->ss.iBase -= ((d+szStep-1)/szStep)*szStep; |
| 6742 | } |
| 6743 | if( pCur->ss.iTerm<iMin ){ |
| 6744 | pCur->ss.iTerm = iMin; |
| 6745 | } |
| 6746 | } |
| 6747 | } |
| 6748 | |
| 6749 | /* Apply LIMIT and OFFSET constraints, if any */ |
| @@ -18710,10 +18708,20 @@ | |
| 18708 | |
| 18709 | /* typedef unsigned int u32; */ |
| 18710 | /* typedef unsigned char u8; */ |
| 18711 | /* typedef sqlite3_int64 i64; */ |
| 18712 | |
| 18713 | /* |
| 18714 | ** Work around C99 "flex-array" syntax for pre-C99 compilers, so as |
| 18715 | ** to avoid complaints from -fsanitize=strict-bounds. |
| 18716 | */ |
| 18717 | #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) |
| 18718 | # define FLEXARRAY |
| 18719 | #else |
| 18720 | # define FLEXARRAY 1 |
| 18721 | #endif |
| 18722 | |
| 18723 | typedef struct RecoverTable RecoverTable; |
| 18724 | typedef struct RecoverColumn RecoverColumn; |
| 18725 | |
| 18726 | /* |
| 18727 | ** When recovering rows of data that can be associated with table |
| @@ -18817,12 +18825,15 @@ | |
| 18825 | ** (aElem[iKey/32] & (1 << (iKey%32))) ? 1 : 0 |
| 18826 | */ |
| 18827 | typedef struct RecoverBitmap RecoverBitmap; |
| 18828 | struct RecoverBitmap { |
| 18829 | i64 nPg; /* Size of bitmap */ |
| 18830 | u32 aElem[FLEXARRAY]; /* Array of 32-bit bitmasks */ |
| 18831 | }; |
| 18832 | |
| 18833 | /* Size in bytes of a RecoverBitmap object sufficient to cover 32 pages */ |
| 18834 | #define SZ_RECOVERBITMAP_32 (16) |
| 18835 | |
| 18836 | /* |
| 18837 | ** State variables (part of the sqlite3_recover structure) used while |
| 18838 | ** recovering data for tables identified in the recovered schema (state |
| 18839 | ** RECOVER_STATE_WRITING). |
| @@ -19059,11 +19070,11 @@ | |
| 19070 | ** large enough to store a bit for all page numbers between 1 and nPg, |
| 19071 | ** inclusive. The bitmap is initially zeroed. |
| 19072 | */ |
| 19073 | static RecoverBitmap *recoverBitmapAlloc(sqlite3_recover *p, i64 nPg){ |
| 19074 | int nElem = (nPg+1+31) / 32; |
| 19075 | int nByte = SZ_RECOVERBITMAP_32 + nElem*sizeof(u32); |
| 19076 | RecoverBitmap *pRet = (RecoverBitmap*)recoverMalloc(p, nByte); |
| 19077 | |
| 19078 | if( pRet ){ |
| 19079 | pRet->nPg = nPg; |
| 19080 | } |
| @@ -32463,11 +32474,11 @@ | |
| 32474 | /* |
| 32475 | ** The CLI needs a working sqlite3_complete() to work properly. So error |
| 32476 | ** out of the build if compiling with SQLITE_OMIT_COMPLETE. |
| 32477 | */ |
| 32478 | #ifdef SQLITE_OMIT_COMPLETE |
| 32479 | # error the CLI application is incompatible with SQLITE_OMIT_COMPLETE. |
| 32480 | #endif |
| 32481 | |
| 32482 | /* |
| 32483 | ** Return true if zSql is a complete SQL statement. Return false if it |
| 32484 | ** ends in the middle of a string literal or C-style comment. |
| @@ -33663,19 +33674,19 @@ | |
| 33674 | /* Run all arguments that do not begin with '-' as if they were separate |
| 33675 | ** command-line inputs, except for the argToSkip argument which contains |
| 33676 | ** the database filename. |
| 33677 | */ |
| 33678 | for(i=0; i<nCmd; i++){ |
| 33679 | echo_group_input(&data, azCmd[i]); |
| 33680 | if( azCmd[i][0]=='.' ){ |
| 33681 | rc = do_meta_command(azCmd[i], &data); |
| 33682 | if( rc ){ |
| 33683 | if( rc==2 ) rc = 0; |
| 33684 | goto shell_main_exit; |
| 33685 | } |
| 33686 | }else{ |
| 33687 | open_db(&data, 0); |
| 33688 | rc = shell_exec(&data, azCmd[i], &zErrMsg); |
| 33689 | if( zErrMsg || rc ){ |
| 33690 | if( zErrMsg!=0 ){ |
| 33691 | shellEmitError(zErrMsg); |
| 33692 | }else{ |
| 33693 |
+486
-287
| --- extsrc/sqlite3.c | ||
| +++ extsrc/sqlite3.c | ||
| @@ -16,11 +16,11 @@ | ||
| 16 | 16 | ** if you want a wrapper to interface SQLite with your choice of programming |
| 17 | 17 | ** language. The code for the "sqlite3" command-line shell is also in a |
| 18 | 18 | ** separate file. This file contains only code for the core SQLite library. |
| 19 | 19 | ** |
| 20 | 20 | ** The content in this amalgamation comes from Fossil check-in |
| 21 | -** e6784af6d50f715338ae3218fc8ba1b89488 with changes in files: | |
| 21 | +** 18bda13e197e4b4ec7464b3e70012f71edc0 with changes in files: | |
| 22 | 22 | ** |
| 23 | 23 | ** |
| 24 | 24 | */ |
| 25 | 25 | #ifndef SQLITE_AMALGAMATION |
| 26 | 26 | #define SQLITE_CORE 1 |
| @@ -465,11 +465,11 @@ | ||
| 465 | 465 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 466 | 466 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 467 | 467 | */ |
| 468 | 468 | #define SQLITE_VERSION "3.50.0" |
| 469 | 469 | #define SQLITE_VERSION_NUMBER 3050000 |
| 470 | -#define SQLITE_SOURCE_ID "2025-02-25 18:10:47 e6784af6d50f715338ae3218fc8ba1b894883c27d797f0b7fd2625cac17d9cd7" | |
| 470 | +#define SQLITE_SOURCE_ID "2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185" | |
| 471 | 471 | |
| 472 | 472 | /* |
| 473 | 473 | ** CAPI3REF: Run-Time Library Version Numbers |
| 474 | 474 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 475 | 475 | ** |
| @@ -5492,11 +5492,11 @@ | ||
| 5492 | 5492 | ** For all versions of SQLite up to and including 3.6.23.1, a call to |
| 5493 | 5493 | ** [sqlite3_reset()] was required after sqlite3_step() returned anything |
| 5494 | 5494 | ** other than [SQLITE_ROW] before any subsequent invocation of |
| 5495 | 5495 | ** sqlite3_step(). Failure to reset the prepared statement using |
| 5496 | 5496 | ** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from |
| 5497 | -** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1], | |
| 5497 | +** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]), | |
| 5498 | 5498 | ** sqlite3_step() began |
| 5499 | 5499 | ** calling [sqlite3_reset()] automatically in this circumstance rather |
| 5500 | 5500 | ** than returning [SQLITE_MISUSE]. This is not considered a compatibility |
| 5501 | 5501 | ** break because any application that ever receives an SQLITE_MISUSE error |
| 5502 | 5502 | ** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option |
| @@ -7388,10 +7388,12 @@ | ||
| 7388 | 7388 | ** ^Any callback set by a previous call to this function |
| 7389 | 7389 | ** for the same database connection is overridden. |
| 7390 | 7390 | ** |
| 7391 | 7391 | ** ^The second argument is a pointer to the function to invoke when a |
| 7392 | 7392 | ** row is updated, inserted or deleted in a rowid table. |
| 7393 | +** ^The update hook is disabled by invoking sqlite3_update_hook() | |
| 7394 | +** with a NULL pointer as the second parameter. | |
| 7393 | 7395 | ** ^The first argument to the callback is a copy of the third argument |
| 7394 | 7396 | ** to sqlite3_update_hook(). |
| 7395 | 7397 | ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], |
| 7396 | 7398 | ** or [SQLITE_UPDATE], depending on the operation that caused the callback |
| 7397 | 7399 | ** to be invoked. |
| @@ -15170,11 +15172,21 @@ | ||
| 15170 | 15172 | /* |
| 15171 | 15173 | ** GCC does not define the offsetof() macro so we'll have to do it |
| 15172 | 15174 | ** ourselves. |
| 15173 | 15175 | */ |
| 15174 | 15176 | #ifndef offsetof |
| 15175 | -#define offsetof(STRUCTURE,FIELD) ((int)((char*)&((STRUCTURE*)0)->FIELD)) | |
| 15177 | +#define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) | |
| 15178 | +#endif | |
| 15179 | + | |
| 15180 | +/* | |
| 15181 | +** Work around C99 "flex-array" syntax for pre-C99 compilers, so as | |
| 15182 | +** to avoid complaints from -fsanitize=strict-bounds. | |
| 15183 | +*/ | |
| 15184 | +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) | |
| 15185 | +# define FLEXARRAY | |
| 15186 | +#else | |
| 15187 | +# define FLEXARRAY 1 | |
| 15176 | 15188 | #endif |
| 15177 | 15189 | |
| 15178 | 15190 | /* |
| 15179 | 15191 | ** Macros to compute minimum and maximum of two numbers. |
| 15180 | 15192 | */ |
| @@ -17405,12 +17417,12 @@ | ||
| 17405 | 17417 | SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*); |
| 17406 | 17418 | #ifdef SQLITE_ENABLE_BYTECODE_VTAB |
| 17407 | 17419 | SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*); |
| 17408 | 17420 | #endif |
| 17409 | 17421 | |
| 17410 | -/* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on | |
| 17411 | -** each VDBE opcode. | |
| 17422 | +/* Use SQLITE_ENABLE_EXPLAIN_COMMENTS to enable generation of extra | |
| 17423 | +** comments on each VDBE opcode. | |
| 17412 | 17424 | ** |
| 17413 | 17425 | ** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op |
| 17414 | 17426 | ** comments in VDBE programs that show key decision points in the code |
| 17415 | 17427 | ** generator. |
| 17416 | 17428 | */ |
| @@ -18945,12 +18957,16 @@ | ||
| 18945 | 18957 | u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */ |
| 18946 | 18958 | Trigger *apTrigger[2];/* Triggers for aAction[] actions */ |
| 18947 | 18959 | struct sColMap { /* Mapping of columns in pFrom to columns in zTo */ |
| 18948 | 18960 | int iFrom; /* Index of column in pFrom */ |
| 18949 | 18961 | char *zCol; /* Name of column in zTo. If NULL use PRIMARY KEY */ |
| 18950 | - } aCol[1]; /* One entry for each of nCol columns */ | |
| 18962 | + } aCol[FLEXARRAY]; /* One entry for each of nCol columns */ | |
| 18951 | 18963 | }; |
| 18964 | + | |
| 18965 | +/* The size (in bytes) of an FKey object holding N columns. The answer | |
| 18966 | +** does NOT include space to hold the zTo name. */ | |
| 18967 | +#define SZ_FKEY(N) (offsetof(FKey,aCol)+(N)*sizeof(struct sColMap)) | |
| 18952 | 18968 | |
| 18953 | 18969 | /* |
| 18954 | 18970 | ** SQLite supports many different ways to resolve a constraint |
| 18955 | 18971 | ** error. ROLLBACK processing means that a constraint violation |
| 18956 | 18972 | ** causes the operation in process to fail and for the current transaction |
| @@ -19009,13 +19025,16 @@ | ||
| 19009 | 19025 | u8 enc; /* Text encoding - one of the SQLITE_UTF* values */ |
| 19010 | 19026 | u16 nKeyField; /* Number of key columns in the index */ |
| 19011 | 19027 | u16 nAllField; /* Total columns, including key plus others */ |
| 19012 | 19028 | sqlite3 *db; /* The database connection */ |
| 19013 | 19029 | u8 *aSortFlags; /* Sort order for each column. */ |
| 19014 | - CollSeq *aColl[1]; /* Collating sequence for each term of the key */ | |
| 19030 | + CollSeq *aColl[FLEXARRAY]; /* Collating sequence for each term of the key */ | |
| 19015 | 19031 | }; |
| 19016 | 19032 | |
| 19033 | +/* The size (in bytes) of a KeyInfo object with up to N fields */ | |
| 19034 | +#define SZ_KEYINFO(N) (offsetof(KeyInfo,aColl) + (N)*sizeof(CollSeq*)) | |
| 19035 | + | |
| 19017 | 19036 | /* |
| 19018 | 19037 | ** Allowed bit values for entries in the KeyInfo.aSortFlags[] array. |
| 19019 | 19038 | */ |
| 19020 | 19039 | #define KEYINFO_ORDER_DESC 0x01 /* DESC sort order */ |
| 19021 | 19040 | #define KEYINFO_ORDER_BIGNULL 0x02 /* NULL is larger than any other value */ |
| @@ -19584,12 +19603,17 @@ | ||
| 19584 | 19603 | u16 iAlias; /* Index into Parse.aAlias[] for zName */ |
| 19585 | 19604 | } x; |
| 19586 | 19605 | int iConstExprReg; /* Register in which Expr value is cached. Used only |
| 19587 | 19606 | ** by Parse.pConstExpr */ |
| 19588 | 19607 | } u; |
| 19589 | - } a[1]; /* One slot for each expression in the list */ | |
| 19608 | + } a[FLEXARRAY]; /* One slot for each expression in the list */ | |
| 19590 | 19609 | }; |
| 19610 | + | |
| 19611 | +/* The size (in bytes) of an ExprList object that is big enough to hold | |
| 19612 | +** as many as N expressions. */ | |
| 19613 | +#define SZ_EXPRLIST(N) \ | |
| 19614 | + (offsetof(ExprList,a) + (N)*sizeof(struct ExprList_item)) | |
| 19591 | 19615 | |
| 19592 | 19616 | /* |
| 19593 | 19617 | ** Allowed values for Expr.a.eEName |
| 19594 | 19618 | */ |
| 19595 | 19619 | #define ENAME_NAME 0 /* The AS clause of a result set */ |
| @@ -19614,13 +19638,16 @@ | ||
| 19614 | 19638 | */ |
| 19615 | 19639 | struct IdList { |
| 19616 | 19640 | int nId; /* Number of identifiers on the list */ |
| 19617 | 19641 | struct IdList_item { |
| 19618 | 19642 | char *zName; /* Name of the identifier */ |
| 19619 | - } a[1]; | |
| 19643 | + } a[FLEXARRAY]; | |
| 19620 | 19644 | }; |
| 19621 | 19645 | |
| 19646 | +/* The size (in bytes) of an IdList object that can hold up to N IDs. */ | |
| 19647 | +#define SZ_IDLIST(N) (offsetof(IdList,a)+(N)*sizeof(struct IdList_item)) | |
| 19648 | + | |
| 19622 | 19649 | /* |
| 19623 | 19650 | ** Allowed values for IdList.eType, which determines which value of the a.u4 |
| 19624 | 19651 | ** is valid. |
| 19625 | 19652 | */ |
| 19626 | 19653 | #define EU4_NONE 0 /* Does not use IdList.a.u4 */ |
| @@ -19736,14 +19763,22 @@ | ||
| 19736 | 19763 | ** is used to hold the FROM clause of a SELECT statement. SrcList also |
| 19737 | 19764 | ** represents the target tables for DELETE, INSERT, and UPDATE statements. |
| 19738 | 19765 | ** |
| 19739 | 19766 | */ |
| 19740 | 19767 | struct SrcList { |
| 19741 | - int nSrc; /* Number of tables or subqueries in the FROM clause */ | |
| 19742 | - u32 nAlloc; /* Number of entries allocated in a[] below */ | |
| 19743 | - SrcItem a[1]; /* One entry for each identifier on the list */ | |
| 19768 | + int nSrc; /* Number of tables or subqueries in the FROM clause */ | |
| 19769 | + u32 nAlloc; /* Number of entries allocated in a[] below */ | |
| 19770 | + SrcItem a[FLEXARRAY]; /* One entry for each identifier on the list */ | |
| 19744 | 19771 | }; |
| 19772 | + | |
| 19773 | +/* Size (in bytes) of a SrcList object that can hold as many as N | |
| 19774 | +** SrcItem objects. */ | |
| 19775 | +#define SZ_SRCLIST(N) (offsetof(SrcList,a)+(N)*sizeof(SrcItem)) | |
| 19776 | + | |
| 19777 | +/* Size (in bytes( of a SrcList object that holds 1 SrcItem. This is a | |
| 19778 | +** special case of SZ_SRCITEM(1) that comes up often. */ | |
| 19779 | +#define SZ_SRCLIST_1 (offsetof(SrcList,a)+sizeof(SrcItem)) | |
| 19745 | 19780 | |
| 19746 | 19781 | /* |
| 19747 | 19782 | ** Permitted values of the SrcList.a.jointype field |
| 19748 | 19783 | */ |
| 19749 | 19784 | #define JT_INNER 0x01 /* Any kind of inner or cross join */ |
| @@ -20804,12 +20839,16 @@ | ||
| 20804 | 20839 | */ |
| 20805 | 20840 | struct With { |
| 20806 | 20841 | int nCte; /* Number of CTEs in the WITH clause */ |
| 20807 | 20842 | int bView; /* Belongs to the outermost Select of a view */ |
| 20808 | 20843 | With *pOuter; /* Containing WITH clause, or NULL */ |
| 20809 | - Cte a[1]; /* For each CTE in the WITH clause.... */ | |
| 20844 | + Cte a[FLEXARRAY]; /* For each CTE in the WITH clause.... */ | |
| 20810 | 20845 | }; |
| 20846 | + | |
| 20847 | +/* The size (in bytes) of a With object that can hold as many | |
| 20848 | +** as N different CTEs. */ | |
| 20849 | +#define SZ_WITH(N) (offsetof(With,a) + (N)*sizeof(Cte)) | |
| 20811 | 20850 | |
| 20812 | 20851 | /* |
| 20813 | 20852 | ** The Cte object is not guaranteed to persist for the entire duration |
| 20814 | 20853 | ** of code generation. (The query flattener or other parser tree |
| 20815 | 20854 | ** edits might delete it.) The following object records information |
| @@ -20835,12 +20874,16 @@ | ||
| 20835 | 20874 | */ |
| 20836 | 20875 | struct DbClientData { |
| 20837 | 20876 | DbClientData *pNext; /* Next in a linked list */ |
| 20838 | 20877 | void *pData; /* The data */ |
| 20839 | 20878 | void (*xDestructor)(void*); /* Destructor. Might be NULL */ |
| 20840 | - char zName[1]; /* Name of this client data. MUST BE LAST */ | |
| 20879 | + char zName[FLEXARRAY]; /* Name of this client data. MUST BE LAST */ | |
| 20841 | 20880 | }; |
| 20881 | + | |
| 20882 | +/* The size (in bytes) of a DbClientData object that can has a name | |
| 20883 | +** that is N bytes long, including the zero-terminator. */ | |
| 20884 | +#define SZ_DBCLIENTDATA(N) (offsetof(DbClientData,zName)+(N)) | |
| 20842 | 20885 | |
| 20843 | 20886 | #ifdef SQLITE_DEBUG |
| 20844 | 20887 | /* |
| 20845 | 20888 | ** An instance of the TreeView object is used for printing the content of |
| 20846 | 20889 | ** data structures on sqlite3DebugPrintf() using a tree-like view. |
| @@ -22673,10 +22716,13 @@ | ||
| 22673 | 22716 | "EXTRA_IFNULLROW", |
| 22674 | 22717 | #endif |
| 22675 | 22718 | #ifdef SQLITE_EXTRA_INIT |
| 22676 | 22719 | "EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT), |
| 22677 | 22720 | #endif |
| 22721 | +#ifdef SQLITE_EXTRA_INIT_MUTEXED | |
| 22722 | + "EXTRA_INIT_MUTEXED=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT_MUTEXED), | |
| 22723 | +#endif | |
| 22678 | 22724 | #ifdef SQLITE_EXTRA_SHUTDOWN |
| 22679 | 22725 | "EXTRA_SHUTDOWN=" CTIMEOPT_VAL(SQLITE_EXTRA_SHUTDOWN), |
| 22680 | 22726 | #endif |
| 22681 | 22727 | #ifdef SQLITE_FTS3_MAX_EXPR_DEPTH |
| 22682 | 22728 | "FTS3_MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_FTS3_MAX_EXPR_DEPTH), |
| @@ -23657,16 +23703,23 @@ | ||
| 23657 | 23703 | #ifdef SQLITE_ENABLE_COLUMN_USED_MASK |
| 23658 | 23704 | u64 maskUsed; /* Mask of columns used by this cursor */ |
| 23659 | 23705 | #endif |
| 23660 | 23706 | VdbeTxtBlbCache *pCache; /* Cache of large TEXT or BLOB values */ |
| 23661 | 23707 | |
| 23662 | - /* 2*nField extra array elements allocated for aType[], beyond the one | |
| 23663 | - ** static element declared in the structure. nField total array slots for | |
| 23664 | - ** aType[] and nField+1 array slots for aOffset[] */ | |
| 23665 | - u32 aType[1]; /* Type values record decode. MUST BE LAST */ | |
| 23708 | + /* Space is allocated for aType to hold at least 2*nField+1 entries: | |
| 23709 | + ** nField slots for aType[] and nField+1 array slots for aOffset[] */ | |
| 23710 | + u32 aType[FLEXARRAY]; /* Type values record decode. MUST BE LAST */ | |
| 23666 | 23711 | }; |
| 23667 | 23712 | |
| 23713 | +/* | |
| 23714 | +** The size (in bytes) of a VdbeCursor object that has an nField value of N | |
| 23715 | +** or less. The value of SZ_VDBECURSOR(n) is guaranteed to be a multiple | |
| 23716 | +** of 8. | |
| 23717 | +*/ | |
| 23718 | +#define SZ_VDBECURSOR(N) \ | |
| 23719 | + (ROUND8(offsetof(VdbeCursor,aType)) + ((N)+1)*sizeof(u64)) | |
| 23720 | + | |
| 23668 | 23721 | /* Return true if P is a null-only cursor |
| 23669 | 23722 | */ |
| 23670 | 23723 | #define IsNullCursor(P) \ |
| 23671 | 23724 | ((P)->eCurType==CURTYPE_PSEUDO && (P)->nullRow && (P)->seekResult==0) |
| 23672 | 23725 | |
| @@ -23919,13 +23972,20 @@ | ||
| 23919 | 23972 | int iOp; /* Instruction number of OP_Function */ |
| 23920 | 23973 | int isError; /* Error code returned by the function. */ |
| 23921 | 23974 | u8 enc; /* Encoding to use for results */ |
| 23922 | 23975 | u8 skipFlag; /* Skip accumulator loading if true */ |
| 23923 | 23976 | u16 argc; /* Number of arguments */ |
| 23924 | - sqlite3_value *argv[1]; /* Argument set */ | |
| 23977 | + sqlite3_value *argv[FLEXARRAY]; /* Argument set */ | |
| 23925 | 23978 | }; |
| 23926 | 23979 | |
| 23980 | +/* | |
| 23981 | +** The size (in bytes) of an sqlite3_context object that holds N | |
| 23982 | +** argv[] arguments. | |
| 23983 | +*/ | |
| 23984 | +#define SZ_CONTEXT(N) \ | |
| 23985 | + (offsetof(sqlite3_context,argv)+(N)*sizeof(sqlite3_value*)) | |
| 23986 | + | |
| 23927 | 23987 | |
| 23928 | 23988 | /* The ScanStatus object holds a single value for the |
| 23929 | 23989 | ** sqlite3_stmt_scanstatus() interface. |
| 23930 | 23990 | ** |
| 23931 | 23991 | ** aAddrRange[]: |
| @@ -24055,11 +24115,11 @@ | ||
| 24055 | 24115 | struct PreUpdate { |
| 24056 | 24116 | Vdbe *v; |
| 24057 | 24117 | VdbeCursor *pCsr; /* Cursor to read old values from */ |
| 24058 | 24118 | int op; /* One of SQLITE_INSERT, UPDATE, DELETE */ |
| 24059 | 24119 | u8 *aRecord; /* old.* database record */ |
| 24060 | - KeyInfo keyinfo; | |
| 24120 | + KeyInfo *pKeyinfo; /* Key information */ | |
| 24061 | 24121 | UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */ |
| 24062 | 24122 | UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */ |
| 24063 | 24123 | int iNewReg; /* Register for new.* values */ |
| 24064 | 24124 | int iBlobWrite; /* Value returned by preupdate_blobwrite() */ |
| 24065 | 24125 | i64 iKey1; /* First key value passed to hook */ |
| @@ -24067,10 +24127,11 @@ | ||
| 24067 | 24127 | Mem oldipk; /* Memory cell holding "old" IPK value */ |
| 24068 | 24128 | Mem *aNew; /* Array of new.* values */ |
| 24069 | 24129 | Table *pTab; /* Schema object being updated */ |
| 24070 | 24130 | Index *pPk; /* PK index if pTab is WITHOUT ROWID */ |
| 24071 | 24131 | sqlite3_value **apDflt; /* Array of default values, if required */ |
| 24132 | + u8 keyinfoSpace[SZ_KEYINFO(0)]; /* Space to hold pKeyinfo[0] content */ | |
| 24072 | 24133 | }; |
| 24073 | 24134 | |
| 24074 | 24135 | /* |
| 24075 | 24136 | ** An instance of this object is used to pass an vector of values into |
| 24076 | 24137 | ** OP_VFilter, the xFilter method of a virtual table. The vector is the |
| @@ -26000,11 +26061,11 @@ | ||
| 26000 | 26061 | ** Return the number of days after the most recent Sunday. |
| 26001 | 26062 | ** |
| 26002 | 26063 | ** In other words, return the day of the week according |
| 26003 | 26064 | ** to this code: |
| 26004 | 26065 | ** |
| 26005 | -** 0=Sunday, 1=Monday, 2=Tues, ..., 6=Saturday | |
| 26066 | +** 0=Sunday, 1=Monday, 2=Tuesday, ..., 6=Saturday | |
| 26006 | 26067 | */ |
| 26007 | 26068 | static int daysAfterSunday(DateTime *pDate){ |
| 26008 | 26069 | assert( pDate->validJD ); |
| 26009 | 26070 | return (int)((pDate->iJD+129600000)/86400000) % 7; |
| 26010 | 26071 | } |
| @@ -32378,11 +32439,11 @@ | ||
| 32378 | 32439 | u32 nBack = 0; |
| 32379 | 32440 | u32 nCtrl = 0; |
| 32380 | 32441 | for(k=0; k<i; k++){ |
| 32381 | 32442 | if( escarg[k]=='\\' ){ |
| 32382 | 32443 | nBack++; |
| 32383 | - }else if( escarg[k]<=0x1f ){ | |
| 32444 | + }else if( ((u8*)escarg)[k]<=0x1f ){ | |
| 32384 | 32445 | nCtrl++; |
| 32385 | 32446 | } |
| 32386 | 32447 | } |
| 32387 | 32448 | if( nCtrl || xtype==etESCAPE_q ){ |
| 32388 | 32449 | n += nBack + 5*nCtrl; |
| @@ -32416,11 +32477,11 @@ | ||
| 32416 | 32477 | bufpt[j++] = ch = escarg[i]; |
| 32417 | 32478 | if( ch==q ){ |
| 32418 | 32479 | bufpt[j++] = ch; |
| 32419 | 32480 | }else if( ch=='\\' ){ |
| 32420 | 32481 | bufpt[j++] = '\\'; |
| 32421 | - }else if( ch<=0x1f ){ | |
| 32482 | + }else if( ((unsigned char)ch)<=0x1f ){ | |
| 32422 | 32483 | bufpt[j-1] = '\\'; |
| 32423 | 32484 | bufpt[j++] = 'u'; |
| 32424 | 32485 | bufpt[j++] = '0'; |
| 32425 | 32486 | bufpt[j++] = '0'; |
| 32426 | 32487 | bufpt[j++] = ch>=0x10 ? '1' : '0'; |
| @@ -37067,11 +37128,11 @@ | ||
| 37067 | 37128 | return 0; |
| 37068 | 37129 | #endif |
| 37069 | 37130 | } |
| 37070 | 37131 | |
| 37071 | 37132 | /* |
| 37072 | -** Compute the absolute value of a 32-bit signed integer, of possible. Or | |
| 37133 | +** Compute the absolute value of a 32-bit signed integer, if possible. Or | |
| 37073 | 37134 | ** if the integer has a value of -2147483648, return +2147483647 |
| 37074 | 37135 | */ |
| 37075 | 37136 | SQLITE_PRIVATE int sqlite3AbsInt32(int x){ |
| 37076 | 37137 | if( x>=0 ) return x; |
| 37077 | 37138 | if( x==(int)0x80000000 ) return 0x7fffffff; |
| @@ -45614,11 +45675,11 @@ | ||
| 45614 | 45675 | sp.tv_sec = microseconds / 1000000; |
| 45615 | 45676 | sp.tv_nsec = (microseconds % 1000000) * 1000; |
| 45616 | 45677 | |
| 45617 | 45678 | /* Almost all modern unix systems support nanosleep(). But if you are |
| 45618 | 45679 | ** compiling for one of the rare exceptions, you can use |
| 45619 | - ** -DHAVE_NANOSLEEP=0 (perhaps in conjuction with -DHAVE_USLEEP if | |
| 45680 | + ** -DHAVE_NANOSLEEP=0 (perhaps in conjunction with -DHAVE_USLEEP if | |
| 45620 | 45681 | ** usleep() is available) in order to bypass the use of nanosleep() */ |
| 45621 | 45682 | nanosleep(&sp, NULL); |
| 45622 | 45683 | |
| 45623 | 45684 | UNUSED_PARAMETER(NotUsed); |
| 45624 | 45685 | return microseconds; |
| @@ -56047,14 +56108,10 @@ | ||
| 56047 | 56108 | void *pStart, *pEnd; /* Bounds of global page cache memory */ |
| 56048 | 56109 | /* Above requires no mutex. Use mutex below for variable that follow. */ |
| 56049 | 56110 | sqlite3_mutex *mutex; /* Mutex for accessing the following: */ |
| 56050 | 56111 | PgFreeslot *pFree; /* Free page blocks */ |
| 56051 | 56112 | int nFreeSlot; /* Number of unused pcache slots */ |
| 56052 | - /* The following value requires a mutex to change. We skip the mutex on | |
| 56053 | - ** reading because (1) most platforms read a 32-bit integer atomically and | |
| 56054 | - ** (2) even if an incorrect value is read, no great harm is done since this | |
| 56055 | - ** is really just an optimization. */ | |
| 56056 | 56113 | int bUnderPressure; /* True if low on PAGECACHE memory */ |
| 56057 | 56114 | } pcache1_g; |
| 56058 | 56115 | |
| 56059 | 56116 | /* |
| 56060 | 56117 | ** All code in this file should access the global structure above via the |
| @@ -56098,11 +56155,11 @@ | ||
| 56098 | 56155 | pcache1.szSlot = sz; |
| 56099 | 56156 | pcache1.nSlot = pcache1.nFreeSlot = n; |
| 56100 | 56157 | pcache1.nReserve = n>90 ? 10 : (n/10 + 1); |
| 56101 | 56158 | pcache1.pStart = pBuf; |
| 56102 | 56159 | pcache1.pFree = 0; |
| 56103 | - pcache1.bUnderPressure = 0; | |
| 56160 | + AtomicStore(&pcache1.bUnderPressure,0); | |
| 56104 | 56161 | while( n-- ){ |
| 56105 | 56162 | p = (PgFreeslot*)pBuf; |
| 56106 | 56163 | p->pNext = pcache1.pFree; |
| 56107 | 56164 | pcache1.pFree = p; |
| 56108 | 56165 | pBuf = (void*)&((char*)pBuf)[sz]; |
| @@ -56166,11 +56223,11 @@ | ||
| 56166 | 56223 | sqlite3_mutex_enter(pcache1.mutex); |
| 56167 | 56224 | p = (PgHdr1 *)pcache1.pFree; |
| 56168 | 56225 | if( p ){ |
| 56169 | 56226 | pcache1.pFree = pcache1.pFree->pNext; |
| 56170 | 56227 | pcache1.nFreeSlot--; |
| 56171 | - pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve; | |
| 56228 | + AtomicStore(&pcache1.bUnderPressure,pcache1.nFreeSlot<pcache1.nReserve); | |
| 56172 | 56229 | assert( pcache1.nFreeSlot>=0 ); |
| 56173 | 56230 | sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte); |
| 56174 | 56231 | sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1); |
| 56175 | 56232 | } |
| 56176 | 56233 | sqlite3_mutex_leave(pcache1.mutex); |
| @@ -56205,11 +56262,11 @@ | ||
| 56205 | 56262 | sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1); |
| 56206 | 56263 | pSlot = (PgFreeslot*)p; |
| 56207 | 56264 | pSlot->pNext = pcache1.pFree; |
| 56208 | 56265 | pcache1.pFree = pSlot; |
| 56209 | 56266 | pcache1.nFreeSlot++; |
| 56210 | - pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve; | |
| 56267 | + AtomicStore(&pcache1.bUnderPressure,pcache1.nFreeSlot<pcache1.nReserve); | |
| 56211 | 56268 | assert( pcache1.nFreeSlot<=pcache1.nSlot ); |
| 56212 | 56269 | sqlite3_mutex_leave(pcache1.mutex); |
| 56213 | 56270 | }else{ |
| 56214 | 56271 | assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) ); |
| 56215 | 56272 | sqlite3MemdebugSetType(p, MEMTYPE_HEAP); |
| @@ -56336,11 +56393,11 @@ | ||
| 56336 | 56393 | ** allocating a new page cache entry in order to avoid stressing |
| 56337 | 56394 | ** the heap even further. |
| 56338 | 56395 | */ |
| 56339 | 56396 | static int pcache1UnderMemoryPressure(PCache1 *pCache){ |
| 56340 | 56397 | if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){ |
| 56341 | - return pcache1.bUnderPressure; | |
| 56398 | + return AtomicLoad(&pcache1.bUnderPressure); | |
| 56342 | 56399 | }else{ |
| 56343 | 56400 | return sqlite3HeapNearlyFull(); |
| 56344 | 56401 | } |
| 56345 | 56402 | } |
| 56346 | 56403 | |
| @@ -66177,12 +66234,16 @@ | ||
| 66177 | 66234 | int iNext; /* Next slot in aIndex[] not yet returned */ |
| 66178 | 66235 | ht_slot *aIndex; /* i0, i1, i2... such that aPgno[iN] ascend */ |
| 66179 | 66236 | u32 *aPgno; /* Array of page numbers. */ |
| 66180 | 66237 | int nEntry; /* Nr. of entries in aPgno[] and aIndex[] */ |
| 66181 | 66238 | int iZero; /* Frame number associated with aPgno[0] */ |
| 66182 | - } aSegment[1]; /* One for every 32KB page in the wal-index */ | |
| 66239 | + } aSegment[FLEXARRAY]; /* One for every 32KB page in the wal-index */ | |
| 66183 | 66240 | }; |
| 66241 | + | |
| 66242 | +/* Size (in bytes) of a WalIterator object suitable for N or fewer segments */ | |
| 66243 | +#define SZ_WALITERATOR(N) \ | |
| 66244 | + (offsetof(WalIterator,aSegment)*(N)*sizeof(struct WalSegment)) | |
| 66184 | 66245 | |
| 66185 | 66246 | /* |
| 66186 | 66247 | ** Define the parameters of the hash tables in the wal-index file. There |
| 66187 | 66248 | ** is a hash-table following every HASHTABLE_NPAGE page numbers in the |
| 66188 | 66249 | ** wal-index. |
| @@ -67540,12 +67601,11 @@ | ||
| 67540 | 67601 | assert( pWal->ckptLock && pWal->hdr.mxFrame>0 ); |
| 67541 | 67602 | iLast = pWal->hdr.mxFrame; |
| 67542 | 67603 | |
| 67543 | 67604 | /* Allocate space for the WalIterator object. */ |
| 67544 | 67605 | nSegment = walFramePage(iLast) + 1; |
| 67545 | - nByte = sizeof(WalIterator) | |
| 67546 | - + (nSegment-1)*sizeof(struct WalSegment) | |
| 67606 | + nByte = SZ_WALITERATOR(nSegment) | |
| 67547 | 67607 | + iLast*sizeof(ht_slot); |
| 67548 | 67608 | p = (WalIterator *)sqlite3_malloc64(nByte |
| 67549 | 67609 | + sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast) |
| 67550 | 67610 | ); |
| 67551 | 67611 | if( !p ){ |
| @@ -86029,16 +86089,14 @@ | ||
| 86029 | 86089 | int nArg, /* Number of argument */ |
| 86030 | 86090 | const FuncDef *pFunc, /* The function to be invoked */ |
| 86031 | 86091 | int eCallCtx /* Calling context */ |
| 86032 | 86092 | ){ |
| 86033 | 86093 | Vdbe *v = pParse->pVdbe; |
| 86034 | - int nByte; | |
| 86035 | 86094 | int addr; |
| 86036 | 86095 | sqlite3_context *pCtx; |
| 86037 | 86096 | assert( v ); |
| 86038 | - nByte = sizeof(*pCtx) + (nArg-1)*sizeof(sqlite3_value*); | |
| 86039 | - pCtx = sqlite3DbMallocRawNN(pParse->db, nByte); | |
| 86097 | + pCtx = sqlite3DbMallocRawNN(pParse->db, SZ_CONTEXT(nArg)); | |
| 86040 | 86098 | if( pCtx==0 ){ |
| 86041 | 86099 | assert( pParse->db->mallocFailed ); |
| 86042 | 86100 | freeEphemeralFunction(pParse->db, (FuncDef*)pFunc); |
| 86043 | 86101 | return 0; |
| 86044 | 86102 | } |
| @@ -91110,25 +91168,26 @@ | ||
| 91110 | 91168 | |
| 91111 | 91169 | preupdate.v = v; |
| 91112 | 91170 | preupdate.pCsr = pCsr; |
| 91113 | 91171 | preupdate.op = op; |
| 91114 | 91172 | preupdate.iNewReg = iReg; |
| 91115 | - preupdate.keyinfo.db = db; | |
| 91116 | - preupdate.keyinfo.enc = ENC(db); | |
| 91117 | - preupdate.keyinfo.nKeyField = pTab->nCol; | |
| 91118 | - preupdate.keyinfo.aSortFlags = (u8*)&fakeSortOrder; | |
| 91173 | + preupdate.pKeyinfo = (KeyInfo*)&preupdate.keyinfoSpace; | |
| 91174 | + preupdate.pKeyinfo->db = db; | |
| 91175 | + preupdate.pKeyinfo->enc = ENC(db); | |
| 91176 | + preupdate.pKeyinfo->nKeyField = pTab->nCol; | |
| 91177 | + preupdate.pKeyinfo->aSortFlags = (u8*)&fakeSortOrder; | |
| 91119 | 91178 | preupdate.iKey1 = iKey1; |
| 91120 | 91179 | preupdate.iKey2 = iKey2; |
| 91121 | 91180 | preupdate.pTab = pTab; |
| 91122 | 91181 | preupdate.iBlobWrite = iBlobWrite; |
| 91123 | 91182 | |
| 91124 | 91183 | db->pPreUpdate = &preupdate; |
| 91125 | 91184 | db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2); |
| 91126 | 91185 | db->pPreUpdate = 0; |
| 91127 | 91186 | sqlite3DbFree(db, preupdate.aRecord); |
| 91128 | - vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked); | |
| 91129 | - vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked); | |
| 91187 | + vdbeFreeUnpacked(db, preupdate.pKeyinfo->nKeyField+1,preupdate.pUnpacked); | |
| 91188 | + vdbeFreeUnpacked(db, preupdate.pKeyinfo->nKeyField+1,preupdate.pNewUnpacked); | |
| 91130 | 91189 | sqlite3VdbeMemRelease(&preupdate.oldipk); |
| 91131 | 91190 | if( preupdate.aNew ){ |
| 91132 | 91191 | int i; |
| 91133 | 91192 | for(i=0; i<pCsr->nField; i++){ |
| 91134 | 91193 | sqlite3VdbeMemRelease(&preupdate.aNew[i]); |
| @@ -93363,11 +93422,11 @@ | ||
| 93363 | 93422 | nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor); |
| 93364 | 93423 | aRec = sqlite3DbMallocRaw(db, nRec); |
| 93365 | 93424 | if( !aRec ) goto preupdate_old_out; |
| 93366 | 93425 | rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec); |
| 93367 | 93426 | if( rc==SQLITE_OK ){ |
| 93368 | - p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec); | |
| 93427 | + p->pUnpacked = vdbeUnpackRecord(p->pKeyinfo, nRec, aRec); | |
| 93369 | 93428 | if( !p->pUnpacked ) rc = SQLITE_NOMEM; |
| 93370 | 93429 | } |
| 93371 | 93430 | if( rc!=SQLITE_OK ){ |
| 93372 | 93431 | sqlite3DbFree(db, aRec); |
| 93373 | 93432 | goto preupdate_old_out; |
| @@ -93428,11 +93487,11 @@ | ||
| 93428 | 93487 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 93429 | 93488 | p = db!=0 ? db->pPreUpdate : 0; |
| 93430 | 93489 | #else |
| 93431 | 93490 | p = db->pPreUpdate; |
| 93432 | 93491 | #endif |
| 93433 | - return (p ? p->keyinfo.nKeyField : 0); | |
| 93492 | + return (p ? p->pKeyinfo->nKeyField : 0); | |
| 93434 | 93493 | } |
| 93435 | 93494 | #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ |
| 93436 | 93495 | |
| 93437 | 93496 | #ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
| 93438 | 93497 | /* |
| @@ -93511,11 +93570,11 @@ | ||
| 93511 | 93570 | UnpackedRecord *pUnpack = p->pNewUnpacked; |
| 93512 | 93571 | if( !pUnpack ){ |
| 93513 | 93572 | Mem *pData = &p->v->aMem[p->iNewReg]; |
| 93514 | 93573 | rc = ExpandBlob(pData); |
| 93515 | 93574 | if( rc!=SQLITE_OK ) goto preupdate_new_out; |
| 93516 | - pUnpack = vdbeUnpackRecord(&p->keyinfo, pData->n, pData->z); | |
| 93575 | + pUnpack = vdbeUnpackRecord(p->pKeyinfo, pData->n, pData->z); | |
| 93517 | 93576 | if( !pUnpack ){ |
| 93518 | 93577 | rc = SQLITE_NOMEM; |
| 93519 | 93578 | goto preupdate_new_out; |
| 93520 | 93579 | } |
| 93521 | 93580 | p->pNewUnpacked = pUnpack; |
| @@ -94305,13 +94364,13 @@ | ||
| 94305 | 94364 | */ |
| 94306 | 94365 | Mem *pMem = iCur>0 ? &p->aMem[p->nMem-iCur] : p->aMem; |
| 94307 | 94366 | |
| 94308 | 94367 | i64 nByte; |
| 94309 | 94368 | VdbeCursor *pCx = 0; |
| 94310 | - nByte = | |
| 94311 | - ROUND8P(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField + | |
| 94312 | - (eCurType==CURTYPE_BTREE?sqlite3BtreeCursorSize():0); | |
| 94369 | + nByte = SZ_VDBECURSOR(nField); | |
| 94370 | + assert( ROUND8(nByte)==nByte ); | |
| 94371 | + if( eCurType==CURTYPE_BTREE ) nByte += sqlite3BtreeCursorSize(); | |
| 94313 | 94372 | |
| 94314 | 94373 | assert( iCur>=0 && iCur<p->nCursor ); |
| 94315 | 94374 | if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/ |
| 94316 | 94375 | sqlite3VdbeFreeCursorNN(p, p->apCsr[iCur]); |
| 94317 | 94376 | p->apCsr[iCur] = 0; |
| @@ -94340,12 +94399,12 @@ | ||
| 94340 | 94399 | memset(pCx, 0, offsetof(VdbeCursor,pAltCursor)); |
| 94341 | 94400 | pCx->eCurType = eCurType; |
| 94342 | 94401 | pCx->nField = nField; |
| 94343 | 94402 | pCx->aOffset = &pCx->aType[nField]; |
| 94344 | 94403 | if( eCurType==CURTYPE_BTREE ){ |
| 94345 | - pCx->uc.pCursor = (BtCursor*) | |
| 94346 | - &pMem->z[ROUND8P(sizeof(VdbeCursor))+2*sizeof(u32)*nField]; | |
| 94404 | + assert( ROUND8(SZ_VDBECURSOR(nField))==SZ_VDBECURSOR(nField) ); | |
| 94405 | + pCx->uc.pCursor = (BtCursor*)&pMem->z[SZ_VDBECURSOR(nField)]; | |
| 94347 | 94406 | sqlite3BtreeCursorZero(pCx->uc.pCursor); |
| 94348 | 94407 | } |
| 94349 | 94408 | return pCx; |
| 94350 | 94409 | } |
| 94351 | 94410 | |
| @@ -100083,11 +100142,11 @@ | ||
| 100083 | 100142 | pCrsr = pC->uc.pCursor; |
| 100084 | 100143 | |
| 100085 | 100144 | /* The OP_RowData opcodes always follow OP_NotExists or |
| 100086 | 100145 | ** OP_SeekRowid or OP_Rewind/Op_Next with no intervening instructions |
| 100087 | 100146 | ** that might invalidate the cursor. |
| 100088 | - ** If this where not the case, on of the following assert()s | |
| 100147 | + ** If this were not the case, one of the following assert()s | |
| 100089 | 100148 | ** would fail. Should this ever change (because of changes in the code |
| 100090 | 100149 | ** generator) then the fix would be to insert a call to |
| 100091 | 100150 | ** sqlite3VdbeCursorMoveto(). |
| 100092 | 100151 | */ |
| 100093 | 100152 | assert( pC->deferredMoveto==0 ); |
| @@ -101732,11 +101791,11 @@ | ||
| 101732 | 101791 | ** cell in which to store the accumulation. Be careful that the memory |
| 101733 | 101792 | ** cell is 8-byte aligned, even on platforms where a pointer is 32-bits. |
| 101734 | 101793 | ** |
| 101735 | 101794 | ** Note: We could avoid this by using a regular memory cell from aMem[] for |
| 101736 | 101795 | ** the accumulator, instead of allocating one here. */ |
| 101737 | - nAlloc = ROUND8P( sizeof(pCtx[0]) + (n-1)*sizeof(sqlite3_value*) ); | |
| 101796 | + nAlloc = ROUND8P( SZ_CONTEXT(n) ); | |
| 101738 | 101797 | pCtx = sqlite3DbMallocRawNN(db, nAlloc + sizeof(Mem)); |
| 101739 | 101798 | if( pCtx==0 ) goto no_mem; |
| 101740 | 101799 | pCtx->pOut = (Mem*)((u8*)pCtx + nAlloc); |
| 101741 | 101800 | assert( EIGHT_BYTE_ALIGNMENT(pCtx->pOut) ); |
| 101742 | 101801 | |
| @@ -103390,10 +103449,11 @@ | ||
| 103390 | 103449 | int iCol; /* Index of zColumn in row-record */ |
| 103391 | 103450 | int rc = SQLITE_OK; |
| 103392 | 103451 | char *zErr = 0; |
| 103393 | 103452 | Table *pTab; |
| 103394 | 103453 | Incrblob *pBlob = 0; |
| 103454 | + int iDb; | |
| 103395 | 103455 | Parse sParse; |
| 103396 | 103456 | |
| 103397 | 103457 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 103398 | 103458 | if( ppBlob==0 ){ |
| 103399 | 103459 | return SQLITE_MISUSE_BKPT; |
| @@ -103435,11 +103495,14 @@ | ||
| 103435 | 103495 | if( pTab && IsView(pTab) ){ |
| 103436 | 103496 | pTab = 0; |
| 103437 | 103497 | sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable); |
| 103438 | 103498 | } |
| 103439 | 103499 | #endif |
| 103440 | - if( !pTab ){ | |
| 103500 | + if( pTab==0 | |
| 103501 | + || ((iDb = sqlite3SchemaToIndex(db, pTab->pSchema))==1 && | |
| 103502 | + sqlite3OpenTempDatabase(&sParse)) | |
| 103503 | + ){ | |
| 103441 | 103504 | if( sParse.zErrMsg ){ |
| 103442 | 103505 | sqlite3DbFree(db, zErr); |
| 103443 | 103506 | zErr = sParse.zErrMsg; |
| 103444 | 103507 | sParse.zErrMsg = 0; |
| 103445 | 103508 | } |
| @@ -103446,11 +103509,11 @@ | ||
| 103446 | 103509 | rc = SQLITE_ERROR; |
| 103447 | 103510 | sqlite3BtreeLeaveAll(db); |
| 103448 | 103511 | goto blob_open_out; |
| 103449 | 103512 | } |
| 103450 | 103513 | pBlob->pTab = pTab; |
| 103451 | - pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName; | |
| 103514 | + pBlob->zDb = db->aDb[iDb].zDbSName; | |
| 103452 | 103515 | |
| 103453 | 103516 | /* Now search pTab for the exact column. */ |
| 103454 | 103517 | iCol = sqlite3ColumnIndex(pTab, zColumn); |
| 103455 | 103518 | if( iCol<0 ){ |
| 103456 | 103519 | sqlite3DbFree(db, zErr); |
| @@ -103530,11 +103593,10 @@ | ||
| 103530 | 103593 | {OP_Column, 0, 0, 1}, /* 3 */ |
| 103531 | 103594 | {OP_ResultRow, 1, 0, 0}, /* 4 */ |
| 103532 | 103595 | {OP_Halt, 0, 0, 0}, /* 5 */ |
| 103533 | 103596 | }; |
| 103534 | 103597 | Vdbe *v = (Vdbe *)pBlob->pStmt; |
| 103535 | - int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); | |
| 103536 | 103598 | VdbeOp *aOp; |
| 103537 | 103599 | |
| 103538 | 103600 | sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag, |
| 103539 | 103601 | pTab->pSchema->schema_cookie, |
| 103540 | 103602 | pTab->pSchema->iGeneration); |
| @@ -104108,12 +104170,15 @@ | ||
| 104108 | 104170 | u8 bUsePMA; /* True if one or more PMAs created */ |
| 104109 | 104171 | u8 bUseThreads; /* True to use background threads */ |
| 104110 | 104172 | u8 iPrev; /* Previous thread used to flush PMA */ |
| 104111 | 104173 | u8 nTask; /* Size of aTask[] array */ |
| 104112 | 104174 | u8 typeMask; |
| 104113 | - SortSubtask aTask[1]; /* One or more subtasks */ | |
| 104175 | + SortSubtask aTask[FLEXARRAY]; /* One or more subtasks */ | |
| 104114 | 104176 | }; |
| 104177 | + | |
| 104178 | +/* Size (in bytes) of a VdbeSorter object that works with N or fewer subtasks */ | |
| 104179 | +#define SZ_VDBESORTER(N) (offsetof(VdbeSorter,aTask)+(N)*sizeof(SortSubtask)) | |
| 104115 | 104180 | |
| 104116 | 104181 | #define SORTER_TYPE_INTEGER 0x01 |
| 104117 | 104182 | #define SORTER_TYPE_TEXT 0x02 |
| 104118 | 104183 | |
| 104119 | 104184 | /* |
| @@ -104742,12 +104807,12 @@ | ||
| 104742 | 104807 | assert( pCsr->pKeyInfo ); |
| 104743 | 104808 | assert( !pCsr->isEphemeral ); |
| 104744 | 104809 | assert( pCsr->eCurType==CURTYPE_SORTER ); |
| 104745 | 104810 | assert( sizeof(KeyInfo) + UMXV(pCsr->pKeyInfo->nKeyField)*sizeof(CollSeq*) |
| 104746 | 104811 | < 0x7fffffff ); |
| 104747 | - szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nKeyField-1)*sizeof(CollSeq*); | |
| 104748 | - sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask); | |
| 104812 | + szKeyInfo = SZ_KEYINFO(pCsr->pKeyInfo->nKeyField+1); | |
| 104813 | + sz = SZ_VDBESORTER(nWorker+1); | |
| 104749 | 104814 | |
| 104750 | 104815 | pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo); |
| 104751 | 104816 | pCsr->uc.pSorter = pSorter; |
| 104752 | 104817 | if( pSorter==0 ){ |
| 104753 | 104818 | rc = SQLITE_NOMEM_BKPT; |
| @@ -105207,10 +105272,14 @@ | ||
| 105207 | 105272 | } |
| 105208 | 105273 | |
| 105209 | 105274 | p->u.pNext = 0; |
| 105210 | 105275 | for(i=0; aSlot[i]; i++){ |
| 105211 | 105276 | p = vdbeSorterMerge(pTask, p, aSlot[i]); |
| 105277 | + /* ,--Each aSlot[] holds twice as much as the previous. So we cannot use | |
| 105278 | + ** | up all 64 aSlots[] with only a 64-bit address space. | |
| 105279 | + ** v */ | |
| 105280 | + assert( i<ArraySize(aSlot) ); | |
| 105212 | 105281 | aSlot[i] = 0; |
| 105213 | 105282 | } |
| 105214 | 105283 | aSlot[i] = p; |
| 105215 | 105284 | p = pNext; |
| 105216 | 105285 | } |
| @@ -109981,32 +110050,34 @@ | ||
| 109981 | 110050 | Table *pTab, /* The table being referenced, or NULL */ |
| 109982 | 110051 | int type, /* NC_IsCheck, NC_PartIdx, NC_IdxExpr, NC_GenCol, or 0 */ |
| 109983 | 110052 | Expr *pExpr, /* Expression to resolve. May be NULL. */ |
| 109984 | 110053 | ExprList *pList /* Expression list to resolve. May be NULL. */ |
| 109985 | 110054 | ){ |
| 109986 | - SrcList sSrc; /* Fake SrcList for pParse->pNewTable */ | |
| 110055 | + SrcList *pSrc; /* Fake SrcList for pParse->pNewTable */ | |
| 109987 | 110056 | NameContext sNC; /* Name context for pParse->pNewTable */ |
| 109988 | 110057 | int rc; |
| 110058 | + u8 srcSpace[SZ_SRCLIST_1]; /* Memory space for the fake SrcList */ | |
| 109989 | 110059 | |
| 109990 | 110060 | assert( type==0 || pTab!=0 ); |
| 109991 | 110061 | assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr |
| 109992 | 110062 | || type==NC_GenCol || pTab==0 ); |
| 109993 | 110063 | memset(&sNC, 0, sizeof(sNC)); |
| 109994 | - memset(&sSrc, 0, sizeof(sSrc)); | |
| 110064 | + pSrc = (SrcList*)srcSpace; | |
| 110065 | + memset(pSrc, 0, SZ_SRCLIST_1); | |
| 109995 | 110066 | if( pTab ){ |
| 109996 | - sSrc.nSrc = 1; | |
| 109997 | - sSrc.a[0].zName = pTab->zName; | |
| 109998 | - sSrc.a[0].pSTab = pTab; | |
| 109999 | - sSrc.a[0].iCursor = -1; | |
| 110067 | + pSrc->nSrc = 1; | |
| 110068 | + pSrc->a[0].zName = pTab->zName; | |
| 110069 | + pSrc->a[0].pSTab = pTab; | |
| 110070 | + pSrc->a[0].iCursor = -1; | |
| 110000 | 110071 | if( pTab->pSchema!=pParse->db->aDb[1].pSchema ){ |
| 110001 | 110072 | /* Cause EP_FromDDL to be set on TK_FUNCTION nodes of non-TEMP |
| 110002 | 110073 | ** schema elements */ |
| 110003 | 110074 | type |= NC_FromDDL; |
| 110004 | 110075 | } |
| 110005 | 110076 | } |
| 110006 | 110077 | sNC.pParse = pParse; |
| 110007 | - sNC.pSrcList = &sSrc; | |
| 110078 | + sNC.pSrcList = pSrc; | |
| 110008 | 110079 | sNC.ncFlags = type | NC_IsDDL; |
| 110009 | 110080 | if( (rc = sqlite3ResolveExprNames(&sNC, pExpr))!=SQLITE_OK ) return rc; |
| 110010 | 110081 | if( pList ) rc = sqlite3ResolveExprListNames(&sNC, pList); |
| 110011 | 110082 | return rc; |
| 110012 | 110083 | } |
| @@ -111751,11 +111822,11 @@ | ||
| 111751 | 111822 | */ |
| 111752 | 111823 | #ifndef SQLITE_OMIT_CTE |
| 111753 | 111824 | SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p){ |
| 111754 | 111825 | With *pRet = 0; |
| 111755 | 111826 | if( p ){ |
| 111756 | - sqlite3_int64 nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1); | |
| 111827 | + sqlite3_int64 nByte = SZ_WITH(p->nCte); | |
| 111757 | 111828 | pRet = sqlite3DbMallocZero(db, nByte); |
| 111758 | 111829 | if( pRet ){ |
| 111759 | 111830 | int i; |
| 111760 | 111831 | pRet->nCte = p->nCte; |
| 111761 | 111832 | for(i=0; i<p->nCte; i++){ |
| @@ -111878,15 +111949,13 @@ | ||
| 111878 | 111949 | #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \ |
| 111879 | 111950 | || !defined(SQLITE_OMIT_SUBQUERY) |
| 111880 | 111951 | SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int flags){ |
| 111881 | 111952 | SrcList *pNew; |
| 111882 | 111953 | int i; |
| 111883 | - int nByte; | |
| 111884 | 111954 | assert( db!=0 ); |
| 111885 | 111955 | if( p==0 ) return 0; |
| 111886 | - nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0); | |
| 111887 | - pNew = sqlite3DbMallocRawNN(db, nByte ); | |
| 111956 | + pNew = sqlite3DbMallocRawNN(db, SZ_SRCLIST(p->nSrc) ); | |
| 111888 | 111957 | if( pNew==0 ) return 0; |
| 111889 | 111958 | pNew->nSrc = pNew->nAlloc = p->nSrc; |
| 111890 | 111959 | for(i=0; i<p->nSrc; i++){ |
| 111891 | 111960 | SrcItem *pNewItem = &pNew->a[i]; |
| 111892 | 111961 | const SrcItem *pOldItem = &p->a[i]; |
| @@ -111944,11 +112013,11 @@ | ||
| 111944 | 112013 | SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, const IdList *p){ |
| 111945 | 112014 | IdList *pNew; |
| 111946 | 112015 | int i; |
| 111947 | 112016 | assert( db!=0 ); |
| 111948 | 112017 | if( p==0 ) return 0; |
| 111949 | - pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew)+(p->nId-1)*sizeof(p->a[0]) ); | |
| 112018 | + pNew = sqlite3DbMallocRawNN(db, SZ_IDLIST(p->nId)); | |
| 111950 | 112019 | if( pNew==0 ) return 0; |
| 111951 | 112020 | pNew->nId = p->nId; |
| 111952 | 112021 | for(i=0; i<p->nId; i++){ |
| 111953 | 112022 | struct IdList_item *pNewItem = &pNew->a[i]; |
| 111954 | 112023 | const struct IdList_item *pOldItem = &p->a[i]; |
| @@ -112028,11 +112097,11 @@ | ||
| 112028 | 112097 | Expr *pExpr /* Expression to be appended. Might be NULL */ |
| 112029 | 112098 | ){ |
| 112030 | 112099 | struct ExprList_item *pItem; |
| 112031 | 112100 | ExprList *pList; |
| 112032 | 112101 | |
| 112033 | - pList = sqlite3DbMallocRawNN(db, sizeof(ExprList)+sizeof(pList->a[0])*4 ); | |
| 112102 | + pList = sqlite3DbMallocRawNN(db, SZ_EXPRLIST(4)); | |
| 112034 | 112103 | if( pList==0 ){ |
| 112035 | 112104 | sqlite3ExprDelete(db, pExpr); |
| 112036 | 112105 | return 0; |
| 112037 | 112106 | } |
| 112038 | 112107 | pList->nAlloc = 4; |
| @@ -112048,12 +112117,11 @@ | ||
| 112048 | 112117 | Expr *pExpr /* Expression to be appended. Might be NULL */ |
| 112049 | 112118 | ){ |
| 112050 | 112119 | struct ExprList_item *pItem; |
| 112051 | 112120 | ExprList *pNew; |
| 112052 | 112121 | pList->nAlloc *= 2; |
| 112053 | - pNew = sqlite3DbRealloc(db, pList, | |
| 112054 | - sizeof(*pList)+(pList->nAlloc-1)*sizeof(pList->a[0])); | |
| 112122 | + pNew = sqlite3DbRealloc(db, pList, SZ_EXPRLIST(pList->nAlloc)); | |
| 112055 | 112123 | if( pNew==0 ){ |
| 112056 | 112124 | sqlite3ExprListDelete(db, pList); |
| 112057 | 112125 | sqlite3ExprDelete(db, pExpr); |
| 112058 | 112126 | return 0; |
| 112059 | 112127 | }else{ |
| @@ -114685,11 +114753,11 @@ | ||
| 114685 | 114753 | return -1; /* Not found */ |
| 114686 | 114754 | } |
| 114687 | 114755 | |
| 114688 | 114756 | |
| 114689 | 114757 | /* |
| 114690 | -** Expresion pExpr is guaranteed to be a TK_COLUMN or equivalent. This | |
| 114758 | +** Expression pExpr is guaranteed to be a TK_COLUMN or equivalent. This | |
| 114691 | 114759 | ** function checks the Parse.pIdxPartExpr list to see if this column |
| 114692 | 114760 | ** can be replaced with a constant value. If so, it generates code to |
| 114693 | 114761 | ** put the constant value in a register (ideally, but not necessarily, |
| 114694 | 114762 | ** register iTarget) and returns the register number. |
| 114695 | 114763 | ** |
| @@ -118531,10 +118599,11 @@ | ||
| 118531 | 118599 | sqlite3 *db, /* Database handle */ |
| 118532 | 118600 | const char *zSql, /* SQL to parse */ |
| 118533 | 118601 | int bTemp /* True if SQL is from temp schema */ |
| 118534 | 118602 | ){ |
| 118535 | 118603 | int rc; |
| 118604 | + u64 flags; | |
| 118536 | 118605 | |
| 118537 | 118606 | sqlite3ParseObjectInit(p, db); |
| 118538 | 118607 | if( zSql==0 ){ |
| 118539 | 118608 | return SQLITE_NOMEM; |
| 118540 | 118609 | } |
| @@ -118549,11 +118618,15 @@ | ||
| 118549 | 118618 | db->init.iDb = (u8)iDb; |
| 118550 | 118619 | } |
| 118551 | 118620 | p->eParseMode = PARSE_MODE_RENAME; |
| 118552 | 118621 | p->db = db; |
| 118553 | 118622 | p->nQueryLoop = 1; |
| 118623 | + flags = db->flags; | |
| 118624 | + testcase( (db->flags & SQLITE_Comments)==0 && strstr(zSql," /* ")!=0 ); | |
| 118625 | + db->flags |= SQLITE_Comments; | |
| 118554 | 118626 | rc = sqlite3RunParser(p, zSql); |
| 118627 | + db->flags = flags; | |
| 118555 | 118628 | if( db->mallocFailed ) rc = SQLITE_NOMEM; |
| 118556 | 118629 | if( rc==SQLITE_OK |
| 118557 | 118630 | && NEVER(p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0) |
| 118558 | 118631 | ){ |
| 118559 | 118632 | rc = SQLITE_CORRUPT_BKPT; |
| @@ -119445,11 +119518,11 @@ | ||
| 119445 | 119518 | int rc; |
| 119446 | 119519 | Parse sParse; |
| 119447 | 119520 | u64 flags = db->flags; |
| 119448 | 119521 | if( bNoDQS ) db->flags &= ~(SQLITE_DqsDML|SQLITE_DqsDDL); |
| 119449 | 119522 | rc = renameParseSql(&sParse, zDb, db, zInput, bTemp); |
| 119450 | - db->flags |= (flags & (SQLITE_DqsDML|SQLITE_DqsDDL)); | |
| 119523 | + db->flags = flags; | |
| 119451 | 119524 | if( rc==SQLITE_OK ){ |
| 119452 | 119525 | if( isLegacy==0 && sParse.pNewTable && IsView(sParse.pNewTable) ){ |
| 119453 | 119526 | NameContext sNC; |
| 119454 | 119527 | memset(&sNC, 0, sizeof(sNC)); |
| 119455 | 119528 | sNC.pParse = &sParse; |
| @@ -126268,11 +126341,11 @@ | ||
| 126268 | 126341 | "columns in the referenced table"); |
| 126269 | 126342 | goto fk_end; |
| 126270 | 126343 | }else{ |
| 126271 | 126344 | nCol = pFromCol->nExpr; |
| 126272 | 126345 | } |
| 126273 | - nByte = sizeof(*pFKey) + (nCol-1)*sizeof(pFKey->aCol[0]) + pTo->n + 1; | |
| 126346 | + nByte = SZ_FKEY(nCol) + pTo->n + 1; | |
| 126274 | 126347 | if( pToCol ){ |
| 126275 | 126348 | for(i=0; i<pToCol->nExpr; i++){ |
| 126276 | 126349 | nByte += sqlite3Strlen30(pToCol->a[i].zEName) + 1; |
| 126277 | 126350 | } |
| 126278 | 126351 | } |
| @@ -127327,16 +127400,15 @@ | ||
| 127327 | 127400 | */ |
| 127328 | 127401 | SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *pToken){ |
| 127329 | 127402 | sqlite3 *db = pParse->db; |
| 127330 | 127403 | int i; |
| 127331 | 127404 | if( pList==0 ){ |
| 127332 | - pList = sqlite3DbMallocZero(db, sizeof(IdList) ); | |
| 127405 | + pList = sqlite3DbMallocZero(db, SZ_IDLIST(1)); | |
| 127333 | 127406 | if( pList==0 ) return 0; |
| 127334 | 127407 | }else{ |
| 127335 | 127408 | IdList *pNew; |
| 127336 | - pNew = sqlite3DbRealloc(db, pList, | |
| 127337 | - sizeof(IdList) + pList->nId*sizeof(pList->a)); | |
| 127409 | + pNew = sqlite3DbRealloc(db, pList, SZ_IDLIST(pList->nId+1)); | |
| 127338 | 127410 | if( pNew==0 ){ |
| 127339 | 127411 | sqlite3IdListDelete(db, pList); |
| 127340 | 127412 | return 0; |
| 127341 | 127413 | } |
| 127342 | 127414 | pList = pNew; |
| @@ -127431,12 +127503,11 @@ | ||
| 127431 | 127503 | sqlite3ErrorMsg(pParse, "too many FROM clause terms, max: %d", |
| 127432 | 127504 | SQLITE_MAX_SRCLIST); |
| 127433 | 127505 | return 0; |
| 127434 | 127506 | } |
| 127435 | 127507 | if( nAlloc>SQLITE_MAX_SRCLIST ) nAlloc = SQLITE_MAX_SRCLIST; |
| 127436 | - pNew = sqlite3DbRealloc(db, pSrc, | |
| 127437 | - sizeof(*pSrc) + (nAlloc-1)*sizeof(pSrc->a[0]) ); | |
| 127508 | + pNew = sqlite3DbRealloc(db, pSrc, SZ_SRCLIST(nAlloc)); | |
| 127438 | 127509 | if( pNew==0 ){ |
| 127439 | 127510 | assert( db->mallocFailed ); |
| 127440 | 127511 | return 0; |
| 127441 | 127512 | } |
| 127442 | 127513 | pSrc = pNew; |
| @@ -127507,11 +127578,11 @@ | ||
| 127507 | 127578 | assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */ |
| 127508 | 127579 | assert( pParse!=0 ); |
| 127509 | 127580 | assert( pParse->db!=0 ); |
| 127510 | 127581 | db = pParse->db; |
| 127511 | 127582 | if( pList==0 ){ |
| 127512 | - pList = sqlite3DbMallocRawNN(pParse->db, sizeof(SrcList) ); | |
| 127583 | + pList = sqlite3DbMallocRawNN(pParse->db, SZ_SRCLIST(1)); | |
| 127513 | 127584 | if( pList==0 ) return 0; |
| 127514 | 127585 | pList->nAlloc = 1; |
| 127515 | 127586 | pList->nSrc = 1; |
| 127516 | 127587 | memset(&pList->a[0], 0, sizeof(pList->a[0])); |
| 127517 | 127588 | pList->a[0].iCursor = -1; |
| @@ -128393,14 +128464,13 @@ | ||
| 128393 | 128464 | } |
| 128394 | 128465 | } |
| 128395 | 128466 | } |
| 128396 | 128467 | |
| 128397 | 128468 | if( pWith ){ |
| 128398 | - sqlite3_int64 nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte); | |
| 128399 | - pNew = sqlite3DbRealloc(db, pWith, nByte); | |
| 128469 | + pNew = sqlite3DbRealloc(db, pWith, SZ_WITH(pWith->nCte+1)); | |
| 128400 | 128470 | }else{ |
| 128401 | - pNew = sqlite3DbMallocZero(db, sizeof(*pWith)); | |
| 128471 | + pNew = sqlite3DbMallocZero(db, SZ_WITH(1)); | |
| 128402 | 128472 | } |
| 128403 | 128473 | assert( (pNew!=0 && zName!=0) || db->mallocFailed ); |
| 128404 | 128474 | |
| 128405 | 128475 | if( db->mallocFailed ){ |
| 128406 | 128476 | sqlite3CteDelete(db, pCte); |
| @@ -131933,11 +132003,11 @@ | ||
| 131933 | 132003 | ** |
| 131934 | 132004 | ** The SUM() function follows the (broken) SQL standard which means |
| 131935 | 132005 | ** that it returns NULL if it sums over no inputs. TOTAL returns |
| 131936 | 132006 | ** 0.0 in that case. In addition, TOTAL always returns a float where |
| 131937 | 132007 | ** SUM might return an integer if it never encounters a floating point |
| 131938 | -** value. TOTAL never fails, but SUM might through an exception if | |
| 132008 | +** value. TOTAL never fails, but SUM might throw an exception if | |
| 131939 | 132009 | ** it overflows an integer. |
| 131940 | 132010 | */ |
| 131941 | 132011 | static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){ |
| 131942 | 132012 | SumCtx *p; |
| 131943 | 132013 | int type; |
| @@ -139748,52 +139818,52 @@ | ||
| 139748 | 139818 | /* 11 */ "notnull", |
| 139749 | 139819 | /* 12 */ "dflt_value", |
| 139750 | 139820 | /* 13 */ "pk", |
| 139751 | 139821 | /* 14 */ "hidden", |
| 139752 | 139822 | /* table_info reuses 8 */ |
| 139753 | - /* 15 */ "schema", /* Used by: table_list */ | |
| 139754 | - /* 16 */ "name", | |
| 139823 | + /* 15 */ "name", /* Used by: function_list */ | |
| 139824 | + /* 16 */ "builtin", | |
| 139755 | 139825 | /* 17 */ "type", |
| 139756 | - /* 18 */ "ncol", | |
| 139757 | - /* 19 */ "wr", | |
| 139758 | - /* 20 */ "strict", | |
| 139759 | - /* 21 */ "seqno", /* Used by: index_xinfo */ | |
| 139760 | - /* 22 */ "cid", | |
| 139761 | - /* 23 */ "name", | |
| 139762 | - /* 24 */ "desc", | |
| 139763 | - /* 25 */ "coll", | |
| 139764 | - /* 26 */ "key", | |
| 139765 | - /* 27 */ "name", /* Used by: function_list */ | |
| 139766 | - /* 28 */ "builtin", | |
| 139767 | - /* 29 */ "type", | |
| 139768 | - /* 30 */ "enc", | |
| 139769 | - /* 31 */ "narg", | |
| 139770 | - /* 32 */ "flags", | |
| 139771 | - /* 33 */ "tbl", /* Used by: stats */ | |
| 139772 | - /* 34 */ "idx", | |
| 139773 | - /* 35 */ "wdth", | |
| 139774 | - /* 36 */ "hght", | |
| 139775 | - /* 37 */ "flgs", | |
| 139776 | - /* 38 */ "seq", /* Used by: index_list */ | |
| 139777 | - /* 39 */ "name", | |
| 139778 | - /* 40 */ "unique", | |
| 139779 | - /* 41 */ "origin", | |
| 139780 | - /* 42 */ "partial", | |
| 139826 | + /* 18 */ "enc", | |
| 139827 | + /* 19 */ "narg", | |
| 139828 | + /* 20 */ "flags", | |
| 139829 | + /* 21 */ "schema", /* Used by: table_list */ | |
| 139830 | + /* 22 */ "name", | |
| 139831 | + /* 23 */ "type", | |
| 139832 | + /* 24 */ "ncol", | |
| 139833 | + /* 25 */ "wr", | |
| 139834 | + /* 26 */ "strict", | |
| 139835 | + /* 27 */ "seqno", /* Used by: index_xinfo */ | |
| 139836 | + /* 28 */ "cid", | |
| 139837 | + /* 29 */ "name", | |
| 139838 | + /* 30 */ "desc", | |
| 139839 | + /* 31 */ "coll", | |
| 139840 | + /* 32 */ "key", | |
| 139841 | + /* 33 */ "seq", /* Used by: index_list */ | |
| 139842 | + /* 34 */ "name", | |
| 139843 | + /* 35 */ "unique", | |
| 139844 | + /* 36 */ "origin", | |
| 139845 | + /* 37 */ "partial", | |
| 139846 | + /* 38 */ "tbl", /* Used by: stats */ | |
| 139847 | + /* 39 */ "idx", | |
| 139848 | + /* 40 */ "wdth", | |
| 139849 | + /* 41 */ "hght", | |
| 139850 | + /* 42 */ "flgs", | |
| 139781 | 139851 | /* 43 */ "table", /* Used by: foreign_key_check */ |
| 139782 | 139852 | /* 44 */ "rowid", |
| 139783 | 139853 | /* 45 */ "parent", |
| 139784 | 139854 | /* 46 */ "fkid", |
| 139785 | - /* index_info reuses 21 */ | |
| 139786 | - /* 47 */ "seq", /* Used by: database_list */ | |
| 139787 | - /* 48 */ "name", | |
| 139788 | - /* 49 */ "file", | |
| 139789 | - /* 50 */ "busy", /* Used by: wal_checkpoint */ | |
| 139790 | - /* 51 */ "log", | |
| 139791 | - /* 52 */ "checkpointed", | |
| 139792 | - /* collation_list reuses 38 */ | |
| 139855 | + /* 47 */ "busy", /* Used by: wal_checkpoint */ | |
| 139856 | + /* 48 */ "log", | |
| 139857 | + /* 49 */ "checkpointed", | |
| 139858 | + /* 50 */ "seq", /* Used by: database_list */ | |
| 139859 | + /* 51 */ "name", | |
| 139860 | + /* 52 */ "file", | |
| 139861 | + /* index_info reuses 27 */ | |
| 139793 | 139862 | /* 53 */ "database", /* Used by: lock_status */ |
| 139794 | 139863 | /* 54 */ "status", |
| 139864 | + /* collation_list reuses 33 */ | |
| 139795 | 139865 | /* 55 */ "cache_size", /* Used by: default_cache_size */ |
| 139796 | 139866 | /* module_list pragma_list reuses 9 */ |
| 139797 | 139867 | /* 56 */ "timeout", /* Used by: busy_timeout */ |
| 139798 | 139868 | }; |
| 139799 | 139869 | |
| @@ -139882,11 +139952,11 @@ | ||
| 139882 | 139952 | #endif |
| 139883 | 139953 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 139884 | 139954 | {/* zName: */ "collation_list", |
| 139885 | 139955 | /* ePragTyp: */ PragTyp_COLLATION_LIST, |
| 139886 | 139956 | /* ePragFlg: */ PragFlg_Result0, |
| 139887 | - /* ColNames: */ 38, 2, | |
| 139957 | + /* ColNames: */ 33, 2, | |
| 139888 | 139958 | /* iArg: */ 0 }, |
| 139889 | 139959 | #endif |
| 139890 | 139960 | #if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS) |
| 139891 | 139961 | {/* zName: */ "compile_options", |
| 139892 | 139962 | /* ePragTyp: */ PragTyp_COMPILE_OPTIONS, |
| @@ -139917,11 +139987,11 @@ | ||
| 139917 | 139987 | #endif |
| 139918 | 139988 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 139919 | 139989 | {/* zName: */ "database_list", |
| 139920 | 139990 | /* ePragTyp: */ PragTyp_DATABASE_LIST, |
| 139921 | 139991 | /* ePragFlg: */ PragFlg_Result0, |
| 139922 | - /* ColNames: */ 47, 3, | |
| 139992 | + /* ColNames: */ 50, 3, | |
| 139923 | 139993 | /* iArg: */ 0 }, |
| 139924 | 139994 | #endif |
| 139925 | 139995 | #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED) |
| 139926 | 139996 | {/* zName: */ "default_cache_size", |
| 139927 | 139997 | /* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE, |
| @@ -139997,11 +140067,11 @@ | ||
| 139997 | 140067 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 139998 | 140068 | #if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS) |
| 139999 | 140069 | {/* zName: */ "function_list", |
| 140000 | 140070 | /* ePragTyp: */ PragTyp_FUNCTION_LIST, |
| 140001 | 140071 | /* ePragFlg: */ PragFlg_Result0, |
| 140002 | - /* ColNames: */ 27, 6, | |
| 140072 | + /* ColNames: */ 15, 6, | |
| 140003 | 140073 | /* iArg: */ 0 }, |
| 140004 | 140074 | #endif |
| 140005 | 140075 | #endif |
| 140006 | 140076 | {/* zName: */ "hard_heap_limit", |
| 140007 | 140077 | /* ePragTyp: */ PragTyp_HARD_HEAP_LIMIT, |
| @@ -140026,21 +140096,21 @@ | ||
| 140026 | 140096 | #endif |
| 140027 | 140097 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 140028 | 140098 | {/* zName: */ "index_info", |
| 140029 | 140099 | /* ePragTyp: */ PragTyp_INDEX_INFO, |
| 140030 | 140100 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 140031 | - /* ColNames: */ 21, 3, | |
| 140101 | + /* ColNames: */ 27, 3, | |
| 140032 | 140102 | /* iArg: */ 0 }, |
| 140033 | 140103 | {/* zName: */ "index_list", |
| 140034 | 140104 | /* ePragTyp: */ PragTyp_INDEX_LIST, |
| 140035 | 140105 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 140036 | - /* ColNames: */ 38, 5, | |
| 140106 | + /* ColNames: */ 33, 5, | |
| 140037 | 140107 | /* iArg: */ 0 }, |
| 140038 | 140108 | {/* zName: */ "index_xinfo", |
| 140039 | 140109 | /* ePragTyp: */ PragTyp_INDEX_INFO, |
| 140040 | 140110 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 140041 | - /* ColNames: */ 21, 6, | |
| 140111 | + /* ColNames: */ 27, 6, | |
| 140042 | 140112 | /* iArg: */ 1 }, |
| 140043 | 140113 | #endif |
| 140044 | 140114 | #if !defined(SQLITE_OMIT_INTEGRITY_CHECK) |
| 140045 | 140115 | {/* zName: */ "integrity_check", |
| 140046 | 140116 | /* ePragTyp: */ PragTyp_INTEGRITY_CHECK, |
| @@ -140215,11 +140285,11 @@ | ||
| 140215 | 140285 | #endif |
| 140216 | 140286 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && defined(SQLITE_DEBUG) |
| 140217 | 140287 | {/* zName: */ "stats", |
| 140218 | 140288 | /* ePragTyp: */ PragTyp_STATS, |
| 140219 | 140289 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq, |
| 140220 | - /* ColNames: */ 33, 5, | |
| 140290 | + /* ColNames: */ 38, 5, | |
| 140221 | 140291 | /* iArg: */ 0 }, |
| 140222 | 140292 | #endif |
| 140223 | 140293 | #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) |
| 140224 | 140294 | {/* zName: */ "synchronous", |
| 140225 | 140295 | /* ePragTyp: */ PragTyp_SYNCHRONOUS, |
| @@ -140234,11 +140304,11 @@ | ||
| 140234 | 140304 | /* ColNames: */ 8, 6, |
| 140235 | 140305 | /* iArg: */ 0 }, |
| 140236 | 140306 | {/* zName: */ "table_list", |
| 140237 | 140307 | /* ePragTyp: */ PragTyp_TABLE_LIST, |
| 140238 | 140308 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1, |
| 140239 | - /* ColNames: */ 15, 6, | |
| 140309 | + /* ColNames: */ 21, 6, | |
| 140240 | 140310 | /* iArg: */ 0 }, |
| 140241 | 140311 | {/* zName: */ "table_xinfo", |
| 140242 | 140312 | /* ePragTyp: */ PragTyp_TABLE_INFO, |
| 140243 | 140313 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 140244 | 140314 | /* ColNames: */ 8, 7, |
| @@ -140311,11 +140381,11 @@ | ||
| 140311 | 140381 | /* ColNames: */ 0, 0, |
| 140312 | 140382 | /* iArg: */ 0 }, |
| 140313 | 140383 | {/* zName: */ "wal_checkpoint", |
| 140314 | 140384 | /* ePragTyp: */ PragTyp_WAL_CHECKPOINT, |
| 140315 | 140385 | /* ePragFlg: */ PragFlg_NeedSchema, |
| 140316 | - /* ColNames: */ 50, 3, | |
| 140386 | + /* ColNames: */ 47, 3, | |
| 140317 | 140387 | /* iArg: */ 0 }, |
| 140318 | 140388 | #endif |
| 140319 | 140389 | #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) |
| 140320 | 140390 | {/* zName: */ "writable_schema", |
| 140321 | 140391 | /* ePragTyp: */ PragTyp_FLAG, |
| @@ -140333,11 +140403,11 @@ | ||
| 140333 | 140403 | ** When the 0x10 bit of PRAGMA optimize is set, any ANALYZE commands |
| 140334 | 140404 | ** will be run with an analysis_limit set to the lessor of the value of |
| 140335 | 140405 | ** the following macro or to the actual analysis_limit if it is non-zero, |
| 140336 | 140406 | ** in order to prevent PRAGMA optimize from running for too long. |
| 140337 | 140407 | ** |
| 140338 | -** The value of 2000 is chosen emperically so that the worst-case run-time | |
| 140408 | +** The value of 2000 is chosen empirically so that the worst-case run-time | |
| 140339 | 140409 | ** for PRAGMA optimize does not exceed 100 milliseconds against a variety |
| 140340 | 140410 | ** of test databases on a RaspberryPI-4 compiled using -Os and without |
| 140341 | 140411 | ** -DSQLITE_DEBUG. Of course, your mileage may vary. For the purpose of |
| 140342 | 140412 | ** this paragraph, "worst-case" means that ANALYZE ends up being |
| 140343 | 140413 | ** run on every table in the database. The worst case typically only |
| @@ -144622,11 +144692,11 @@ | ||
| 144622 | 144692 | pNew->iOffset = 0; |
| 144623 | 144693 | pNew->selId = ++pParse->nSelect; |
| 144624 | 144694 | pNew->addrOpenEphm[0] = -1; |
| 144625 | 144695 | pNew->addrOpenEphm[1] = -1; |
| 144626 | 144696 | pNew->nSelectRow = 0; |
| 144627 | - if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*pSrc)); | |
| 144697 | + if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1); | |
| 144628 | 144698 | pNew->pSrc = pSrc; |
| 144629 | 144699 | pNew->pWhere = pWhere; |
| 144630 | 144700 | pNew->pGroupBy = pGroupBy; |
| 144631 | 144701 | pNew->pHaving = pHaving; |
| 144632 | 144702 | pNew->pOrderBy = pOrderBy; |
| @@ -146005,20 +146075,20 @@ | ||
| 146005 | 146075 | /* |
| 146006 | 146076 | ** Allocate a KeyInfo object sufficient for an index of N key columns and |
| 146007 | 146077 | ** X extra columns. |
| 146008 | 146078 | */ |
| 146009 | 146079 | SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){ |
| 146010 | - int nExtra = (N+X)*(sizeof(CollSeq*)+1) - sizeof(CollSeq*); | |
| 146011 | - KeyInfo *p = sqlite3DbMallocRawNN(db, sizeof(KeyInfo) + nExtra); | |
| 146080 | + int nExtra = (N+X)*(sizeof(CollSeq*)+1); | |
| 146081 | + KeyInfo *p = sqlite3DbMallocRawNN(db, SZ_KEYINFO(0) + nExtra); | |
| 146012 | 146082 | if( p ){ |
| 146013 | 146083 | p->aSortFlags = (u8*)&p->aColl[N+X]; |
| 146014 | 146084 | p->nKeyField = (u16)N; |
| 146015 | 146085 | p->nAllField = (u16)(N+X); |
| 146016 | 146086 | p->enc = ENC(db); |
| 146017 | 146087 | p->db = db; |
| 146018 | 146088 | p->nRef = 1; |
| 146019 | - memset(&p[1], 0, nExtra); | |
| 146089 | + memset(p->aColl, 0, nExtra); | |
| 146020 | 146090 | }else{ |
| 146021 | 146091 | return (KeyInfo*)sqlite3OomFault(db); |
| 146022 | 146092 | } |
| 146023 | 146093 | return p; |
| 146024 | 146094 | } |
| @@ -150530,11 +150600,11 @@ | ||
| 150530 | 150600 | } |
| 150531 | 150601 | pTabList = p->pSrc; |
| 150532 | 150602 | pEList = p->pEList; |
| 150533 | 150603 | if( pParse->pWith && (p->selFlags & SF_View) ){ |
| 150534 | 150604 | if( p->pWith==0 ){ |
| 150535 | - p->pWith = (With*)sqlite3DbMallocZero(db, sizeof(With)); | |
| 150605 | + p->pWith = (With*)sqlite3DbMallocZero(db, SZ_WITH(1) ); | |
| 150536 | 150606 | if( p->pWith==0 ){ |
| 150537 | 150607 | return WRC_Abort; |
| 150538 | 150608 | } |
| 150539 | 150609 | } |
| 150540 | 150610 | p->pWith->bView = 1; |
| @@ -151669,10 +151739,11 @@ | ||
| 151669 | 151739 | ** * The subquery is a UNION ALL of two or more terms |
| 151670 | 151740 | ** * The subquery does not have a LIMIT clause |
| 151671 | 151741 | ** * There is no WHERE or GROUP BY or HAVING clauses on the subqueries |
| 151672 | 151742 | ** * The outer query is a simple count(*) with no WHERE clause or other |
| 151673 | 151743 | ** extraneous syntax. |
| 151744 | +** * None of the subqueries are DISTINCT (forumpost/a860f5fb2e 2025-03-10) | |
| 151674 | 151745 | ** |
| 151675 | 151746 | ** Return TRUE if the optimization is undertaken. |
| 151676 | 151747 | */ |
| 151677 | 151748 | static int countOfViewOptimization(Parse *pParse, Select *p){ |
| 151678 | 151749 | Select *pSub, *pPrior; |
| @@ -151701,11 +151772,15 @@ | ||
| 151701 | 151772 | if( pSub->selFlags & SF_CopyCte ) return 0; /* Not a CTE */ |
| 151702 | 151773 | do{ |
| 151703 | 151774 | if( pSub->op!=TK_ALL && pSub->pPrior ) return 0; /* Must be UNION ALL */ |
| 151704 | 151775 | if( pSub->pWhere ) return 0; /* No WHERE clause */ |
| 151705 | 151776 | if( pSub->pLimit ) return 0; /* No LIMIT clause */ |
| 151706 | - if( pSub->selFlags & SF_Aggregate ) return 0; /* Not an aggregate */ | |
| 151777 | + if( pSub->selFlags & (SF_Aggregate|SF_Distinct) ){ | |
| 151778 | + testcase( pSub->selFlags & SF_Aggregate ); | |
| 151779 | + testcase( pSub->selFlags & SF_Distinct ); | |
| 151780 | + return 0; /* Not an aggregate nor DISTINCT */ | |
| 151781 | + } | |
| 151707 | 151782 | assert( pSub->pHaving==0 ); /* Due to the previous */ |
| 151708 | 151783 | pSub = pSub->pPrior; /* Repeat over compound */ |
| 151709 | 151784 | }while( pSub ); |
| 151710 | 151785 | |
| 151711 | 151786 | /* If we reach this point then it is OK to perform the transformation */ |
| @@ -151713,11 +151788,11 @@ | ||
| 151713 | 151788 | db = pParse->db; |
| 151714 | 151789 | pCount = pExpr; |
| 151715 | 151790 | pExpr = 0; |
| 151716 | 151791 | pSub = sqlite3SubqueryDetach(db, pFrom); |
| 151717 | 151792 | sqlite3SrcListDelete(db, p->pSrc); |
| 151718 | - p->pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*p->pSrc)); | |
| 151793 | + p->pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1); | |
| 151719 | 151794 | while( pSub ){ |
| 151720 | 151795 | Expr *pTerm; |
| 151721 | 151796 | pPrior = pSub->pPrior; |
| 151722 | 151797 | pSub->pPrior = 0; |
| 151723 | 151798 | pSub->pNext = 0; |
| @@ -154504,11 +154579,12 @@ | ||
| 154504 | 154579 | Vdbe *v = pParse->pVdbe; |
| 154505 | 154580 | sqlite3 *db = pParse->db; |
| 154506 | 154581 | ExprList *pNew; |
| 154507 | 154582 | Returning *pReturning; |
| 154508 | 154583 | Select sSelect; |
| 154509 | - SrcList sFrom; | |
| 154584 | + SrcList *pFrom; | |
| 154585 | + u8 fromSpace[SZ_SRCLIST_1]; | |
| 154510 | 154586 | |
| 154511 | 154587 | assert( v!=0 ); |
| 154512 | 154588 | if( !pParse->bReturning ){ |
| 154513 | 154589 | /* This RETURNING trigger must be for a different statement as |
| 154514 | 154590 | ** this statement lacks a RETURNING clause. */ |
| @@ -154520,17 +154596,18 @@ | ||
| 154520 | 154596 | if( pTrigger != &(pReturning->retTrig) ){ |
| 154521 | 154597 | /* This RETURNING trigger is for a different statement */ |
| 154522 | 154598 | return; |
| 154523 | 154599 | } |
| 154524 | 154600 | memset(&sSelect, 0, sizeof(sSelect)); |
| 154525 | - memset(&sFrom, 0, sizeof(sFrom)); | |
| 154601 | + pFrom = (SrcList*)fromSpace; | |
| 154602 | + memset(pFrom, 0, SZ_SRCLIST_1); | |
| 154526 | 154603 | sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0); |
| 154527 | - sSelect.pSrc = &sFrom; | |
| 154528 | - sFrom.nSrc = 1; | |
| 154529 | - sFrom.a[0].pSTab = pTab; | |
| 154530 | - sFrom.a[0].zName = pTab->zName; /* tag-20240424-1 */ | |
| 154531 | - sFrom.a[0].iCursor = -1; | |
| 154604 | + sSelect.pSrc = pFrom; | |
| 154605 | + pFrom->nSrc = 1; | |
| 154606 | + pFrom->a[0].pSTab = pTab; | |
| 154607 | + pFrom->a[0].zName = pTab->zName; /* tag-20240424-1 */ | |
| 154608 | + pFrom->a[0].iCursor = -1; | |
| 154532 | 154609 | sqlite3SelectPrep(pParse, &sSelect, 0); |
| 154533 | 154610 | if( pParse->nErr==0 ){ |
| 154534 | 154611 | assert( db->mallocFailed==0 ); |
| 154535 | 154612 | sqlite3GenerateColumnNames(pParse, &sSelect); |
| 154536 | 154613 | } |
| @@ -156927,11 +157004,11 @@ | ||
| 156927 | 157004 | saved_flags = db->flags; |
| 156928 | 157005 | saved_mDbFlags = db->mDbFlags; |
| 156929 | 157006 | saved_nChange = db->nChange; |
| 156930 | 157007 | saved_nTotalChange = db->nTotalChange; |
| 156931 | 157008 | saved_mTrace = db->mTrace; |
| 156932 | - db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks; | |
| 157009 | + db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_Comments; | |
| 156933 | 157010 | db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum; |
| 156934 | 157011 | db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder |
| 156935 | 157012 | | SQLITE_Defensive | SQLITE_CountRows); |
| 156936 | 157013 | db->mTrace = 0; |
| 156937 | 157014 | |
| @@ -159056,13 +159133,18 @@ | ||
| 159056 | 159133 | WhereLoop *pLoops; /* List of all WhereLoop objects */ |
| 159057 | 159134 | WhereMemBlock *pMemToFree;/* Memory to free when this object destroyed */ |
| 159058 | 159135 | Bitmask revMask; /* Mask of ORDER BY terms that need reversing */ |
| 159059 | 159136 | WhereClause sWC; /* Decomposition of the WHERE clause */ |
| 159060 | 159137 | WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */ |
| 159061 | - WhereLevel a[1]; /* Information about each nest loop in WHERE */ | |
| 159138 | + WhereLevel a[FLEXARRAY]; /* Information about each nest loop in WHERE */ | |
| 159062 | 159139 | }; |
| 159063 | 159140 | |
| 159141 | +/* | |
| 159142 | +** The size (in bytes) of a WhereInfo object that holds N WhereLevels. | |
| 159143 | +*/ | |
| 159144 | +#define SZ_WHEREINFO(N) ROUND8(offsetof(WhereInfo,a)+(N)*sizeof(WhereLevel)) | |
| 159145 | + | |
| 159064 | 159146 | /* |
| 159065 | 159147 | ** Private interfaces - callable only by other where.c routines. |
| 159066 | 159148 | ** |
| 159067 | 159149 | ** where.c: |
| 159068 | 159150 | */ |
| @@ -161509,12 +161591,11 @@ | ||
| 161509 | 161591 | */ |
| 161510 | 161592 | if( pWInfo->nLevel>1 ){ |
| 161511 | 161593 | int nNotReady; /* The number of notReady tables */ |
| 161512 | 161594 | SrcItem *origSrc; /* Original list of tables */ |
| 161513 | 161595 | nNotReady = pWInfo->nLevel - iLevel - 1; |
| 161514 | - pOrTab = sqlite3DbMallocRawNN(db, | |
| 161515 | - sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0])); | |
| 161596 | + pOrTab = sqlite3DbMallocRawNN(db, SZ_SRCLIST(nNotReady+1)); | |
| 161516 | 161597 | if( pOrTab==0 ) return notReady; |
| 161517 | 161598 | pOrTab->nAlloc = (u8)(nNotReady + 1); |
| 161518 | 161599 | pOrTab->nSrc = pOrTab->nAlloc; |
| 161519 | 161600 | memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem)); |
| 161520 | 161601 | origSrc = pWInfo->pTabList->a; |
| @@ -162053,11 +162134,12 @@ | ||
| 162053 | 162134 | Expr *pSubWhere = 0; |
| 162054 | 162135 | WhereClause *pWC = &pWInfo->sWC; |
| 162055 | 162136 | WhereInfo *pSubWInfo; |
| 162056 | 162137 | WhereLoop *pLoop = pLevel->pWLoop; |
| 162057 | 162138 | SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; |
| 162058 | - SrcList sFrom; | |
| 162139 | + SrcList *pFrom; | |
| 162140 | + u8 fromSpace[SZ_SRCLIST_1]; | |
| 162059 | 162141 | Bitmask mAll = 0; |
| 162060 | 162142 | int k; |
| 162061 | 162143 | |
| 162062 | 162144 | ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pSTab->zName)); |
| 162063 | 162145 | sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn, |
| @@ -162097,17 +162179,18 @@ | ||
| 162097 | 162179 | if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue; |
| 162098 | 162180 | pSubWhere = sqlite3ExprAnd(pParse, pSubWhere, |
| 162099 | 162181 | sqlite3ExprDup(pParse->db, pTerm->pExpr, 0)); |
| 162100 | 162182 | } |
| 162101 | 162183 | } |
| 162102 | - sFrom.nSrc = 1; | |
| 162103 | - sFrom.nAlloc = 1; | |
| 162104 | - memcpy(&sFrom.a[0], pTabItem, sizeof(SrcItem)); | |
| 162105 | - sFrom.a[0].fg.jointype = 0; | |
| 162184 | + pFrom = (SrcList*)fromSpace; | |
| 162185 | + pFrom->nSrc = 1; | |
| 162186 | + pFrom->nAlloc = 1; | |
| 162187 | + memcpy(&pFrom->a[0], pTabItem, sizeof(SrcItem)); | |
| 162188 | + pFrom->a[0].fg.jointype = 0; | |
| 162106 | 162189 | assert( pParse->withinRJSubrtn < 100 ); |
| 162107 | 162190 | pParse->withinRJSubrtn++; |
| 162108 | - pSubWInfo = sqlite3WhereBegin(pParse, &sFrom, pSubWhere, 0, 0, 0, | |
| 162191 | + pSubWInfo = sqlite3WhereBegin(pParse, pFrom, pSubWhere, 0, 0, 0, | |
| 162109 | 162192 | WHERE_RIGHT_JOIN, 0); |
| 162110 | 162193 | if( pSubWInfo ){ |
| 162111 | 162194 | int iCur = pLevel->iTabCur; |
| 162112 | 162195 | int r = ++pParse->nMem; |
| 162113 | 162196 | int nPk; |
| @@ -164091,15 +164174,20 @@ | ||
| 164091 | 164174 | WhereClause *pWC; /* The Where clause being analyzed */ |
| 164092 | 164175 | Parse *pParse; /* The parsing context */ |
| 164093 | 164176 | int eDistinct; /* Value to return from sqlite3_vtab_distinct() */ |
| 164094 | 164177 | u32 mIn; /* Mask of terms that are <col> IN (...) */ |
| 164095 | 164178 | u32 mHandleIn; /* Terms that vtab will handle as <col> IN (...) */ |
| 164096 | - sqlite3_value *aRhs[1]; /* RHS values for constraints. MUST BE LAST | |
| 164097 | - ** because extra space is allocated to hold up | |
| 164098 | - ** to nTerm such values */ | |
| 164179 | + sqlite3_value *aRhs[FLEXARRAY]; /* RHS values for constraints. MUST BE LAST | |
| 164180 | + ** Extra space is allocated to hold up | |
| 164181 | + ** to nTerm such values */ | |
| 164099 | 164182 | }; |
| 164100 | 164183 | |
| 164184 | +/* Size (in bytes) of a HiddenIndeInfo object sufficient to hold as | |
| 164185 | +** many as N constraints */ | |
| 164186 | +#define SZ_HIDDENINDEXINFO(N) \ | |
| 164187 | + (offsetof(HiddenIndexInfo,aRhs) + (N)*sizeof(sqlite3_value*)) | |
| 164188 | + | |
| 164101 | 164189 | /* Forward declaration of methods */ |
| 164102 | 164190 | static int whereLoopResize(sqlite3*, WhereLoop*, int); |
| 164103 | 164191 | |
| 164104 | 164192 | /* |
| 164105 | 164193 | ** Return the estimated number of output rows from a WHERE clause |
| @@ -165573,12 +165661,12 @@ | ||
| 165573 | 165661 | |
| 165574 | 165662 | /* Allocate the sqlite3_index_info structure |
| 165575 | 165663 | */ |
| 165576 | 165664 | pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo) |
| 165577 | 165665 | + (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm |
| 165578 | - + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden) | |
| 165579 | - + sizeof(sqlite3_value*)*nTerm ); | |
| 165666 | + + sizeof(*pIdxOrderBy)*nOrderBy | |
| 165667 | + + SZ_HIDDENINDEXINFO(nTerm) ); | |
| 165580 | 165668 | if( pIdxInfo==0 ){ |
| 165581 | 165669 | sqlite3ErrorMsg(pParse, "out of memory"); |
| 165582 | 165670 | return 0; |
| 165583 | 165671 | } |
| 165584 | 165672 | pHidden = (struct HiddenIndexInfo*)&pIdxInfo[1]; |
| @@ -170768,14 +170856,11 @@ | ||
| 170768 | 170856 | ** struct, the contents of WhereInfo.a[], the WhereClause structure |
| 170769 | 170857 | ** and the WhereMaskSet structure. Since WhereClause contains an 8-byte |
| 170770 | 170858 | ** field (type Bitmask) it must be aligned on an 8-byte boundary on |
| 170771 | 170859 | ** some architectures. Hence the ROUND8() below. |
| 170772 | 170860 | */ |
| 170773 | - nByteWInfo = ROUND8P(sizeof(WhereInfo)); | |
| 170774 | - if( nTabList>1 ){ | |
| 170775 | - nByteWInfo = ROUND8P(nByteWInfo + (nTabList-1)*sizeof(WhereLevel)); | |
| 170776 | - } | |
| 170861 | + nByteWInfo = SZ_WHEREINFO(nTabList); | |
| 170777 | 170862 | pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop)); |
| 170778 | 170863 | if( db->mallocFailed ){ |
| 170779 | 170864 | sqlite3DbFree(db, pWInfo); |
| 170780 | 170865 | pWInfo = 0; |
| 170781 | 170866 | goto whereBeginError; |
| @@ -181607,11 +181692,15 @@ | ||
| 181607 | 181692 | tokenType = analyzeOverKeyword((const u8*)&zSql[4], lastTokenParsed); |
| 181608 | 181693 | }else if( tokenType==TK_FILTER ){ |
| 181609 | 181694 | assert( n==6 ); |
| 181610 | 181695 | tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed); |
| 181611 | 181696 | #endif /* SQLITE_OMIT_WINDOWFUNC */ |
| 181612 | - }else if( tokenType==TK_COMMENT && (db->flags & SQLITE_Comments)!=0 ){ | |
| 181697 | + }else if( tokenType==TK_COMMENT | |
| 181698 | + && (db->init.busy || (db->flags & SQLITE_Comments)!=0) | |
| 181699 | + ){ | |
| 181700 | + /* Ignore SQL comments if either (1) we are reparsing the schema or | |
| 181701 | + ** (2) SQLITE_DBCONFIG_ENABLE_COMMENTS is turned on (the default). */ | |
| 181613 | 181702 | zSql += n; |
| 181614 | 181703 | continue; |
| 181615 | 181704 | }else if( tokenType!=TK_QNUMBER ){ |
| 181616 | 181705 | Token x; |
| 181617 | 181706 | x.z = zSql; |
| @@ -182502,10 +182591,18 @@ | ||
| 182502 | 182591 | } |
| 182503 | 182592 | #endif |
| 182504 | 182593 | if( rc==SQLITE_OK ){ |
| 182505 | 182594 | sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage, |
| 182506 | 182595 | sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage); |
| 182596 | +#ifdef SQLITE_EXTRA_INIT_MUTEXED | |
| 182597 | + { | |
| 182598 | + int SQLITE_EXTRA_INIT_MUTEXED(const char*); | |
| 182599 | + rc = SQLITE_EXTRA_INIT_MUTEXED(0); | |
| 182600 | + } | |
| 182601 | +#endif | |
| 182602 | + } | |
| 182603 | + if( rc==SQLITE_OK ){ | |
| 182507 | 182604 | sqlite3MemoryBarrier(); |
| 182508 | 182605 | sqlite3GlobalConfig.isInit = 1; |
| 182509 | 182606 | #ifdef SQLITE_EXTRA_INIT |
| 182510 | 182607 | bRunExtraInit = 1; |
| 182511 | 182608 | #endif |
| @@ -184063,10 +184160,14 @@ | ||
| 184063 | 184160 | sqlite3_file *fd = sqlite3PagerFile(sqlite3BtreePager(pBt)); |
| 184064 | 184161 | sqlite3OsFileControlHint(fd, SQLITE_FCNTL_BLOCK_ON_CONNECT, (void*)&bBOC); |
| 184065 | 184162 | } |
| 184066 | 184163 | } |
| 184067 | 184164 | sqlite3BtreeLeaveAll(db); |
| 184165 | +#endif | |
| 184166 | +#if !defined(SQLITE_ENABLE_API_ARMOR) && !defined(SQLITE_ENABLE_SETLK_TIMEOUT) | |
| 184167 | + UNUSED_PARAMETER(db); | |
| 184168 | + UNUSED_PARAMETER(flags); | |
| 184068 | 184169 | #endif |
| 184069 | 184170 | return SQLITE_OK; |
| 184070 | 184171 | } |
| 184071 | 184172 | |
| 184072 | 184173 | /* |
| @@ -186032,11 +186133,11 @@ | ||
| 186032 | 186133 | }else if( pData==0 ){ |
| 186033 | 186134 | sqlite3_mutex_leave(db->mutex); |
| 186034 | 186135 | return SQLITE_OK; |
| 186035 | 186136 | }else{ |
| 186036 | 186137 | size_t n = strlen(zName); |
| 186037 | - p = sqlite3_malloc64( sizeof(DbClientData)+n+1 ); | |
| 186138 | + p = sqlite3_malloc64( SZ_DBCLIENTDATA(n+1) ); | |
| 186038 | 186139 | if( p==0 ){ |
| 186039 | 186140 | if( xDestructor ) xDestructor(pData); |
| 186040 | 186141 | sqlite3_mutex_leave(db->mutex); |
| 186041 | 186142 | return SQLITE_NOMEM; |
| 186042 | 186143 | } |
| @@ -186398,12 +186499,12 @@ | ||
| 186398 | 186499 | #endif |
| 186399 | 186500 | |
| 186400 | 186501 | /* sqlite3_test_control(SQLITE_TESTCTRL_FK_NO_ACTION, sqlite3 *db, int b); |
| 186401 | 186502 | ** |
| 186402 | 186503 | ** If b is true, then activate the SQLITE_FkNoAction setting. If b is |
| 186403 | - ** false then clearn that setting. If the SQLITE_FkNoAction setting is | |
| 186404 | - ** abled, all foreign key ON DELETE and ON UPDATE actions behave as if | |
| 186504 | + ** false then clear that setting. If the SQLITE_FkNoAction setting is | |
| 186505 | + ** enabled, all foreign key ON DELETE and ON UPDATE actions behave as if | |
| 186405 | 186506 | ** they were NO ACTION, regardless of how they are defined. |
| 186406 | 186507 | ** |
| 186407 | 186508 | ** NB: One must usually run "PRAGMA writable_schema=RESET" after |
| 186408 | 186509 | ** using this test-control, before it will take full effect. failing |
| 186409 | 186510 | ** to reset the schema can result in some unexpected behavior. |
| @@ -187746,11 +187847,11 @@ | ||
| 187746 | 187847 | ** } |
| 187747 | 187848 | ** |
| 187748 | 187849 | ** Here, array { X } means zero or more occurrences of X, adjacent in |
| 187749 | 187850 | ** memory. A "position" is an index of a token in the token stream |
| 187750 | 187851 | ** generated by the tokenizer. Note that POS_END and POS_COLUMN occur |
| 187751 | -** in the same logical place as the position element, and act as sentinals | |
| 187852 | +** in the same logical place as the position element, and act as sentinels | |
| 187752 | 187853 | ** ending a position list array. POS_END is 0. POS_COLUMN is 1. |
| 187753 | 187854 | ** The positions numbers are not stored literally but rather as two more |
| 187754 | 187855 | ** than the difference from the prior position, or the just the position plus |
| 187755 | 187856 | ** 2 for the first position. Example: |
| 187756 | 187857 | ** |
| @@ -188433,10 +188534,23 @@ | ||
| 188433 | 188534 | |
| 188434 | 188535 | #define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) |
| 188435 | 188536 | #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) |
| 188436 | 188537 | |
| 188437 | 188538 | #define deliberate_fall_through |
| 188539 | + | |
| 188540 | +/* | |
| 188541 | +** Macros needed to provide flexible arrays in a portable way | |
| 188542 | +*/ | |
| 188543 | +#ifndef offsetof | |
| 188544 | +# define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) | |
| 188545 | +#endif | |
| 188546 | +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) | |
| 188547 | +# define FLEXARRAY | |
| 188548 | +#else | |
| 188549 | +# define FLEXARRAY 1 | |
| 188550 | +#endif | |
| 188551 | + | |
| 188438 | 188552 | |
| 188439 | 188553 | #endif /* SQLITE_AMALGAMATION */ |
| 188440 | 188554 | |
| 188441 | 188555 | #ifdef SQLITE_DEBUG |
| 188442 | 188556 | SQLITE_PRIVATE int sqlite3Fts3Corrupt(void); |
| @@ -188538,11 +188652,11 @@ | ||
| 188538 | 188652 | int inTransaction; /* True after xBegin but before xCommit/xRollback */ |
| 188539 | 188653 | int mxSavepoint; /* Largest valid xSavepoint integer */ |
| 188540 | 188654 | #endif |
| 188541 | 188655 | |
| 188542 | 188656 | #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) |
| 188543 | - /* True to disable the incremental doclist optimization. This is controled | |
| 188657 | + /* True to disable the incremental doclist optimization. This is controlled | |
| 188544 | 188658 | ** by special insert command 'test-no-incr-doclist'. */ |
| 188545 | 188659 | int bNoIncrDoclist; |
| 188546 | 188660 | |
| 188547 | 188661 | /* Number of segments in a level */ |
| 188548 | 188662 | int nMergeCount; |
| @@ -188590,11 +188704,11 @@ | ||
| 188590 | 188704 | #define FTS3_EVAL_NEXT 1 |
| 188591 | 188705 | #define FTS3_EVAL_MATCHINFO 2 |
| 188592 | 188706 | |
| 188593 | 188707 | /* |
| 188594 | 188708 | ** The Fts3Cursor.eSearch member is always set to one of the following. |
| 188595 | -** Actualy, Fts3Cursor.eSearch can be greater than or equal to | |
| 188709 | +** Actually, Fts3Cursor.eSearch can be greater than or equal to | |
| 188596 | 188710 | ** FTS3_FULLTEXT_SEARCH. If so, then Fts3Cursor.eSearch - 2 is the index |
| 188597 | 188711 | ** of the column to be searched. For example, in |
| 188598 | 188712 | ** |
| 188599 | 188713 | ** CREATE VIRTUAL TABLE ex1 USING fts3(a,b,c,d); |
| 188600 | 188714 | ** SELECT docid FROM ex1 WHERE b MATCH 'one two three'; |
| @@ -188663,12 +188777,16 @@ | ||
| 188663 | 188777 | /* Variables below this point are populated by fts3_expr.c when parsing |
| 188664 | 188778 | ** a MATCH expression. Everything above is part of the evaluation phase. |
| 188665 | 188779 | */ |
| 188666 | 188780 | int nToken; /* Number of tokens in the phrase */ |
| 188667 | 188781 | int iColumn; /* Index of column this phrase must match */ |
| 188668 | - Fts3PhraseToken aToken[1]; /* One entry for each token in the phrase */ | |
| 188782 | + Fts3PhraseToken aToken[FLEXARRAY]; /* One for each token in the phrase */ | |
| 188669 | 188783 | }; |
| 188784 | + | |
| 188785 | +/* Size (in bytes) of an Fts3Phrase object large enough to hold N tokens */ | |
| 188786 | +#define SZ_FTS3PHRASE(N) \ | |
| 188787 | + (offsetof(Fts3Phrase,aToken)+(N)*sizeof(Fts3PhraseToken)) | |
| 188670 | 188788 | |
| 188671 | 188789 | /* |
| 188672 | 188790 | ** A tree of these objects forms the RHS of a MATCH operator. |
| 188673 | 188791 | ** |
| 188674 | 188792 | ** If Fts3Expr.eType is FTSQUERY_PHRASE and isLoaded is true, then aDoclist |
| @@ -191243,11 +191361,11 @@ | ||
| 191243 | 191361 | ** |
| 191244 | 191362 | ** The space required to store the output is therefore the sum of the |
| 191245 | 191363 | ** sizes of the two inputs, plus enough space for exactly one of the input |
| 191246 | 191364 | ** docids to grow. |
| 191247 | 191365 | ** |
| 191248 | - ** A symetric argument may be made if the doclists are in descending | |
| 191366 | + ** A symmetric argument may be made if the doclists are in descending | |
| 191249 | 191367 | ** order. |
| 191250 | 191368 | */ |
| 191251 | 191369 | aOut = sqlite3_malloc64((i64)n1+n2+FTS3_VARINT_MAX-1+FTS3_BUFFER_PADDING); |
| 191252 | 191370 | if( !aOut ) return SQLITE_NOMEM; |
| 191253 | 191371 | |
| @@ -193341,11 +193459,11 @@ | ||
| 193341 | 193459 | ** |
| 193342 | 193460 | ** * features at least one token that uses an incremental doclist, and |
| 193343 | 193461 | ** |
| 193344 | 193462 | ** * does not contain any deferred tokens. |
| 193345 | 193463 | ** |
| 193346 | -** Advance it to the next matching documnent in the database and populate | |
| 193464 | +** Advance it to the next matching document in the database and populate | |
| 193347 | 193465 | ** the Fts3Doclist.pList and nList fields. |
| 193348 | 193466 | ** |
| 193349 | 193467 | ** If there is no "next" entry and no error occurs, then *pbEof is set to |
| 193350 | 193468 | ** 1 before returning. Otherwise, if no error occurs and the iterator is |
| 193351 | 193469 | ** successfully advanced, *pbEof is set to 0. |
| @@ -194348,11 +194466,11 @@ | ||
| 194348 | 194466 | |
| 194349 | 194467 | return rc; |
| 194350 | 194468 | } |
| 194351 | 194469 | |
| 194352 | 194470 | /* |
| 194353 | -** Restart interation for expression pExpr so that the next call to | |
| 194471 | +** Restart iteration for expression pExpr so that the next call to | |
| 194354 | 194472 | ** fts3EvalNext() visits the first row. Do not allow incremental |
| 194355 | 194473 | ** loading or merging of phrase doclists for this iteration. |
| 194356 | 194474 | ** |
| 194357 | 194475 | ** If *pRc is other than SQLITE_OK when this function is called, it is |
| 194358 | 194476 | ** a no-op. If an error occurs within this function, *pRc is set to an |
| @@ -195539,10 +195657,27 @@ | ||
| 195539 | 195657 | /* |
| 195540 | 195658 | ** Function getNextNode(), which is called by fts3ExprParse(), may itself |
| 195541 | 195659 | ** call fts3ExprParse(). So this forward declaration is required. |
| 195542 | 195660 | */ |
| 195543 | 195661 | static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *); |
| 195662 | + | |
| 195663 | +/* | |
| 195664 | +** Search buffer z[], size n, for a '"' character. Or, if enable_parenthesis | |
| 195665 | +** is defined, search for '(' and ')' as well. Return the index of the first | |
| 195666 | +** such character in the buffer. If there is no such character, return -1. | |
| 195667 | +*/ | |
| 195668 | +static int findBarredChar(const char *z, int n){ | |
| 195669 | + int ii; | |
| 195670 | + for(ii=0; ii<n; ii++){ | |
| 195671 | + if( (z[ii]=='"') | |
| 195672 | + || (sqlite3_fts3_enable_parentheses && (z[ii]=='(' || z[ii]==')')) | |
| 195673 | + ){ | |
| 195674 | + return ii; | |
| 195675 | + } | |
| 195676 | + } | |
| 195677 | + return -1; | |
| 195678 | +} | |
| 195544 | 195679 | |
| 195545 | 195680 | /* |
| 195546 | 195681 | ** Extract the next token from buffer z (length n) using the tokenizer |
| 195547 | 195682 | ** and other information (column names etc.) in pParse. Create an Fts3Expr |
| 195548 | 195683 | ** structure of type FTSQUERY_PHRASE containing a phrase consisting of this |
| @@ -195564,38 +195699,42 @@ | ||
| 195564 | 195699 | sqlite3_tokenizer *pTokenizer = pParse->pTokenizer; |
| 195565 | 195700 | sqlite3_tokenizer_module const *pModule = pTokenizer->pModule; |
| 195566 | 195701 | int rc; |
| 195567 | 195702 | sqlite3_tokenizer_cursor *pCursor; |
| 195568 | 195703 | Fts3Expr *pRet = 0; |
| 195569 | - int i = 0; | |
| 195570 | - | |
| 195571 | - /* Set variable i to the maximum number of bytes of input to tokenize. */ | |
| 195572 | - for(i=0; i<n; i++){ | |
| 195573 | - if( sqlite3_fts3_enable_parentheses && (z[i]=='(' || z[i]==')') ) break; | |
| 195574 | - if( z[i]=='"' ) break; | |
| 195575 | - } | |
| 195576 | - | |
| 195577 | - *pnConsumed = i; | |
| 195578 | - rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, i, &pCursor); | |
| 195704 | + | |
| 195705 | + *pnConsumed = n; | |
| 195706 | + rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, n, &pCursor); | |
| 195579 | 195707 | if( rc==SQLITE_OK ){ |
| 195580 | 195708 | const char *zToken; |
| 195581 | 195709 | int nToken = 0, iStart = 0, iEnd = 0, iPosition = 0; |
| 195582 | 195710 | sqlite3_int64 nByte; /* total space to allocate */ |
| 195583 | 195711 | |
| 195584 | 195712 | rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition); |
| 195585 | 195713 | if( rc==SQLITE_OK ){ |
| 195586 | - nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken; | |
| 195714 | + /* Check that this tokenization did not gobble up any " characters. Or, | |
| 195715 | + ** if enable_parenthesis is true, that it did not gobble up any | |
| 195716 | + ** open or close parenthesis characters either. If it did, call | |
| 195717 | + ** getNextToken() again, but pass only that part of the input buffer | |
| 195718 | + ** up to the first such character. */ | |
| 195719 | + int iBarred = findBarredChar(z, iEnd); | |
| 195720 | + if( iBarred>=0 ){ | |
| 195721 | + pModule->xClose(pCursor); | |
| 195722 | + return getNextToken(pParse, iCol, z, iBarred, ppExpr, pnConsumed); | |
| 195723 | + } | |
| 195724 | + | |
| 195725 | + nByte = sizeof(Fts3Expr) + SZ_FTS3PHRASE(1) + nToken; | |
| 195587 | 195726 | pRet = (Fts3Expr *)sqlite3Fts3MallocZero(nByte); |
| 195588 | 195727 | if( !pRet ){ |
| 195589 | 195728 | rc = SQLITE_NOMEM; |
| 195590 | 195729 | }else{ |
| 195591 | 195730 | pRet->eType = FTSQUERY_PHRASE; |
| 195592 | 195731 | pRet->pPhrase = (Fts3Phrase *)&pRet[1]; |
| 195593 | 195732 | pRet->pPhrase->nToken = 1; |
| 195594 | 195733 | pRet->pPhrase->iColumn = iCol; |
| 195595 | 195734 | pRet->pPhrase->aToken[0].n = nToken; |
| 195596 | - pRet->pPhrase->aToken[0].z = (char *)&pRet->pPhrase[1]; | |
| 195735 | + pRet->pPhrase->aToken[0].z = (char*)&pRet->pPhrase->aToken[1]; | |
| 195597 | 195736 | memcpy(pRet->pPhrase->aToken[0].z, zToken, nToken); |
| 195598 | 195737 | |
| 195599 | 195738 | if( iEnd<n && z[iEnd]=='*' ){ |
| 195600 | 195739 | pRet->pPhrase->aToken[0].isPrefix = 1; |
| 195601 | 195740 | iEnd++; |
| @@ -195615,11 +195754,15 @@ | ||
| 195615 | 195754 | } |
| 195616 | 195755 | } |
| 195617 | 195756 | |
| 195618 | 195757 | } |
| 195619 | 195758 | *pnConsumed = iEnd; |
| 195620 | - }else if( i && rc==SQLITE_DONE ){ | |
| 195759 | + }else if( n && rc==SQLITE_DONE ){ | |
| 195760 | + int iBarred = findBarredChar(z, n); | |
| 195761 | + if( iBarred>=0 ){ | |
| 195762 | + *pnConsumed = iBarred; | |
| 195763 | + } | |
| 195621 | 195764 | rc = SQLITE_OK; |
| 195622 | 195765 | } |
| 195623 | 195766 | |
| 195624 | 195767 | pModule->xClose(pCursor); |
| 195625 | 195768 | } |
| @@ -195664,11 +195807,11 @@ | ||
| 195664 | 195807 | Fts3Expr *p = 0; |
| 195665 | 195808 | sqlite3_tokenizer_cursor *pCursor = 0; |
| 195666 | 195809 | char *zTemp = 0; |
| 195667 | 195810 | i64 nTemp = 0; |
| 195668 | 195811 | |
| 195669 | - const int nSpace = sizeof(Fts3Expr) + sizeof(Fts3Phrase); | |
| 195812 | + const int nSpace = sizeof(Fts3Expr) + SZ_FTS3PHRASE(1); | |
| 195670 | 195813 | int nToken = 0; |
| 195671 | 195814 | |
| 195672 | 195815 | /* The final Fts3Expr data structure, including the Fts3Phrase, |
| 195673 | 195816 | ** Fts3PhraseToken structures token buffers are all stored as a single |
| 195674 | 195817 | ** allocation so that the expression can be freed with a single call to |
| @@ -196036,11 +196179,11 @@ | ||
| 196036 | 196179 | int eType = p->eType; |
| 196037 | 196180 | isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft); |
| 196038 | 196181 | |
| 196039 | 196182 | /* The isRequirePhrase variable is set to true if a phrase or |
| 196040 | 196183 | ** an expression contained in parenthesis is required. If a |
| 196041 | - ** binary operator (AND, OR, NOT or NEAR) is encounted when | |
| 196184 | + ** binary operator (AND, OR, NOT or NEAR) is encountered when | |
| 196042 | 196185 | ** isRequirePhrase is set, this is a syntax error. |
| 196043 | 196186 | */ |
| 196044 | 196187 | if( !isPhrase && isRequirePhrase ){ |
| 196045 | 196188 | sqlite3Fts3ExprFree(p); |
| 196046 | 196189 | rc = SQLITE_ERROR; |
| @@ -196618,11 +196761,10 @@ | ||
| 196618 | 196761 | pTokenizer, 0, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr |
| 196619 | 196762 | ); |
| 196620 | 196763 | } |
| 196621 | 196764 | |
| 196622 | 196765 | if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){ |
| 196623 | - sqlite3Fts3ExprFree(pExpr); | |
| 196624 | 196766 | sqlite3_result_error(context, "Error parsing expression", -1); |
| 196625 | 196767 | }else if( rc==SQLITE_NOMEM || !(zBuf = exprToString(pExpr, 0)) ){ |
| 196626 | 196768 | sqlite3_result_error_nomem(context); |
| 196627 | 196769 | }else{ |
| 196628 | 196770 | sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); |
| @@ -196861,11 +197003,11 @@ | ||
| 196861 | 197003 | pEntry->count++; |
| 196862 | 197004 | pEntry->chain = pNew; |
| 196863 | 197005 | } |
| 196864 | 197006 | |
| 196865 | 197007 | |
| 196866 | -/* Resize the hash table so that it cantains "new_size" buckets. | |
| 197008 | +/* Resize the hash table so that it contains "new_size" buckets. | |
| 196867 | 197009 | ** "new_size" must be a power of 2. The hash table might fail |
| 196868 | 197010 | ** to resize if sqliteMalloc() fails. |
| 196869 | 197011 | ** |
| 196870 | 197012 | ** Return non-zero if a memory allocation error occurs. |
| 196871 | 197013 | */ |
| @@ -197316,11 +197458,11 @@ | ||
| 197316 | 197458 | isConsonant(z+2); |
| 197317 | 197459 | } |
| 197318 | 197460 | |
| 197319 | 197461 | /* |
| 197320 | 197462 | ** If the word ends with zFrom and xCond() is true for the stem |
| 197321 | -** of the word that preceeds the zFrom ending, then change the | |
| 197463 | +** of the word that precedes the zFrom ending, then change the | |
| 197322 | 197464 | ** ending to zTo. |
| 197323 | 197465 | ** |
| 197324 | 197466 | ** The input word *pz and zFrom are both in reverse order. zTo |
| 197325 | 197467 | ** is in normal order. |
| 197326 | 197468 | ** |
| @@ -202899,11 +203041,11 @@ | ||
| 202899 | 203041 | ** previous term. Before this function returns, it is updated to contain a |
| 202900 | 203042 | ** copy of zTerm/nTerm. |
| 202901 | 203043 | ** |
| 202902 | 203044 | ** It is assumed that the buffer associated with pNode is already large |
| 202903 | 203045 | ** enough to accommodate the new entry. The buffer associated with pPrev |
| 202904 | -** is extended by this function if requrired. | |
| 203046 | +** is extended by this function if required. | |
| 202905 | 203047 | ** |
| 202906 | 203048 | ** If an error (i.e. OOM condition) occurs, an SQLite error code is |
| 202907 | 203049 | ** returned. Otherwise, SQLITE_OK. |
| 202908 | 203050 | */ |
| 202909 | 203051 | static int fts3AppendToNode( |
| @@ -204562,11 +204704,11 @@ | ||
| 204562 | 204704 | #endif |
| 204563 | 204705 | |
| 204564 | 204706 | /* |
| 204565 | 204707 | ** SQLite value pRowid contains the rowid of a row that may or may not be |
| 204566 | 204708 | ** present in the FTS3 table. If it is, delete it and adjust the contents |
| 204567 | -** of subsiduary data structures accordingly. | |
| 204709 | +** of subsidiary data structures accordingly. | |
| 204568 | 204710 | */ |
| 204569 | 204711 | static int fts3DeleteByRowid( |
| 204570 | 204712 | Fts3Table *p, |
| 204571 | 204713 | sqlite3_value *pRowid, |
| 204572 | 204714 | int *pnChng, /* IN/OUT: Decrement if row is deleted */ |
| @@ -204888,12 +205030,16 @@ | ||
| 204888 | 205030 | struct MatchinfoBuffer { |
| 204889 | 205031 | u8 aRef[3]; |
| 204890 | 205032 | int nElem; |
| 204891 | 205033 | int bGlobal; /* Set if global data is loaded */ |
| 204892 | 205034 | char *zMatchinfo; |
| 204893 | - u32 aMatchinfo[1]; | |
| 205035 | + u32 aMI[FLEXARRAY]; | |
| 204894 | 205036 | }; |
| 205037 | + | |
| 205038 | +/* Size (in bytes) of a MatchinfoBuffer sufficient for N elements */ | |
| 205039 | +#define SZ_MATCHINFOBUFFER(N) \ | |
| 205040 | + (offsetof(MatchinfoBuffer,aMI)+(((N)+1)/2)*sizeof(u64)) | |
| 204895 | 205041 | |
| 204896 | 205042 | |
| 204897 | 205043 | /* |
| 204898 | 205044 | ** The snippet() and offsets() functions both return text values. An instance |
| 204899 | 205045 | ** of the following structure is used to accumulate those values while the |
| @@ -204915,17 +205061,17 @@ | ||
| 204915 | 205061 | ** Allocate a two-slot MatchinfoBuffer object. |
| 204916 | 205062 | */ |
| 204917 | 205063 | static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){ |
| 204918 | 205064 | MatchinfoBuffer *pRet; |
| 204919 | 205065 | sqlite3_int64 nByte = sizeof(u32) * (2*(sqlite3_int64)nElem + 1) |
| 204920 | - + sizeof(MatchinfoBuffer); | |
| 205066 | + + SZ_MATCHINFOBUFFER(1); | |
| 204921 | 205067 | sqlite3_int64 nStr = strlen(zMatchinfo); |
| 204922 | 205068 | |
| 204923 | 205069 | pRet = sqlite3Fts3MallocZero(nByte + nStr+1); |
| 204924 | 205070 | if( pRet ){ |
| 204925 | - pRet->aMatchinfo[0] = (u8*)(&pRet->aMatchinfo[1]) - (u8*)pRet; | |
| 204926 | - pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0] | |
| 205071 | + pRet->aMI[0] = (u8*)(&pRet->aMI[1]) - (u8*)pRet; | |
| 205072 | + pRet->aMI[1+nElem] = pRet->aMI[0] | |
| 204927 | 205073 | + sizeof(u32)*((int)nElem+1); |
| 204928 | 205074 | pRet->nElem = (int)nElem; |
| 204929 | 205075 | pRet->zMatchinfo = ((char*)pRet) + nByte; |
| 204930 | 205076 | memcpy(pRet->zMatchinfo, zMatchinfo, nStr+1); |
| 204931 | 205077 | pRet->aRef[0] = 1; |
| @@ -204935,14 +205081,14 @@ | ||
| 204935 | 205081 | } |
| 204936 | 205082 | |
| 204937 | 205083 | static void fts3MIBufferFree(void *p){ |
| 204938 | 205084 | MatchinfoBuffer *pBuf = (MatchinfoBuffer*)((u8*)p - ((u32*)p)[-1]); |
| 204939 | 205085 | |
| 204940 | - assert( (u32*)p==&pBuf->aMatchinfo[1] | |
| 204941 | - || (u32*)p==&pBuf->aMatchinfo[pBuf->nElem+2] | |
| 205086 | + assert( (u32*)p==&pBuf->aMI[1] | |
| 205087 | + || (u32*)p==&pBuf->aMI[pBuf->nElem+2] | |
| 204942 | 205088 | ); |
| 204943 | - if( (u32*)p==&pBuf->aMatchinfo[1] ){ | |
| 205089 | + if( (u32*)p==&pBuf->aMI[1] ){ | |
| 204944 | 205090 | pBuf->aRef[1] = 0; |
| 204945 | 205091 | }else{ |
| 204946 | 205092 | pBuf->aRef[2] = 0; |
| 204947 | 205093 | } |
| 204948 | 205094 | |
| @@ -204955,32 +205101,32 @@ | ||
| 204955 | 205101 | void (*xRet)(void*) = 0; |
| 204956 | 205102 | u32 *aOut = 0; |
| 204957 | 205103 | |
| 204958 | 205104 | if( p->aRef[1]==0 ){ |
| 204959 | 205105 | p->aRef[1] = 1; |
| 204960 | - aOut = &p->aMatchinfo[1]; | |
| 205106 | + aOut = &p->aMI[1]; | |
| 204961 | 205107 | xRet = fts3MIBufferFree; |
| 204962 | 205108 | } |
| 204963 | 205109 | else if( p->aRef[2]==0 ){ |
| 204964 | 205110 | p->aRef[2] = 1; |
| 204965 | - aOut = &p->aMatchinfo[p->nElem+2]; | |
| 205111 | + aOut = &p->aMI[p->nElem+2]; | |
| 204966 | 205112 | xRet = fts3MIBufferFree; |
| 204967 | 205113 | }else{ |
| 204968 | 205114 | aOut = (u32*)sqlite3_malloc64(p->nElem * sizeof(u32)); |
| 204969 | 205115 | if( aOut ){ |
| 204970 | 205116 | xRet = sqlite3_free; |
| 204971 | - if( p->bGlobal ) memcpy(aOut, &p->aMatchinfo[1], p->nElem*sizeof(u32)); | |
| 205117 | + if( p->bGlobal ) memcpy(aOut, &p->aMI[1], p->nElem*sizeof(u32)); | |
| 204972 | 205118 | } |
| 204973 | 205119 | } |
| 204974 | 205120 | |
| 204975 | 205121 | *paOut = aOut; |
| 204976 | 205122 | return xRet; |
| 204977 | 205123 | } |
| 204978 | 205124 | |
| 204979 | 205125 | static void fts3MIBufferSetGlobal(MatchinfoBuffer *p){ |
| 204980 | 205126 | p->bGlobal = 1; |
| 204981 | - memcpy(&p->aMatchinfo[2+p->nElem], &p->aMatchinfo[1], p->nElem*sizeof(u32)); | |
| 205127 | + memcpy(&p->aMI[2+p->nElem], &p->aMI[1], p->nElem*sizeof(u32)); | |
| 204982 | 205128 | } |
| 204983 | 205129 | |
| 204984 | 205130 | /* |
| 204985 | 205131 | ** Free a MatchinfoBuffer object allocated using fts3MIBufferNew() |
| 204986 | 205132 | */ |
| @@ -205391,11 +205537,11 @@ | ||
| 205391 | 205537 | if( nAppend<0 ){ |
| 205392 | 205538 | nAppend = (int)strlen(zAppend); |
| 205393 | 205539 | } |
| 205394 | 205540 | |
| 205395 | 205541 | /* If there is insufficient space allocated at StrBuffer.z, use realloc() |
| 205396 | - ** to grow the buffer until so that it is big enough to accomadate the | |
| 205542 | + ** to grow the buffer until so that it is big enough to accommodate the | |
| 205397 | 205543 | ** appended data. |
| 205398 | 205544 | */ |
| 205399 | 205545 | if( pStr->n+nAppend+1>=pStr->nAlloc ){ |
| 205400 | 205546 | sqlite3_int64 nAlloc = pStr->nAlloc+(sqlite3_int64)nAppend+100; |
| 205401 | 205547 | char *zNew = sqlite3_realloc64(pStr->z, nAlloc); |
| @@ -207766,11 +207912,11 @@ | ||
| 207766 | 207912 | ** |
| 207767 | 207913 | ** When a match if found, the matching entry is moved to become the |
| 207768 | 207914 | ** most-recently used entry if it isn't so already. |
| 207769 | 207915 | ** |
| 207770 | 207916 | ** The JsonParse object returned still belongs to the Cache and might |
| 207771 | -** be deleted at any moment. If the caller whants the JsonParse to | |
| 207917 | +** be deleted at any moment. If the caller wants the JsonParse to | |
| 207772 | 207918 | ** linger, it needs to increment the nPJRef reference counter. |
| 207773 | 207919 | */ |
| 207774 | 207920 | static JsonParse *jsonCacheSearch( |
| 207775 | 207921 | sqlite3_context *ctx, /* The SQL statement context holding the cache */ |
| 207776 | 207922 | sqlite3_value *pArg /* Function argument containing SQL text */ |
| @@ -210811,11 +210957,11 @@ | ||
| 210811 | 210957 | ** |
| 210812 | 210958 | ** This goes against all historical documentation about how the SQLite |
| 210813 | 210959 | ** JSON functions were suppose to work. From the beginning, blob was |
| 210814 | 210960 | ** reserved for expansion and a blob value should have raised an error. |
| 210815 | 210961 | ** But it did not, due to a bug. And many applications came to depend |
| 210816 | - ** upon this buggy behavior, espeically when using the CLI and reading | |
| 210962 | + ** upon this buggy behavior, especially when using the CLI and reading | |
| 210817 | 210963 | ** JSON text using readfile(), which returns a blob. For this reason |
| 210818 | 210964 | ** we will continue to support the bug moving forward. |
| 210819 | 210965 | ** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d |
| 210820 | 210966 | */ |
| 210821 | 210967 | } |
| @@ -212911,10 +213057,18 @@ | ||
| 212911 | 213057 | # define NEVER(X) ((X)?(assert(0),1):0) |
| 212912 | 213058 | #else |
| 212913 | 213059 | # define ALWAYS(X) (X) |
| 212914 | 213060 | # define NEVER(X) (X) |
| 212915 | 213061 | #endif |
| 213062 | +#ifndef offsetof | |
| 213063 | +#define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) | |
| 213064 | +#endif | |
| 213065 | +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) | |
| 213066 | +# define FLEXARRAY | |
| 213067 | +#else | |
| 213068 | +# define FLEXARRAY 1 | |
| 213069 | +#endif | |
| 212916 | 213070 | #endif /* !defined(SQLITE_AMALGAMATION) */ |
| 212917 | 213071 | |
| 212918 | 213072 | /* Macro to check for 4-byte alignment. Only used inside of assert() */ |
| 212919 | 213073 | #ifdef SQLITE_DEBUG |
| 212920 | 213074 | # define FOUR_BYTE_ALIGNED(X) ((((char*)(X) - (char*)0) & 3)==0) |
| @@ -213231,13 +213385,17 @@ | ||
| 213231 | 213385 | struct RtreeMatchArg { |
| 213232 | 213386 | u32 iSize; /* Size of this object */ |
| 213233 | 213387 | RtreeGeomCallback cb; /* Info about the callback functions */ |
| 213234 | 213388 | int nParam; /* Number of parameters to the SQL function */ |
| 213235 | 213389 | sqlite3_value **apSqlParam; /* Original SQL parameter values */ |
| 213236 | - RtreeDValue aParam[1]; /* Values for parameters to the SQL function */ | |
| 213390 | + RtreeDValue aParam[FLEXARRAY]; /* Values for parameters to the SQL function */ | |
| 213237 | 213391 | }; |
| 213238 | 213392 | |
| 213393 | +/* Size of an RtreeMatchArg object with N parameters */ | |
| 213394 | +#define SZ_RTREEMATCHARG(N) \ | |
| 213395 | + (offsetof(RtreeMatchArg,aParam)+(N)*sizeof(RtreeDValue)) | |
| 213396 | + | |
| 213239 | 213397 | #ifndef MAX |
| 213240 | 213398 | # define MAX(x,y) ((x) < (y) ? (y) : (x)) |
| 213241 | 213399 | #endif |
| 213242 | 213400 | #ifndef MIN |
| 213243 | 213401 | # define MIN(x,y) ((x) > (y) ? (y) : (x)) |
| @@ -214922,11 +215080,11 @@ | ||
| 214922 | 215080 | |
| 214923 | 215081 | return rc; |
| 214924 | 215082 | } |
| 214925 | 215083 | |
| 214926 | 215084 | /* |
| 214927 | -** Return the N-dimensional volumn of the cell stored in *p. | |
| 215085 | +** Return the N-dimensional volume of the cell stored in *p. | |
| 214928 | 215086 | */ |
| 214929 | 215087 | static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){ |
| 214930 | 215088 | RtreeDValue area = (RtreeDValue)1; |
| 214931 | 215089 | assert( pRtree->nDim>=1 && pRtree->nDim<=5 ); |
| 214932 | 215090 | #ifndef SQLITE_RTREE_INT_ONLY |
| @@ -216688,11 +216846,11 @@ | ||
| 216688 | 216846 | } |
| 216689 | 216847 | |
| 216690 | 216848 | /* |
| 216691 | 216849 | ** The second and subsequent arguments to this function are a printf() |
| 216692 | 216850 | ** style format string and arguments. This function formats the string and |
| 216693 | -** appends it to the report being accumuated in pCheck. | |
| 216851 | +** appends it to the report being accumulated in pCheck. | |
| 216694 | 216852 | */ |
| 216695 | 216853 | static void rtreeCheckAppendMsg(RtreeCheck *pCheck, const char *zFmt, ...){ |
| 216696 | 216854 | va_list ap; |
| 216697 | 216855 | va_start(ap, zFmt); |
| 216698 | 216856 | if( pCheck->rc==SQLITE_OK && pCheck->nErr<RTREE_CHECK_MAX_ERROR ){ |
| @@ -217876,11 +218034,11 @@ | ||
| 217876 | 218034 | |
| 217877 | 218035 | /* |
| 217878 | 218036 | ** Determine if point (x0,y0) is beneath line segment (x1,y1)->(x2,y2). |
| 217879 | 218037 | ** Returns: |
| 217880 | 218038 | ** |
| 217881 | -** +2 x0,y0 is on the line segement | |
| 218039 | +** +2 x0,y0 is on the line segment | |
| 217882 | 218040 | ** |
| 217883 | 218041 | ** +1 x0,y0 is beneath line segment |
| 217884 | 218042 | ** |
| 217885 | 218043 | ** 0 x0,y0 is not on or beneath the line segment or the line segment |
| 217886 | 218044 | ** is vertical and x0,y0 is not on the line segment |
| @@ -217982,11 +218140,11 @@ | ||
| 217982 | 218140 | } |
| 217983 | 218141 | sqlite3_free(p1); |
| 217984 | 218142 | sqlite3_free(p2); |
| 217985 | 218143 | } |
| 217986 | 218144 | |
| 217987 | -/* Objects used by the overlap algorihm. */ | |
| 218145 | +/* Objects used by the overlap algorithm. */ | |
| 217988 | 218146 | typedef struct GeoEvent GeoEvent; |
| 217989 | 218147 | typedef struct GeoSegment GeoSegment; |
| 217990 | 218148 | typedef struct GeoOverlap GeoOverlap; |
| 217991 | 218149 | struct GeoEvent { |
| 217992 | 218150 | double x; /* X coordinate at which event occurs */ |
| @@ -219029,12 +219187,11 @@ | ||
| 219029 | 219187 | RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx); |
| 219030 | 219188 | RtreeMatchArg *pBlob; |
| 219031 | 219189 | sqlite3_int64 nBlob; |
| 219032 | 219190 | int memErr = 0; |
| 219033 | 219191 | |
| 219034 | - nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(RtreeDValue) | |
| 219035 | - + nArg*sizeof(sqlite3_value*); | |
| 219192 | + nBlob = SZ_RTREEMATCHARG(nArg) + nArg*sizeof(sqlite3_value*); | |
| 219036 | 219193 | pBlob = (RtreeMatchArg *)sqlite3_malloc64(nBlob); |
| 219037 | 219194 | if( !pBlob ){ |
| 219038 | 219195 | sqlite3_result_error_nomem(ctx); |
| 219039 | 219196 | }else{ |
| 219040 | 219197 | int i; |
| @@ -220125,11 +220282,11 @@ | ||
| 220125 | 220282 | ** to read from the original database snapshot. In other words, partially |
| 220126 | 220283 | ** applied transactions are not visible to other clients. |
| 220127 | 220284 | ** |
| 220128 | 220285 | ** "RBU" stands for "Resumable Bulk Update". As in a large database update |
| 220129 | 220286 | ** transmitted via a wireless network to a mobile device. A transaction |
| 220130 | -** applied using this extension is hence refered to as an "RBU update". | |
| 220287 | +** applied using this extension is hence referred to as an "RBU update". | |
| 220131 | 220288 | ** |
| 220132 | 220289 | ** |
| 220133 | 220290 | ** LIMITATIONS |
| 220134 | 220291 | ** |
| 220135 | 220292 | ** An "RBU update" transaction is subject to the following limitations: |
| @@ -220422,11 +220579,11 @@ | ||
| 220422 | 220579 | ** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents |
| 220423 | 220580 | ** of the state tables within the state database are zeroed. This way, |
| 220424 | 220581 | ** the next call to sqlite3rbu_vacuum() opens a handle that starts a |
| 220425 | 220582 | ** new RBU vacuum operation. |
| 220426 | 220583 | ** |
| 220427 | -** As with sqlite3rbu_open(), Zipvfs users should rever to the comment | |
| 220584 | +** As with sqlite3rbu_open(), Zipvfs users should refer to the comment | |
| 220428 | 220585 | ** describing the sqlite3rbu_create_vfs() API function below for |
| 220429 | 220586 | ** a description of the complications associated with using RBU with |
| 220430 | 220587 | ** zipvfs databases. |
| 220431 | 220588 | */ |
| 220432 | 220589 | SQLITE_API sqlite3rbu *sqlite3rbu_vacuum( |
| @@ -220518,11 +220675,11 @@ | ||
| 220518 | 220675 | /* |
| 220519 | 220676 | ** Close an RBU handle. |
| 220520 | 220677 | ** |
| 220521 | 220678 | ** If the RBU update has been completely applied, mark the RBU database |
| 220522 | 220679 | ** as fully applied. Otherwise, assuming no error has occurred, save the |
| 220523 | -** current state of the RBU update appliation to the RBU database. | |
| 220680 | +** current state of the RBU update application to the RBU database. | |
| 220524 | 220681 | ** |
| 220525 | 220682 | ** If an error has already occurred as part of an sqlite3rbu_step() |
| 220526 | 220683 | ** or sqlite3rbu_open() call, or if one occurs within this function, an |
| 220527 | 220684 | ** SQLite error code is returned. Additionally, if pzErrmsg is not NULL, |
| 220528 | 220685 | ** *pzErrmsg may be set to point to a buffer containing a utf-8 formatted |
| @@ -225444,11 +225601,11 @@ | ||
| 225444 | 225601 | int rc; |
| 225445 | 225602 | rc = p->pReal->pMethods->xFileSize(p->pReal, pSize); |
| 225446 | 225603 | |
| 225447 | 225604 | /* If this is an RBU vacuum operation and this is the target database, |
| 225448 | 225605 | ** pretend that it has at least one page. Otherwise, SQLite will not |
| 225449 | - ** check for the existance of a *-wal file. rbuVfsRead() contains | |
| 225606 | + ** check for the existence of a *-wal file. rbuVfsRead() contains | |
| 225450 | 225607 | ** similar logic. */ |
| 225451 | 225608 | if( rc==SQLITE_OK && *pSize==0 |
| 225452 | 225609 | && p->pRbu && rbuIsVacuum(p->pRbu) |
| 225453 | 225610 | && (p->openFlags & SQLITE_OPEN_MAIN_DB) |
| 225454 | 225611 | ){ |
| @@ -228674,11 +228831,11 @@ | ||
| 228674 | 228831 | } |
| 228675 | 228832 | |
| 228676 | 228833 | /* |
| 228677 | 228834 | ** This function is called to initialize the SessionTable.nCol, azCol[] |
| 228678 | 228835 | ** abPK[] and azDflt[] members of SessionTable object pTab. If these |
| 228679 | -** fields are already initilialized, this function is a no-op. | |
| 228836 | +** fields are already initialized, this function is a no-op. | |
| 228680 | 228837 | ** |
| 228681 | 228838 | ** If an error occurs, an error code is stored in sqlite3_session.rc and |
| 228682 | 228839 | ** non-zero returned. Or, if no error occurs but the table has no primary |
| 228683 | 228840 | ** key, sqlite3_session.rc is left set to SQLITE_OK and non-zero returned to |
| 228684 | 228841 | ** indicate that updates on this table should be ignored. SessionTable.abPK |
| @@ -230497,11 +230654,11 @@ | ||
| 230497 | 230654 | int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */ |
| 230498 | 230655 | void **ppChangeset /* OUT: Buffer containing changeset */ |
| 230499 | 230656 | ){ |
| 230500 | 230657 | sqlite3 *db = pSession->db; /* Source database handle */ |
| 230501 | 230658 | SessionTable *pTab; /* Used to iterate through attached tables */ |
| 230502 | - SessionBuffer buf = {0,0,0}; /* Buffer in which to accumlate changeset */ | |
| 230659 | + SessionBuffer buf = {0,0,0}; /* Buffer in which to accumulate changeset */ | |
| 230503 | 230660 | int rc; /* Return code */ |
| 230504 | 230661 | |
| 230505 | 230662 | assert( xOutput==0 || (pnChangeset==0 && ppChangeset==0) ); |
| 230506 | 230663 | assert( xOutput!=0 || (pnChangeset!=0 && ppChangeset!=0) ); |
| 230507 | 230664 | |
| @@ -234931,10 +235088,22 @@ | ||
| 234931 | 235088 | # define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&3)==0) |
| 234932 | 235089 | #else |
| 234933 | 235090 | # define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0) |
| 234934 | 235091 | #endif |
| 234935 | 235092 | |
| 235093 | +/* | |
| 235094 | +** Macros needed to provide flexible arrays in a portable way | |
| 235095 | +*/ | |
| 235096 | +#ifndef offsetof | |
| 235097 | +# define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) | |
| 235098 | +#endif | |
| 235099 | +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) | |
| 235100 | +# define FLEXARRAY | |
| 235101 | +#else | |
| 235102 | +# define FLEXARRAY 1 | |
| 235103 | +#endif | |
| 235104 | + | |
| 234936 | 235105 | #endif |
| 234937 | 235106 | |
| 234938 | 235107 | /* Truncate very long tokens to this many bytes. Hard limit is |
| 234939 | 235108 | ** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset |
| 234940 | 235109 | ** field that occurs at the start of each leaf page (see fts5_index.c). */ |
| @@ -235003,14 +235172,15 @@ | ||
| 235003 | 235172 | ** |
| 235004 | 235173 | ** This object is used by fts5_expr.c and fts5_index.c. |
| 235005 | 235174 | */ |
| 235006 | 235175 | struct Fts5Colset { |
| 235007 | 235176 | int nCol; |
| 235008 | - int aiCol[1]; | |
| 235177 | + int aiCol[FLEXARRAY]; | |
| 235009 | 235178 | }; |
| 235010 | 235179 | |
| 235011 | - | |
| 235180 | +/* Size (int bytes) of a complete Fts5Colset object with N columns. */ | |
| 235181 | +#define SZ_FTS5COLSET(N) (sizeof(i64)*((N+2)/2)) | |
| 235012 | 235182 | |
| 235013 | 235183 | /************************************************************************** |
| 235014 | 235184 | ** Interface to code in fts5_config.c. fts5_config.c contains contains code |
| 235015 | 235185 | ** to parse the arguments passed to the CREATE VIRTUAL TABLE statement. |
| 235016 | 235186 | */ |
| @@ -235835,11 +236005,11 @@ | ||
| 235835 | 236005 | ************************************************************************* |
| 235836 | 236006 | ** Driver template for the LEMON parser generator. |
| 235837 | 236007 | ** |
| 235838 | 236008 | ** The "lemon" program processes an LALR(1) input grammar file, then uses |
| 235839 | 236009 | ** this template to construct a parser. The "lemon" program inserts text |
| 235840 | -** at each "%%" line. Also, any "P-a-r-s-e" identifer prefix (without the | |
| 236010 | +** at each "%%" line. Also, any "P-a-r-s-e" identifier prefix (without the | |
| 235841 | 236011 | ** interstitial "-" characters) contained in this template is changed into |
| 235842 | 236012 | ** the value of the %name directive from the grammar. Otherwise, the content |
| 235843 | 236013 | ** of this template is copied straight through into the generate parser |
| 235844 | 236014 | ** source file. |
| 235845 | 236015 | ** |
| @@ -237989,11 +238159,11 @@ | ||
| 237989 | 238159 | ** where "N" is the total number of documents in the set and nHit |
| 237990 | 238160 | ** is the number that contain at least one instance of the phrase |
| 237991 | 238161 | ** under consideration. |
| 237992 | 238162 | ** |
| 237993 | 238163 | ** The problem with this is that if (N < 2*nHit), the IDF is |
| 237994 | - ** negative. Which is undesirable. So the mimimum allowable IDF is | |
| 238164 | + ** negative. Which is undesirable. So the minimum allowable IDF is | |
| 237995 | 238165 | ** (1e-6) - roughly the same as a term that appears in just over |
| 237996 | 238166 | ** half of set of 5,000,000 documents. */ |
| 237997 | 238167 | double idf = log( (nRow - nHit + 0.5) / (nHit + 0.5) ); |
| 237998 | 238168 | if( idf<=0.0 ) idf = 1e-6; |
| 237999 | 238169 | p->aIDF[i] = idf; |
| @@ -238452,11 +238622,11 @@ | ||
| 238452 | 238622 | ** |
| 238453 | 238623 | ** * All non-ASCII characters, |
| 238454 | 238624 | ** * The 52 upper and lower case ASCII characters, and |
| 238455 | 238625 | ** * The 10 integer ASCII characters. |
| 238456 | 238626 | ** * The underscore character "_" (0x5F). |
| 238457 | -** * The unicode "subsitute" character (0x1A). | |
| 238627 | +** * The unicode "substitute" character (0x1A). | |
| 238458 | 238628 | */ |
| 238459 | 238629 | static int sqlite3Fts5IsBareword(char t){ |
| 238460 | 238630 | u8 aBareword[128] = { |
| 238461 | 238631 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 .. 0x0F */ |
| 238462 | 238632 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* 0x10 .. 0x1F */ |
| @@ -239770,12 +239940,16 @@ | ||
| 239770 | 239940 | Fts5ExprNearset *pNear; /* For FTS5_STRING - cluster of phrases */ |
| 239771 | 239941 | |
| 239772 | 239942 | /* Child nodes. For a NOT node, this array always contains 2 entries. For |
| 239773 | 239943 | ** AND or OR nodes, it contains 2 or more entries. */ |
| 239774 | 239944 | int nChild; /* Number of child nodes */ |
| 239775 | - Fts5ExprNode *apChild[1]; /* Array of child nodes */ | |
| 239945 | + Fts5ExprNode *apChild[FLEXARRAY]; /* Array of child nodes */ | |
| 239776 | 239946 | }; |
| 239947 | + | |
| 239948 | +/* Size (in bytes) of an Fts5ExprNode object that holds up to N children */ | |
| 239949 | +#define SZ_FTS5EXPRNODE(N) \ | |
| 239950 | + (offsetof(Fts5ExprNode,apChild) + (N)*sizeof(Fts5ExprNode*)) | |
| 239777 | 239951 | |
| 239778 | 239952 | #define Fts5NodeIsString(p) ((p)->eType==FTS5_TERM || (p)->eType==FTS5_STRING) |
| 239779 | 239953 | |
| 239780 | 239954 | /* |
| 239781 | 239955 | ** Invoke the xNext method of an Fts5ExprNode object. This macro should be |
| @@ -239803,24 +239977,31 @@ | ||
| 239803 | 239977 | */ |
| 239804 | 239978 | struct Fts5ExprPhrase { |
| 239805 | 239979 | Fts5ExprNode *pNode; /* FTS5_STRING node this phrase is part of */ |
| 239806 | 239980 | Fts5Buffer poslist; /* Current position list */ |
| 239807 | 239981 | int nTerm; /* Number of entries in aTerm[] */ |
| 239808 | - Fts5ExprTerm aTerm[1]; /* Terms that make up this phrase */ | |
| 239982 | + Fts5ExprTerm aTerm[FLEXARRAY]; /* Terms that make up this phrase */ | |
| 239809 | 239983 | }; |
| 239984 | + | |
| 239985 | +/* Size (in bytes) of an Fts5ExprPhrase object that holds up to N terms */ | |
| 239986 | +#define SZ_FTS5EXPRPHRASE(N) \ | |
| 239987 | + (offsetof(Fts5ExprPhrase,aTerm) + (N)*sizeof(Fts5ExprTerm)) | |
| 239810 | 239988 | |
| 239811 | 239989 | /* |
| 239812 | 239990 | ** One or more phrases that must appear within a certain token distance of |
| 239813 | 239991 | ** each other within each matching document. |
| 239814 | 239992 | */ |
| 239815 | 239993 | struct Fts5ExprNearset { |
| 239816 | 239994 | int nNear; /* NEAR parameter */ |
| 239817 | 239995 | Fts5Colset *pColset; /* Columns to search (NULL -> all columns) */ |
| 239818 | 239996 | int nPhrase; /* Number of entries in aPhrase[] array */ |
| 239819 | - Fts5ExprPhrase *apPhrase[1]; /* Array of phrase pointers */ | |
| 239997 | + Fts5ExprPhrase *apPhrase[FLEXARRAY]; /* Array of phrase pointers */ | |
| 239820 | 239998 | }; |
| 239821 | 239999 | |
| 240000 | +/* Size (in bytes) of an Fts5ExprNearset object covering up to N phrases */ | |
| 240001 | +#define SZ_FTS5EXPRNEARSET(N) \ | |
| 240002 | + (offsetof(Fts5ExprNearset,apPhrase)+(N)*sizeof(Fts5ExprPhrase*)) | |
| 239822 | 240003 | |
| 239823 | 240004 | /* |
| 239824 | 240005 | ** Parse context. |
| 239825 | 240006 | */ |
| 239826 | 240007 | struct Fts5Parse { |
| @@ -239976,11 +240157,11 @@ | ||
| 239976 | 240157 | assert_expr_depth_ok(sParse.rc, sParse.pExpr); |
| 239977 | 240158 | |
| 239978 | 240159 | /* If the LHS of the MATCH expression was a user column, apply the |
| 239979 | 240160 | ** implicit column-filter. */ |
| 239980 | 240161 | if( sParse.rc==SQLITE_OK && iCol<pConfig->nCol ){ |
| 239981 | - int n = sizeof(Fts5Colset); | |
| 240162 | + int n = SZ_FTS5COLSET(1); | |
| 239982 | 240163 | Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&sParse.rc, n); |
| 239983 | 240164 | if( pColset ){ |
| 239984 | 240165 | pColset->nCol = 1; |
| 239985 | 240166 | pColset->aiCol[0] = iCol; |
| 239986 | 240167 | sqlite3Fts5ParseSetColset(&sParse, sParse.pExpr, pColset); |
| @@ -241334,11 +241515,11 @@ | ||
| 241334 | 241515 | Fts5ExprNearset *pRet = 0; |
| 241335 | 241516 | |
| 241336 | 241517 | if( pParse->rc==SQLITE_OK ){ |
| 241337 | 241518 | if( pNear==0 ){ |
| 241338 | 241519 | sqlite3_int64 nByte; |
| 241339 | - nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*); | |
| 241520 | + nByte = SZ_FTS5EXPRNEARSET(SZALLOC+1); | |
| 241340 | 241521 | pRet = sqlite3_malloc64(nByte); |
| 241341 | 241522 | if( pRet==0 ){ |
| 241342 | 241523 | pParse->rc = SQLITE_NOMEM; |
| 241343 | 241524 | }else{ |
| 241344 | 241525 | memset(pRet, 0, (size_t)nByte); |
| @@ -241345,11 +241526,11 @@ | ||
| 241345 | 241526 | } |
| 241346 | 241527 | }else if( (pNear->nPhrase % SZALLOC)==0 ){ |
| 241347 | 241528 | int nNew = pNear->nPhrase + SZALLOC; |
| 241348 | 241529 | sqlite3_int64 nByte; |
| 241349 | 241530 | |
| 241350 | - nByte = sizeof(Fts5ExprNearset) + nNew * sizeof(Fts5ExprPhrase*); | |
| 241531 | + nByte = SZ_FTS5EXPRNEARSET(nNew+1); | |
| 241351 | 241532 | pRet = (Fts5ExprNearset*)sqlite3_realloc64(pNear, nByte); |
| 241352 | 241533 | if( pRet==0 ){ |
| 241353 | 241534 | pParse->rc = SQLITE_NOMEM; |
| 241354 | 241535 | } |
| 241355 | 241536 | }else{ |
| @@ -241436,16 +241617,16 @@ | ||
| 241436 | 241617 | if( pPhrase==0 || (pPhrase->nTerm % SZALLOC)==0 ){ |
| 241437 | 241618 | Fts5ExprPhrase *pNew; |
| 241438 | 241619 | int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0); |
| 241439 | 241620 | |
| 241440 | 241621 | pNew = (Fts5ExprPhrase*)sqlite3_realloc64(pPhrase, |
| 241441 | - sizeof(Fts5ExprPhrase) + sizeof(Fts5ExprTerm) * nNew | |
| 241622 | + SZ_FTS5EXPRPHRASE(nNew+1) | |
| 241442 | 241623 | ); |
| 241443 | 241624 | if( pNew==0 ){ |
| 241444 | 241625 | rc = SQLITE_NOMEM; |
| 241445 | 241626 | }else{ |
| 241446 | - if( pPhrase==0 ) memset(pNew, 0, sizeof(Fts5ExprPhrase)); | |
| 241627 | + if( pPhrase==0 ) memset(pNew, 0, SZ_FTS5EXPRPHRASE(1)); | |
| 241447 | 241628 | pCtx->pPhrase = pPhrase = pNew; |
| 241448 | 241629 | pNew->nTerm = nNew - SZALLOC; |
| 241449 | 241630 | } |
| 241450 | 241631 | } |
| 241451 | 241632 | |
| @@ -241549,11 +241730,11 @@ | ||
| 241549 | 241730 | } |
| 241550 | 241731 | |
| 241551 | 241732 | if( sCtx.pPhrase==0 ){ |
| 241552 | 241733 | /* This happens when parsing a token or quoted phrase that contains |
| 241553 | 241734 | ** no token characters at all. (e.g ... MATCH '""'). */ |
| 241554 | - sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, sizeof(Fts5ExprPhrase)); | |
| 241735 | + sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, SZ_FTS5EXPRPHRASE(1)); | |
| 241555 | 241736 | }else if( sCtx.pPhrase->nTerm ){ |
| 241556 | 241737 | sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = (u8)bPrefix; |
| 241557 | 241738 | } |
| 241558 | 241739 | assert( pParse->apPhrase!=0 ); |
| 241559 | 241740 | pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase; |
| @@ -241584,23 +241765,22 @@ | ||
| 241584 | 241765 | if( rc==SQLITE_OK ){ |
| 241585 | 241766 | pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc, |
| 241586 | 241767 | sizeof(Fts5ExprPhrase*)); |
| 241587 | 241768 | } |
| 241588 | 241769 | if( rc==SQLITE_OK ){ |
| 241589 | - pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc, | |
| 241590 | - sizeof(Fts5ExprNode)); | |
| 241770 | + pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRNODE(1)); | |
| 241591 | 241771 | } |
| 241592 | 241772 | if( rc==SQLITE_OK ){ |
| 241593 | 241773 | pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc, |
| 241594 | - sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*)); | |
| 241774 | + SZ_FTS5EXPRNEARSET(2)); | |
| 241595 | 241775 | } |
| 241596 | 241776 | if( rc==SQLITE_OK && ALWAYS(pOrig!=0) ){ |
| 241597 | 241777 | Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset; |
| 241598 | 241778 | if( pColsetOrig ){ |
| 241599 | 241779 | sqlite3_int64 nByte; |
| 241600 | 241780 | Fts5Colset *pColset; |
| 241601 | - nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int); | |
| 241781 | + nByte = SZ_FTS5COLSET(pColsetOrig->nCol); | |
| 241602 | 241782 | pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte); |
| 241603 | 241783 | if( pColset ){ |
| 241604 | 241784 | memcpy(pColset, pColsetOrig, (size_t)nByte); |
| 241605 | 241785 | } |
| 241606 | 241786 | pNew->pRoot->pNear->pColset = pColset; |
| @@ -241624,11 +241804,11 @@ | ||
| 241624 | 241804 | } |
| 241625 | 241805 | } |
| 241626 | 241806 | }else{ |
| 241627 | 241807 | /* This happens when parsing a token or quoted phrase that contains |
| 241628 | 241808 | ** no token characters at all. (e.g ... MATCH '""'). */ |
| 241629 | - sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase)); | |
| 241809 | + sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRPHRASE(1)); | |
| 241630 | 241810 | } |
| 241631 | 241811 | } |
| 241632 | 241812 | |
| 241633 | 241813 | if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){ |
| 241634 | 241814 | /* All the allocations succeeded. Put the expression object together. */ |
| @@ -241718,11 +241898,11 @@ | ||
| 241718 | 241898 | Fts5Colset *pNew; /* New colset object to return */ |
| 241719 | 241899 | |
| 241720 | 241900 | assert( pParse->rc==SQLITE_OK ); |
| 241721 | 241901 | assert( iCol>=0 && iCol<pParse->pConfig->nCol ); |
| 241722 | 241902 | |
| 241723 | - pNew = sqlite3_realloc64(p, sizeof(Fts5Colset) + sizeof(int)*nCol); | |
| 241903 | + pNew = sqlite3_realloc64(p, SZ_FTS5COLSET(nCol+1)); | |
| 241724 | 241904 | if( pNew==0 ){ |
| 241725 | 241905 | pParse->rc = SQLITE_NOMEM; |
| 241726 | 241906 | }else{ |
| 241727 | 241907 | int *aiCol = pNew->aiCol; |
| 241728 | 241908 | int i, j; |
| @@ -241753,11 +241933,11 @@ | ||
| 241753 | 241933 | static Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse *pParse, Fts5Colset *p){ |
| 241754 | 241934 | Fts5Colset *pRet; |
| 241755 | 241935 | int nCol = pParse->pConfig->nCol; |
| 241756 | 241936 | |
| 241757 | 241937 | pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc, |
| 241758 | - sizeof(Fts5Colset) + sizeof(int)*nCol | |
| 241938 | + SZ_FTS5COLSET(nCol+1) | |
| 241759 | 241939 | ); |
| 241760 | 241940 | if( pRet ){ |
| 241761 | 241941 | int i; |
| 241762 | 241942 | int iOld = 0; |
| 241763 | 241943 | for(i=0; i<nCol; i++){ |
| @@ -241814,11 +241994,11 @@ | ||
| 241814 | 241994 | ** fails, (*pRc) is set to SQLITE_NOMEM and NULL is returned. |
| 241815 | 241995 | */ |
| 241816 | 241996 | static Fts5Colset *fts5CloneColset(int *pRc, Fts5Colset *pOrig){ |
| 241817 | 241997 | Fts5Colset *pRet; |
| 241818 | 241998 | if( pOrig ){ |
| 241819 | - sqlite3_int64 nByte = sizeof(Fts5Colset) + (pOrig->nCol-1) * sizeof(int); | |
| 241999 | + sqlite3_int64 nByte = SZ_FTS5COLSET(pOrig->nCol); | |
| 241820 | 242000 | pRet = (Fts5Colset*)sqlite3Fts5MallocZero(pRc, nByte); |
| 241821 | 242001 | if( pRet ){ |
| 241822 | 242002 | memcpy(pRet, pOrig, (size_t)nByte); |
| 241823 | 242003 | } |
| 241824 | 242004 | }else{ |
| @@ -241982,21 +242162,21 @@ | ||
| 241982 | 242162 | Fts5ExprNode *pRet; |
| 241983 | 242163 | |
| 241984 | 242164 | assert( pNear->nPhrase==1 ); |
| 241985 | 242165 | assert( pParse->bPhraseToAnd ); |
| 241986 | 242166 | |
| 241987 | - nByte = sizeof(Fts5ExprNode) + nTerm*sizeof(Fts5ExprNode*); | |
| 242167 | + nByte = SZ_FTS5EXPRNODE(nTerm+1); | |
| 241988 | 242168 | pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); |
| 241989 | 242169 | if( pRet ){ |
| 241990 | 242170 | pRet->eType = FTS5_AND; |
| 241991 | 242171 | pRet->nChild = nTerm; |
| 241992 | 242172 | pRet->iHeight = 1; |
| 241993 | 242173 | fts5ExprAssignXNext(pRet); |
| 241994 | 242174 | pParse->nPhrase--; |
| 241995 | 242175 | for(ii=0; ii<nTerm; ii++){ |
| 241996 | 242176 | Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero( |
| 241997 | - &pParse->rc, sizeof(Fts5ExprPhrase) | |
| 242177 | + &pParse->rc, SZ_FTS5EXPRPHRASE(1) | |
| 241998 | 242178 | ); |
| 241999 | 242179 | if( pPhrase ){ |
| 242000 | 242180 | if( parseGrowPhraseArray(pParse) ){ |
| 242001 | 242181 | fts5ExprPhraseFree(pPhrase); |
| 242002 | 242182 | }else{ |
| @@ -242061,11 +242241,11 @@ | ||
| 242061 | 242241 | nChild = 2; |
| 242062 | 242242 | if( pLeft->eType==eType ) nChild += pLeft->nChild-1; |
| 242063 | 242243 | if( pRight->eType==eType ) nChild += pRight->nChild-1; |
| 242064 | 242244 | } |
| 242065 | 242245 | |
| 242066 | - nByte = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*(nChild-1); | |
| 242246 | + nByte = SZ_FTS5EXPRNODE(nChild); | |
| 242067 | 242247 | pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); |
| 242068 | 242248 | |
| 242069 | 242249 | if( pRet ){ |
| 242070 | 242250 | pRet->eType = eType; |
| 242071 | 242251 | pRet->pNear = pNear; |
| @@ -242936,11 +243116,11 @@ | ||
| 242936 | 243116 | } |
| 242937 | 243117 | return rc; |
| 242938 | 243118 | } |
| 242939 | 243119 | |
| 242940 | 243120 | /* |
| 242941 | -** Clear the token mappings for all Fts5IndexIter objects mannaged by | |
| 243121 | +** Clear the token mappings for all Fts5IndexIter objects managed by | |
| 242942 | 243122 | ** the expression passed as the only argument. |
| 242943 | 243123 | */ |
| 242944 | 243124 | static void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){ |
| 242945 | 243125 | int ii; |
| 242946 | 243126 | for(ii=0; ii<pExpr->nPhrase; ii++){ |
| @@ -242971,11 +243151,11 @@ | ||
| 242971 | 243151 | |
| 242972 | 243152 | typedef struct Fts5HashEntry Fts5HashEntry; |
| 242973 | 243153 | |
| 242974 | 243154 | /* |
| 242975 | 243155 | ** This file contains the implementation of an in-memory hash table used |
| 242976 | -** to accumuluate "term -> doclist" content before it is flused to a level-0 | |
| 243156 | +** to accumulate "term -> doclist" content before it is flushed to a level-0 | |
| 242977 | 243157 | ** segment. |
| 242978 | 243158 | */ |
| 242979 | 243159 | |
| 242980 | 243160 | |
| 242981 | 243161 | struct Fts5Hash { |
| @@ -243028,11 +243208,11 @@ | ||
| 243028 | 243208 | int iPos; /* Position of last value written */ |
| 243029 | 243209 | i64 iRowid; /* Rowid of last value written */ |
| 243030 | 243210 | }; |
| 243031 | 243211 | |
| 243032 | 243212 | /* |
| 243033 | -** Eqivalent to: | |
| 243213 | +** Equivalent to: | |
| 243034 | 243214 | ** |
| 243035 | 243215 | ** char *fts5EntryKey(Fts5HashEntry *pEntry){ return zKey; } |
| 243036 | 243216 | */ |
| 243037 | 243217 | #define fts5EntryKey(p) ( ((char *)(&(p)[1])) ) |
| 243038 | 243218 | |
| @@ -243964,13 +244144,17 @@ | ||
| 243964 | 244144 | int nRef; /* Object reference count */ |
| 243965 | 244145 | u64 nWriteCounter; /* Total leaves written to level 0 */ |
| 243966 | 244146 | u64 nOriginCntr; /* Origin value for next top-level segment */ |
| 243967 | 244147 | int nSegment; /* Total segments in this structure */ |
| 243968 | 244148 | int nLevel; /* Number of levels in this index */ |
| 243969 | - Fts5StructureLevel aLevel[1]; /* Array of nLevel level objects */ | |
| 244149 | + Fts5StructureLevel aLevel[FLEXARRAY]; /* Array of nLevel level objects */ | |
| 243970 | 244150 | }; |
| 243971 | 244151 | |
| 244152 | +/* Size (in bytes) of an Fts5Structure object holding up to N levels */ | |
| 244153 | +#define SZ_FTS5STRUCTURE(N) \ | |
| 244154 | + (offsetof(Fts5Structure,aLevel) + (N)*sizeof(Fts5StructureLevel)) | |
| 244155 | + | |
| 243972 | 244156 | /* |
| 243973 | 244157 | ** An object of type Fts5SegWriter is used to write to segments. |
| 243974 | 244158 | */ |
| 243975 | 244159 | struct Fts5PageWriter { |
| 243976 | 244160 | int pgno; /* Page number for this page */ |
| @@ -244096,14 +244280,18 @@ | ||
| 244096 | 244280 | |
| 244097 | 244281 | /* |
| 244098 | 244282 | ** Array of tombstone pages. Reference counted. |
| 244099 | 244283 | */ |
| 244100 | 244284 | struct Fts5TombstoneArray { |
| 244101 | - int nRef; /* Number of pointers to this object */ | |
| 244285 | + int nRef; /* Number of pointers to this object */ | |
| 244102 | 244286 | int nTombstone; |
| 244103 | - Fts5Data *apTombstone[1]; /* Array of tombstone pages */ | |
| 244287 | + Fts5Data *apTombstone[FLEXARRAY]; /* Array of tombstone pages */ | |
| 244104 | 244288 | }; |
| 244289 | + | |
| 244290 | +/* Size (in bytes) of an Fts5TombstoneArray holding up to N tombstones */ | |
| 244291 | +#define SZ_FTS5TOMBSTONEARRAY(N) \ | |
| 244292 | + (offsetof(Fts5TombstoneArray,apTombstone)+(N)*sizeof(Fts5Data*)) | |
| 244105 | 244293 | |
| 244106 | 244294 | /* |
| 244107 | 244295 | ** Argument is a pointer to an Fts5Data structure that contains a |
| 244108 | 244296 | ** leaf page. |
| 244109 | 244297 | */ |
| @@ -244169,12 +244357,15 @@ | ||
| 244169 | 244357 | int bRev; /* True to iterate in reverse order */ |
| 244170 | 244358 | u8 bSkipEmpty; /* True to skip deleted entries */ |
| 244171 | 244359 | |
| 244172 | 244360 | i64 iSwitchRowid; /* Firstest rowid of other than aFirst[1] */ |
| 244173 | 244361 | Fts5CResult *aFirst; /* Current merge state (see above) */ |
| 244174 | - Fts5SegIter aSeg[1]; /* Array of segment iterators */ | |
| 244362 | + Fts5SegIter aSeg[FLEXARRAY]; /* Array of segment iterators */ | |
| 244175 | 244363 | }; |
| 244364 | + | |
| 244365 | +/* Size (in bytes) of an Fts5Iter object holding up to N segment iterators */ | |
| 244366 | +#define SZ_FTS5ITER(N) (offsetof(Fts5Iter,aSeg)+(N)*sizeof(Fts5SegIter)) | |
| 244176 | 244367 | |
| 244177 | 244368 | /* |
| 244178 | 244369 | ** An instance of the following type is used to iterate through the contents |
| 244179 | 244370 | ** of a doclist-index record. |
| 244180 | 244371 | ** |
| @@ -244198,12 +244389,16 @@ | ||
| 244198 | 244389 | i64 iRowid; /* First rowid on leaf iLeafPgno */ |
| 244199 | 244390 | }; |
| 244200 | 244391 | struct Fts5DlidxIter { |
| 244201 | 244392 | int nLvl; |
| 244202 | 244393 | int iSegid; |
| 244203 | - Fts5DlidxLvl aLvl[1]; | |
| 244394 | + Fts5DlidxLvl aLvl[FLEXARRAY]; | |
| 244204 | 244395 | }; |
| 244396 | + | |
| 244397 | +/* Size (in bytes) of an Fts5DlidxIter object with up to N levels */ | |
| 244398 | +#define SZ_FTS5DLIDXITER(N) \ | |
| 244399 | + (offsetof(Fts5DlidxIter,aLvl)+(N)*sizeof(Fts5DlidxLvl)) | |
| 244205 | 244400 | |
| 244206 | 244401 | static void fts5PutU16(u8 *aOut, u16 iVal){ |
| 244207 | 244402 | aOut[0] = (iVal>>8); |
| 244208 | 244403 | aOut[1] = (iVal&0xFF); |
| 244209 | 244404 | } |
| @@ -244568,11 +244763,11 @@ | ||
| 244568 | 244763 | ** an error occurs, (*pRc) is set to an SQLite error code before returning. |
| 244569 | 244764 | */ |
| 244570 | 244765 | static void fts5StructureMakeWritable(int *pRc, Fts5Structure **pp){ |
| 244571 | 244766 | Fts5Structure *p = *pp; |
| 244572 | 244767 | if( *pRc==SQLITE_OK && p->nRef>1 ){ |
| 244573 | - i64 nByte = sizeof(Fts5Structure)+(p->nLevel-1)*sizeof(Fts5StructureLevel); | |
| 244768 | + i64 nByte = SZ_FTS5STRUCTURE(p->nLevel); | |
| 244574 | 244769 | Fts5Structure *pNew; |
| 244575 | 244770 | pNew = (Fts5Structure*)sqlite3Fts5MallocZero(pRc, nByte); |
| 244576 | 244771 | if( pNew ){ |
| 244577 | 244772 | int i; |
| 244578 | 244773 | memcpy(pNew, p, nByte); |
| @@ -244642,14 +244837,11 @@ | ||
| 244642 | 244837 | if( nLevel>FTS5_MAX_SEGMENT || nLevel<0 |
| 244643 | 244838 | || nSegment>FTS5_MAX_SEGMENT || nSegment<0 |
| 244644 | 244839 | ){ |
| 244645 | 244840 | return FTS5_CORRUPT; |
| 244646 | 244841 | } |
| 244647 | - nByte = ( | |
| 244648 | - sizeof(Fts5Structure) + /* Main structure */ | |
| 244649 | - sizeof(Fts5StructureLevel) * (nLevel-1) /* aLevel[] array */ | |
| 244650 | - ); | |
| 244842 | + nByte = SZ_FTS5STRUCTURE(nLevel); | |
| 244651 | 244843 | pRet = (Fts5Structure*)sqlite3Fts5MallocZero(&rc, nByte); |
| 244652 | 244844 | |
| 244653 | 244845 | if( pRet ){ |
| 244654 | 244846 | pRet->nRef = 1; |
| 244655 | 244847 | pRet->nLevel = nLevel; |
| @@ -244725,14 +244917,11 @@ | ||
| 244725 | 244917 | fts5StructureMakeWritable(pRc, ppStruct); |
| 244726 | 244918 | assert( (ppStruct!=0 && (*ppStruct)!=0) || (*pRc)!=SQLITE_OK ); |
| 244727 | 244919 | if( *pRc==SQLITE_OK ){ |
| 244728 | 244920 | Fts5Structure *pStruct = *ppStruct; |
| 244729 | 244921 | int nLevel = pStruct->nLevel; |
| 244730 | - sqlite3_int64 nByte = ( | |
| 244731 | - sizeof(Fts5Structure) + /* Main structure */ | |
| 244732 | - sizeof(Fts5StructureLevel) * (nLevel+1) /* aLevel[] array */ | |
| 244733 | - ); | |
| 244922 | + sqlite3_int64 nByte = SZ_FTS5STRUCTURE(nLevel+2); | |
| 244734 | 244923 | |
| 244735 | 244924 | pStruct = sqlite3_realloc64(pStruct, nByte); |
| 244736 | 244925 | if( pStruct ){ |
| 244737 | 244926 | memset(&pStruct->aLevel[nLevel], 0, sizeof(Fts5StructureLevel)); |
| 244738 | 244927 | pStruct->nLevel++; |
| @@ -245267,11 +245456,11 @@ | ||
| 245267 | 245456 | Fts5DlidxIter *pIter = 0; |
| 245268 | 245457 | int i; |
| 245269 | 245458 | int bDone = 0; |
| 245270 | 245459 | |
| 245271 | 245460 | for(i=0; p->rc==SQLITE_OK && bDone==0; i++){ |
| 245272 | - sqlite3_int64 nByte = sizeof(Fts5DlidxIter) + i * sizeof(Fts5DlidxLvl); | |
| 245461 | + sqlite3_int64 nByte = SZ_FTS5DLIDXITER(i+1); | |
| 245273 | 245462 | Fts5DlidxIter *pNew; |
| 245274 | 245463 | |
| 245275 | 245464 | pNew = (Fts5DlidxIter*)sqlite3_realloc64(pIter, nByte); |
| 245276 | 245465 | if( pNew==0 ){ |
| 245277 | 245466 | p->rc = SQLITE_NOMEM; |
| @@ -245485,11 +245674,11 @@ | ||
| 245485 | 245674 | ** leave an error in the Fts5Index object. |
| 245486 | 245675 | */ |
| 245487 | 245676 | static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){ |
| 245488 | 245677 | const int nTomb = pIter->pSeg->nPgTombstone; |
| 245489 | 245678 | if( nTomb>0 ){ |
| 245490 | - int nByte = nTomb * sizeof(Fts5Data*) + sizeof(Fts5TombstoneArray); | |
| 245679 | + int nByte = SZ_FTS5TOMBSTONEARRAY(nTomb+1); | |
| 245491 | 245680 | Fts5TombstoneArray *pNew; |
| 245492 | 245681 | pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte); |
| 245493 | 245682 | if( pNew ){ |
| 245494 | 245683 | pNew->nTombstone = nTomb; |
| 245495 | 245684 | pNew->nRef = 1; |
| @@ -246946,12 +247135,11 @@ | ||
| 246946 | 247135 | Fts5Iter *pNew; |
| 246947 | 247136 | i64 nSlot; /* Power of two >= nSeg */ |
| 246948 | 247137 | |
| 246949 | 247138 | for(nSlot=2; nSlot<nSeg; nSlot=nSlot*2); |
| 246950 | 247139 | pNew = fts5IdxMalloc(p, |
| 246951 | - sizeof(Fts5Iter) + /* pNew */ | |
| 246952 | - sizeof(Fts5SegIter) * (nSlot-1) + /* pNew->aSeg[] */ | |
| 247140 | + SZ_FTS5ITER(nSlot) + /* pNew + pNew->aSeg[] */ | |
| 246953 | 247141 | sizeof(Fts5CResult) * nSlot /* pNew->aFirst[] */ |
| 246954 | 247142 | ); |
| 246955 | 247143 | if( pNew ){ |
| 246956 | 247144 | pNew->nSeg = nSlot; |
| 246957 | 247145 | pNew->aFirst = (Fts5CResult*)&pNew->aSeg[nSlot]; |
| @@ -249313,11 +249501,11 @@ | ||
| 249313 | 249501 | static Fts5Structure *fts5IndexOptimizeStruct( |
| 249314 | 249502 | Fts5Index *p, |
| 249315 | 249503 | Fts5Structure *pStruct |
| 249316 | 249504 | ){ |
| 249317 | 249505 | Fts5Structure *pNew = 0; |
| 249318 | - sqlite3_int64 nByte = sizeof(Fts5Structure); | |
| 249506 | + sqlite3_int64 nByte = SZ_FTS5STRUCTURE(1); | |
| 249319 | 249507 | int nSeg = pStruct->nSegment; |
| 249320 | 249508 | int i; |
| 249321 | 249509 | |
| 249322 | 249510 | /* Figure out if this structure requires optimization. A structure does |
| 249323 | 249511 | ** not require optimization if either: |
| @@ -249343,10 +249531,11 @@ | ||
| 249343 | 249531 | } |
| 249344 | 249532 | assert( pStruct->aLevel[i].nMerge<=nThis ); |
| 249345 | 249533 | } |
| 249346 | 249534 | |
| 249347 | 249535 | nByte += (((i64)pStruct->nLevel)+1) * sizeof(Fts5StructureLevel); |
| 249536 | + assert( nByte==SZ_FTS5STRUCTURE(pStruct->nLevel+2) ); | |
| 249348 | 249537 | pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte); |
| 249349 | 249538 | |
| 249350 | 249539 | if( pNew ){ |
| 249351 | 249540 | Fts5StructureLevel *pLvl; |
| 249352 | 249541 | nByte = nSeg * sizeof(Fts5StructureSegment); |
| @@ -249919,12 +250108,16 @@ | ||
| 249919 | 250108 | /* The following are used for other full-token tokendata queries only. */ |
| 249920 | 250109 | int nIter; |
| 249921 | 250110 | int nIterAlloc; |
| 249922 | 250111 | Fts5PoslistReader *aPoslistReader; |
| 249923 | 250112 | int *aPoslistToIter; |
| 249924 | - Fts5Iter *apIter[1]; | |
| 250113 | + Fts5Iter *apIter[FLEXARRAY]; | |
| 249925 | 250114 | }; |
| 250115 | + | |
| 250116 | +/* Size in bytes of an Fts5TokenDataIter object holding up to N iterators */ | |
| 250117 | +#define SZ_FTS5TOKENDATAITER(N) \ | |
| 250118 | + (offsetof(Fts5TokenDataIter,apIter) + (N)*sizeof(Fts5Iter)) | |
| 249926 | 250119 | |
| 249927 | 250120 | /* |
| 249928 | 250121 | ** The two input arrays - a1[] and a2[] - are in sorted order. This function |
| 249929 | 250122 | ** merges the two arrays together and writes the result to output array |
| 249930 | 250123 | ** aOut[]. aOut[] is guaranteed to be large enough to hold the result. |
| @@ -249993,11 +250186,11 @@ | ||
| 249993 | 250186 | } |
| 249994 | 250187 | |
| 249995 | 250188 | /* |
| 249996 | 250189 | ** Sort the contents of the pT->aMap[] array. |
| 249997 | 250190 | ** |
| 249998 | -** The sorting algorithm requries a malloc(). If this fails, an error code | |
| 250191 | +** The sorting algorithm requires a malloc(). If this fails, an error code | |
| 249999 | 250192 | ** is left in Fts5Index.rc before returning. |
| 250000 | 250193 | */ |
| 250001 | 250194 | static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){ |
| 250002 | 250195 | Fts5TokenDataMap *aTmp = 0; |
| 250003 | 250196 | int nByte = pT->nMap * sizeof(Fts5TokenDataMap); |
| @@ -250184,11 +250377,11 @@ | ||
| 250184 | 250377 | if( iIdx==0 |
| 250185 | 250378 | && p->pConfig->eDetail==FTS5_DETAIL_FULL |
| 250186 | 250379 | && p->pConfig->bPrefixInsttoken |
| 250187 | 250380 | ){ |
| 250188 | 250381 | s.pTokendata = &s2; |
| 250189 | - s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, sizeof(*s2.pT)); | |
| 250382 | + s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, SZ_FTS5TOKENDATAITER(1)); | |
| 250190 | 250383 | } |
| 250191 | 250384 | |
| 250192 | 250385 | if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){ |
| 250193 | 250386 | s.xMerge = fts5MergeRowidLists; |
| 250194 | 250387 | s.xAppend = fts5AppendRowid; |
| @@ -250312,19 +250505,21 @@ | ||
| 250312 | 250505 | ** The %_data table is completely empty when this function is called. This |
| 250313 | 250506 | ** function populates it with the initial structure objects for each index, |
| 250314 | 250507 | ** and the initial version of the "averages" record (a zero-byte blob). |
| 250315 | 250508 | */ |
| 250316 | 250509 | static int sqlite3Fts5IndexReinit(Fts5Index *p){ |
| 250317 | - Fts5Structure s; | |
| 250510 | + Fts5Structure *pTmp; | |
| 250511 | + u8 tmpSpace[SZ_FTS5STRUCTURE(1)]; | |
| 250318 | 250512 | fts5StructureInvalidate(p); |
| 250319 | 250513 | fts5IndexDiscardData(p); |
| 250320 | - memset(&s, 0, sizeof(Fts5Structure)); | |
| 250514 | + pTmp = (Fts5Structure*)tmpSpace; | |
| 250515 | + memset(pTmp, 0, SZ_FTS5STRUCTURE(1)); | |
| 250321 | 250516 | if( p->pConfig->bContentlessDelete ){ |
| 250322 | - s.nOriginCntr = 1; | |
| 250517 | + pTmp->nOriginCntr = 1; | |
| 250323 | 250518 | } |
| 250324 | 250519 | fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0); |
| 250325 | - fts5StructureWrite(p, &s); | |
| 250520 | + fts5StructureWrite(p, pTmp); | |
| 250326 | 250521 | return fts5IndexReturn(p); |
| 250327 | 250522 | } |
| 250328 | 250523 | |
| 250329 | 250524 | /* |
| 250330 | 250525 | ** Open a new Fts5Index handle. If the bCreate argument is true, create |
| @@ -250528,11 +250723,11 @@ | ||
| 250528 | 250723 | Fts5TokenDataIter *pRet = pIn; |
| 250529 | 250724 | |
| 250530 | 250725 | if( p->rc==SQLITE_OK ){ |
| 250531 | 250726 | if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){ |
| 250532 | 250727 | int nAlloc = pIn ? pIn->nIterAlloc*2 : 16; |
| 250533 | - int nByte = nAlloc * sizeof(Fts5Iter*) + sizeof(Fts5TokenDataIter); | |
| 250728 | + int nByte = SZ_FTS5TOKENDATAITER(nAlloc+1); | |
| 250534 | 250729 | Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte); |
| 250535 | 250730 | |
| 250536 | 250731 | if( pNew==0 ){ |
| 250537 | 250732 | p->rc = SQLITE_NOMEM; |
| 250538 | 250733 | }else{ |
| @@ -251044,11 +251239,12 @@ | ||
| 251044 | 251239 | |
| 251045 | 251240 | memset(&ctx, 0, sizeof(ctx)); |
| 251046 | 251241 | |
| 251047 | 251242 | fts5BufferGrow(&p->rc, &token, nToken+1); |
| 251048 | 251243 | assert( token.p!=0 || p->rc!=SQLITE_OK ); |
| 251049 | - ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*ctx.pT)); | |
| 251244 | + ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, | |
| 251245 | + SZ_FTS5TOKENDATAITER(1)); | |
| 251050 | 251246 | |
| 251051 | 251247 | if( p->rc==SQLITE_OK ){ |
| 251052 | 251248 | |
| 251053 | 251249 | /* Fill in the token prefix to search for */ |
| 251054 | 251250 | token.p[0] = FTS5_MAIN_PREFIX; |
| @@ -251175,11 +251371,12 @@ | ||
| 251175 | 251371 | assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL ); |
| 251176 | 251372 | assert( pIter->pTokenDataIter || pIter->nSeg>0 ); |
| 251177 | 251373 | if( pIter->nSeg>0 ){ |
| 251178 | 251374 | /* This is a prefix term iterator. */ |
| 251179 | 251375 | if( pT==0 ){ |
| 251180 | - pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*pT)); | |
| 251376 | + pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, | |
| 251377 | + SZ_FTS5TOKENDATAITER(1)); | |
| 251181 | 251378 | pIter->pTokenDataIter = pT; |
| 251182 | 251379 | } |
| 251183 | 251380 | if( pT ){ |
| 251184 | 251381 | fts5TokendataIterAppendMap(p, pT, pT->terms.n, nToken, iRowid, iPos); |
| 251185 | 251382 | fts5BufferAppendBlob(&p->rc, &pT->terms, nToken, (const u8*)pToken); |
| @@ -252209,11 +252406,11 @@ | ||
| 252209 | 252406 | } |
| 252210 | 252407 | #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ |
| 252211 | 252408 | |
| 252212 | 252409 | #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) |
| 252213 | 252410 | static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){ |
| 252214 | - int iSegid, iHeight, iPgno, bDlidx, bTomb; /* Rowid compenents */ | |
| 252411 | + int iSegid, iHeight, iPgno, bDlidx, bTomb; /* Rowid components */ | |
| 252215 | 252412 | fts5DecodeRowid(iKey, &bTomb, &iSegid, &bDlidx, &iHeight, &iPgno); |
| 252216 | 252413 | |
| 252217 | 252414 | if( iSegid==0 ){ |
| 252218 | 252415 | if( iKey==FTS5_AVERAGES_ROWID ){ |
| 252219 | 252416 | sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{averages} "); |
| @@ -253170,13 +253367,15 @@ | ||
| 253170 | 253367 | struct Fts5Sorter { |
| 253171 | 253368 | sqlite3_stmt *pStmt; |
| 253172 | 253369 | i64 iRowid; /* Current rowid */ |
| 253173 | 253370 | const u8 *aPoslist; /* Position lists for current row */ |
| 253174 | 253371 | int nIdx; /* Number of entries in aIdx[] */ |
| 253175 | - int aIdx[1]; /* Offsets into aPoslist for current row */ | |
| 253372 | + int aIdx[FLEXARRAY]; /* Offsets into aPoslist for current row */ | |
| 253176 | 253373 | }; |
| 253177 | 253374 | |
| 253375 | +/* Size (int bytes) of an Fts5Sorter object with N indexes */ | |
| 253376 | +#define SZ_FTS5SORTER(N) (offsetof(Fts5Sorter,nIdx)+((N+2)/2)*sizeof(i64)) | |
| 253178 | 253377 | |
| 253179 | 253378 | /* |
| 253180 | 253379 | ** Virtual-table cursor object. |
| 253181 | 253380 | ** |
| 253182 | 253381 | ** iSpecial: |
| @@ -254050,11 +254249,11 @@ | ||
| 254050 | 254249 | int rc; |
| 254051 | 254250 | const char *zRank = pCsr->zRank; |
| 254052 | 254251 | const char *zRankArgs = pCsr->zRankArgs; |
| 254053 | 254252 | |
| 254054 | 254253 | nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); |
| 254055 | - nByte = sizeof(Fts5Sorter) + sizeof(int) * (nPhrase-1); | |
| 254254 | + nByte = SZ_FTS5SORTER(nPhrase); | |
| 254056 | 254255 | pSorter = (Fts5Sorter*)sqlite3_malloc64(nByte); |
| 254057 | 254256 | if( pSorter==0 ) return SQLITE_NOMEM; |
| 254058 | 254257 | memset(pSorter, 0, (size_t)nByte); |
| 254059 | 254258 | pSorter->nIdx = nPhrase; |
| 254060 | 254259 | |
| @@ -256576,11 +256775,11 @@ | ||
| 256576 | 256775 | int nArg, /* Number of args */ |
| 256577 | 256776 | sqlite3_value **apUnused /* Function arguments */ |
| 256578 | 256777 | ){ |
| 256579 | 256778 | assert( nArg==0 ); |
| 256580 | 256779 | UNUSED_PARAM2(nArg, apUnused); |
| 256581 | - sqlite3_result_text(pCtx, "fts5: 2025-02-25 16:39:51 6f0b6d95db17e69ac7e46a39f52770291ac4cfe43eea09add224946a6e11f04e", -1, SQLITE_TRANSIENT); | |
| 256780 | + sqlite3_result_text(pCtx, "fts5: 2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185", -1, SQLITE_TRANSIENT); | |
| 256582 | 256781 | } |
| 256583 | 256782 | |
| 256584 | 256783 | /* |
| 256585 | 256784 | ** Implementation of fts5_locale(LOCALE, TEXT) function. |
| 256586 | 256785 | ** |
| 256587 | 256786 |
| --- extsrc/sqlite3.c | |
| +++ extsrc/sqlite3.c | |
| @@ -16,11 +16,11 @@ | |
| 16 | ** if you want a wrapper to interface SQLite with your choice of programming |
| 17 | ** language. The code for the "sqlite3" command-line shell is also in a |
| 18 | ** separate file. This file contains only code for the core SQLite library. |
| 19 | ** |
| 20 | ** The content in this amalgamation comes from Fossil check-in |
| 21 | ** e6784af6d50f715338ae3218fc8ba1b89488 with changes in files: |
| 22 | ** |
| 23 | ** |
| 24 | */ |
| 25 | #ifndef SQLITE_AMALGAMATION |
| 26 | #define SQLITE_CORE 1 |
| @@ -465,11 +465,11 @@ | |
| 465 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 466 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 467 | */ |
| 468 | #define SQLITE_VERSION "3.50.0" |
| 469 | #define SQLITE_VERSION_NUMBER 3050000 |
| 470 | #define SQLITE_SOURCE_ID "2025-02-25 18:10:47 e6784af6d50f715338ae3218fc8ba1b894883c27d797f0b7fd2625cac17d9cd7" |
| 471 | |
| 472 | /* |
| 473 | ** CAPI3REF: Run-Time Library Version Numbers |
| 474 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 475 | ** |
| @@ -5492,11 +5492,11 @@ | |
| 5492 | ** For all versions of SQLite up to and including 3.6.23.1, a call to |
| 5493 | ** [sqlite3_reset()] was required after sqlite3_step() returned anything |
| 5494 | ** other than [SQLITE_ROW] before any subsequent invocation of |
| 5495 | ** sqlite3_step(). Failure to reset the prepared statement using |
| 5496 | ** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from |
| 5497 | ** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1], |
| 5498 | ** sqlite3_step() began |
| 5499 | ** calling [sqlite3_reset()] automatically in this circumstance rather |
| 5500 | ** than returning [SQLITE_MISUSE]. This is not considered a compatibility |
| 5501 | ** break because any application that ever receives an SQLITE_MISUSE error |
| 5502 | ** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option |
| @@ -7388,10 +7388,12 @@ | |
| 7388 | ** ^Any callback set by a previous call to this function |
| 7389 | ** for the same database connection is overridden. |
| 7390 | ** |
| 7391 | ** ^The second argument is a pointer to the function to invoke when a |
| 7392 | ** row is updated, inserted or deleted in a rowid table. |
| 7393 | ** ^The first argument to the callback is a copy of the third argument |
| 7394 | ** to sqlite3_update_hook(). |
| 7395 | ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], |
| 7396 | ** or [SQLITE_UPDATE], depending on the operation that caused the callback |
| 7397 | ** to be invoked. |
| @@ -15170,11 +15172,21 @@ | |
| 15170 | /* |
| 15171 | ** GCC does not define the offsetof() macro so we'll have to do it |
| 15172 | ** ourselves. |
| 15173 | */ |
| 15174 | #ifndef offsetof |
| 15175 | #define offsetof(STRUCTURE,FIELD) ((int)((char*)&((STRUCTURE*)0)->FIELD)) |
| 15176 | #endif |
| 15177 | |
| 15178 | /* |
| 15179 | ** Macros to compute minimum and maximum of two numbers. |
| 15180 | */ |
| @@ -17405,12 +17417,12 @@ | |
| 17405 | SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*); |
| 17406 | #ifdef SQLITE_ENABLE_BYTECODE_VTAB |
| 17407 | SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*); |
| 17408 | #endif |
| 17409 | |
| 17410 | /* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on |
| 17411 | ** each VDBE opcode. |
| 17412 | ** |
| 17413 | ** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op |
| 17414 | ** comments in VDBE programs that show key decision points in the code |
| 17415 | ** generator. |
| 17416 | */ |
| @@ -18945,12 +18957,16 @@ | |
| 18945 | u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */ |
| 18946 | Trigger *apTrigger[2];/* Triggers for aAction[] actions */ |
| 18947 | struct sColMap { /* Mapping of columns in pFrom to columns in zTo */ |
| 18948 | int iFrom; /* Index of column in pFrom */ |
| 18949 | char *zCol; /* Name of column in zTo. If NULL use PRIMARY KEY */ |
| 18950 | } aCol[1]; /* One entry for each of nCol columns */ |
| 18951 | }; |
| 18952 | |
| 18953 | /* |
| 18954 | ** SQLite supports many different ways to resolve a constraint |
| 18955 | ** error. ROLLBACK processing means that a constraint violation |
| 18956 | ** causes the operation in process to fail and for the current transaction |
| @@ -19009,13 +19025,16 @@ | |
| 19009 | u8 enc; /* Text encoding - one of the SQLITE_UTF* values */ |
| 19010 | u16 nKeyField; /* Number of key columns in the index */ |
| 19011 | u16 nAllField; /* Total columns, including key plus others */ |
| 19012 | sqlite3 *db; /* The database connection */ |
| 19013 | u8 *aSortFlags; /* Sort order for each column. */ |
| 19014 | CollSeq *aColl[1]; /* Collating sequence for each term of the key */ |
| 19015 | }; |
| 19016 | |
| 19017 | /* |
| 19018 | ** Allowed bit values for entries in the KeyInfo.aSortFlags[] array. |
| 19019 | */ |
| 19020 | #define KEYINFO_ORDER_DESC 0x01 /* DESC sort order */ |
| 19021 | #define KEYINFO_ORDER_BIGNULL 0x02 /* NULL is larger than any other value */ |
| @@ -19584,12 +19603,17 @@ | |
| 19584 | u16 iAlias; /* Index into Parse.aAlias[] for zName */ |
| 19585 | } x; |
| 19586 | int iConstExprReg; /* Register in which Expr value is cached. Used only |
| 19587 | ** by Parse.pConstExpr */ |
| 19588 | } u; |
| 19589 | } a[1]; /* One slot for each expression in the list */ |
| 19590 | }; |
| 19591 | |
| 19592 | /* |
| 19593 | ** Allowed values for Expr.a.eEName |
| 19594 | */ |
| 19595 | #define ENAME_NAME 0 /* The AS clause of a result set */ |
| @@ -19614,13 +19638,16 @@ | |
| 19614 | */ |
| 19615 | struct IdList { |
| 19616 | int nId; /* Number of identifiers on the list */ |
| 19617 | struct IdList_item { |
| 19618 | char *zName; /* Name of the identifier */ |
| 19619 | } a[1]; |
| 19620 | }; |
| 19621 | |
| 19622 | /* |
| 19623 | ** Allowed values for IdList.eType, which determines which value of the a.u4 |
| 19624 | ** is valid. |
| 19625 | */ |
| 19626 | #define EU4_NONE 0 /* Does not use IdList.a.u4 */ |
| @@ -19736,14 +19763,22 @@ | |
| 19736 | ** is used to hold the FROM clause of a SELECT statement. SrcList also |
| 19737 | ** represents the target tables for DELETE, INSERT, and UPDATE statements. |
| 19738 | ** |
| 19739 | */ |
| 19740 | struct SrcList { |
| 19741 | int nSrc; /* Number of tables or subqueries in the FROM clause */ |
| 19742 | u32 nAlloc; /* Number of entries allocated in a[] below */ |
| 19743 | SrcItem a[1]; /* One entry for each identifier on the list */ |
| 19744 | }; |
| 19745 | |
| 19746 | /* |
| 19747 | ** Permitted values of the SrcList.a.jointype field |
| 19748 | */ |
| 19749 | #define JT_INNER 0x01 /* Any kind of inner or cross join */ |
| @@ -20804,12 +20839,16 @@ | |
| 20804 | */ |
| 20805 | struct With { |
| 20806 | int nCte; /* Number of CTEs in the WITH clause */ |
| 20807 | int bView; /* Belongs to the outermost Select of a view */ |
| 20808 | With *pOuter; /* Containing WITH clause, or NULL */ |
| 20809 | Cte a[1]; /* For each CTE in the WITH clause.... */ |
| 20810 | }; |
| 20811 | |
| 20812 | /* |
| 20813 | ** The Cte object is not guaranteed to persist for the entire duration |
| 20814 | ** of code generation. (The query flattener or other parser tree |
| 20815 | ** edits might delete it.) The following object records information |
| @@ -20835,12 +20874,16 @@ | |
| 20835 | */ |
| 20836 | struct DbClientData { |
| 20837 | DbClientData *pNext; /* Next in a linked list */ |
| 20838 | void *pData; /* The data */ |
| 20839 | void (*xDestructor)(void*); /* Destructor. Might be NULL */ |
| 20840 | char zName[1]; /* Name of this client data. MUST BE LAST */ |
| 20841 | }; |
| 20842 | |
| 20843 | #ifdef SQLITE_DEBUG |
| 20844 | /* |
| 20845 | ** An instance of the TreeView object is used for printing the content of |
| 20846 | ** data structures on sqlite3DebugPrintf() using a tree-like view. |
| @@ -22673,10 +22716,13 @@ | |
| 22673 | "EXTRA_IFNULLROW", |
| 22674 | #endif |
| 22675 | #ifdef SQLITE_EXTRA_INIT |
| 22676 | "EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT), |
| 22677 | #endif |
| 22678 | #ifdef SQLITE_EXTRA_SHUTDOWN |
| 22679 | "EXTRA_SHUTDOWN=" CTIMEOPT_VAL(SQLITE_EXTRA_SHUTDOWN), |
| 22680 | #endif |
| 22681 | #ifdef SQLITE_FTS3_MAX_EXPR_DEPTH |
| 22682 | "FTS3_MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_FTS3_MAX_EXPR_DEPTH), |
| @@ -23657,16 +23703,23 @@ | |
| 23657 | #ifdef SQLITE_ENABLE_COLUMN_USED_MASK |
| 23658 | u64 maskUsed; /* Mask of columns used by this cursor */ |
| 23659 | #endif |
| 23660 | VdbeTxtBlbCache *pCache; /* Cache of large TEXT or BLOB values */ |
| 23661 | |
| 23662 | /* 2*nField extra array elements allocated for aType[], beyond the one |
| 23663 | ** static element declared in the structure. nField total array slots for |
| 23664 | ** aType[] and nField+1 array slots for aOffset[] */ |
| 23665 | u32 aType[1]; /* Type values record decode. MUST BE LAST */ |
| 23666 | }; |
| 23667 | |
| 23668 | /* Return true if P is a null-only cursor |
| 23669 | */ |
| 23670 | #define IsNullCursor(P) \ |
| 23671 | ((P)->eCurType==CURTYPE_PSEUDO && (P)->nullRow && (P)->seekResult==0) |
| 23672 | |
| @@ -23919,13 +23972,20 @@ | |
| 23919 | int iOp; /* Instruction number of OP_Function */ |
| 23920 | int isError; /* Error code returned by the function. */ |
| 23921 | u8 enc; /* Encoding to use for results */ |
| 23922 | u8 skipFlag; /* Skip accumulator loading if true */ |
| 23923 | u16 argc; /* Number of arguments */ |
| 23924 | sqlite3_value *argv[1]; /* Argument set */ |
| 23925 | }; |
| 23926 | |
| 23927 | |
| 23928 | /* The ScanStatus object holds a single value for the |
| 23929 | ** sqlite3_stmt_scanstatus() interface. |
| 23930 | ** |
| 23931 | ** aAddrRange[]: |
| @@ -24055,11 +24115,11 @@ | |
| 24055 | struct PreUpdate { |
| 24056 | Vdbe *v; |
| 24057 | VdbeCursor *pCsr; /* Cursor to read old values from */ |
| 24058 | int op; /* One of SQLITE_INSERT, UPDATE, DELETE */ |
| 24059 | u8 *aRecord; /* old.* database record */ |
| 24060 | KeyInfo keyinfo; |
| 24061 | UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */ |
| 24062 | UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */ |
| 24063 | int iNewReg; /* Register for new.* values */ |
| 24064 | int iBlobWrite; /* Value returned by preupdate_blobwrite() */ |
| 24065 | i64 iKey1; /* First key value passed to hook */ |
| @@ -24067,10 +24127,11 @@ | |
| 24067 | Mem oldipk; /* Memory cell holding "old" IPK value */ |
| 24068 | Mem *aNew; /* Array of new.* values */ |
| 24069 | Table *pTab; /* Schema object being updated */ |
| 24070 | Index *pPk; /* PK index if pTab is WITHOUT ROWID */ |
| 24071 | sqlite3_value **apDflt; /* Array of default values, if required */ |
| 24072 | }; |
| 24073 | |
| 24074 | /* |
| 24075 | ** An instance of this object is used to pass an vector of values into |
| 24076 | ** OP_VFilter, the xFilter method of a virtual table. The vector is the |
| @@ -26000,11 +26061,11 @@ | |
| 26000 | ** Return the number of days after the most recent Sunday. |
| 26001 | ** |
| 26002 | ** In other words, return the day of the week according |
| 26003 | ** to this code: |
| 26004 | ** |
| 26005 | ** 0=Sunday, 1=Monday, 2=Tues, ..., 6=Saturday |
| 26006 | */ |
| 26007 | static int daysAfterSunday(DateTime *pDate){ |
| 26008 | assert( pDate->validJD ); |
| 26009 | return (int)((pDate->iJD+129600000)/86400000) % 7; |
| 26010 | } |
| @@ -32378,11 +32439,11 @@ | |
| 32378 | u32 nBack = 0; |
| 32379 | u32 nCtrl = 0; |
| 32380 | for(k=0; k<i; k++){ |
| 32381 | if( escarg[k]=='\\' ){ |
| 32382 | nBack++; |
| 32383 | }else if( escarg[k]<=0x1f ){ |
| 32384 | nCtrl++; |
| 32385 | } |
| 32386 | } |
| 32387 | if( nCtrl || xtype==etESCAPE_q ){ |
| 32388 | n += nBack + 5*nCtrl; |
| @@ -32416,11 +32477,11 @@ | |
| 32416 | bufpt[j++] = ch = escarg[i]; |
| 32417 | if( ch==q ){ |
| 32418 | bufpt[j++] = ch; |
| 32419 | }else if( ch=='\\' ){ |
| 32420 | bufpt[j++] = '\\'; |
| 32421 | }else if( ch<=0x1f ){ |
| 32422 | bufpt[j-1] = '\\'; |
| 32423 | bufpt[j++] = 'u'; |
| 32424 | bufpt[j++] = '0'; |
| 32425 | bufpt[j++] = '0'; |
| 32426 | bufpt[j++] = ch>=0x10 ? '1' : '0'; |
| @@ -37067,11 +37128,11 @@ | |
| 37067 | return 0; |
| 37068 | #endif |
| 37069 | } |
| 37070 | |
| 37071 | /* |
| 37072 | ** Compute the absolute value of a 32-bit signed integer, of possible. Or |
| 37073 | ** if the integer has a value of -2147483648, return +2147483647 |
| 37074 | */ |
| 37075 | SQLITE_PRIVATE int sqlite3AbsInt32(int x){ |
| 37076 | if( x>=0 ) return x; |
| 37077 | if( x==(int)0x80000000 ) return 0x7fffffff; |
| @@ -45614,11 +45675,11 @@ | |
| 45614 | sp.tv_sec = microseconds / 1000000; |
| 45615 | sp.tv_nsec = (microseconds % 1000000) * 1000; |
| 45616 | |
| 45617 | /* Almost all modern unix systems support nanosleep(). But if you are |
| 45618 | ** compiling for one of the rare exceptions, you can use |
| 45619 | ** -DHAVE_NANOSLEEP=0 (perhaps in conjuction with -DHAVE_USLEEP if |
| 45620 | ** usleep() is available) in order to bypass the use of nanosleep() */ |
| 45621 | nanosleep(&sp, NULL); |
| 45622 | |
| 45623 | UNUSED_PARAMETER(NotUsed); |
| 45624 | return microseconds; |
| @@ -56047,14 +56108,10 @@ | |
| 56047 | void *pStart, *pEnd; /* Bounds of global page cache memory */ |
| 56048 | /* Above requires no mutex. Use mutex below for variable that follow. */ |
| 56049 | sqlite3_mutex *mutex; /* Mutex for accessing the following: */ |
| 56050 | PgFreeslot *pFree; /* Free page blocks */ |
| 56051 | int nFreeSlot; /* Number of unused pcache slots */ |
| 56052 | /* The following value requires a mutex to change. We skip the mutex on |
| 56053 | ** reading because (1) most platforms read a 32-bit integer atomically and |
| 56054 | ** (2) even if an incorrect value is read, no great harm is done since this |
| 56055 | ** is really just an optimization. */ |
| 56056 | int bUnderPressure; /* True if low on PAGECACHE memory */ |
| 56057 | } pcache1_g; |
| 56058 | |
| 56059 | /* |
| 56060 | ** All code in this file should access the global structure above via the |
| @@ -56098,11 +56155,11 @@ | |
| 56098 | pcache1.szSlot = sz; |
| 56099 | pcache1.nSlot = pcache1.nFreeSlot = n; |
| 56100 | pcache1.nReserve = n>90 ? 10 : (n/10 + 1); |
| 56101 | pcache1.pStart = pBuf; |
| 56102 | pcache1.pFree = 0; |
| 56103 | pcache1.bUnderPressure = 0; |
| 56104 | while( n-- ){ |
| 56105 | p = (PgFreeslot*)pBuf; |
| 56106 | p->pNext = pcache1.pFree; |
| 56107 | pcache1.pFree = p; |
| 56108 | pBuf = (void*)&((char*)pBuf)[sz]; |
| @@ -56166,11 +56223,11 @@ | |
| 56166 | sqlite3_mutex_enter(pcache1.mutex); |
| 56167 | p = (PgHdr1 *)pcache1.pFree; |
| 56168 | if( p ){ |
| 56169 | pcache1.pFree = pcache1.pFree->pNext; |
| 56170 | pcache1.nFreeSlot--; |
| 56171 | pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve; |
| 56172 | assert( pcache1.nFreeSlot>=0 ); |
| 56173 | sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte); |
| 56174 | sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1); |
| 56175 | } |
| 56176 | sqlite3_mutex_leave(pcache1.mutex); |
| @@ -56205,11 +56262,11 @@ | |
| 56205 | sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1); |
| 56206 | pSlot = (PgFreeslot*)p; |
| 56207 | pSlot->pNext = pcache1.pFree; |
| 56208 | pcache1.pFree = pSlot; |
| 56209 | pcache1.nFreeSlot++; |
| 56210 | pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve; |
| 56211 | assert( pcache1.nFreeSlot<=pcache1.nSlot ); |
| 56212 | sqlite3_mutex_leave(pcache1.mutex); |
| 56213 | }else{ |
| 56214 | assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) ); |
| 56215 | sqlite3MemdebugSetType(p, MEMTYPE_HEAP); |
| @@ -56336,11 +56393,11 @@ | |
| 56336 | ** allocating a new page cache entry in order to avoid stressing |
| 56337 | ** the heap even further. |
| 56338 | */ |
| 56339 | static int pcache1UnderMemoryPressure(PCache1 *pCache){ |
| 56340 | if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){ |
| 56341 | return pcache1.bUnderPressure; |
| 56342 | }else{ |
| 56343 | return sqlite3HeapNearlyFull(); |
| 56344 | } |
| 56345 | } |
| 56346 | |
| @@ -66177,12 +66234,16 @@ | |
| 66177 | int iNext; /* Next slot in aIndex[] not yet returned */ |
| 66178 | ht_slot *aIndex; /* i0, i1, i2... such that aPgno[iN] ascend */ |
| 66179 | u32 *aPgno; /* Array of page numbers. */ |
| 66180 | int nEntry; /* Nr. of entries in aPgno[] and aIndex[] */ |
| 66181 | int iZero; /* Frame number associated with aPgno[0] */ |
| 66182 | } aSegment[1]; /* One for every 32KB page in the wal-index */ |
| 66183 | }; |
| 66184 | |
| 66185 | /* |
| 66186 | ** Define the parameters of the hash tables in the wal-index file. There |
| 66187 | ** is a hash-table following every HASHTABLE_NPAGE page numbers in the |
| 66188 | ** wal-index. |
| @@ -67540,12 +67601,11 @@ | |
| 67540 | assert( pWal->ckptLock && pWal->hdr.mxFrame>0 ); |
| 67541 | iLast = pWal->hdr.mxFrame; |
| 67542 | |
| 67543 | /* Allocate space for the WalIterator object. */ |
| 67544 | nSegment = walFramePage(iLast) + 1; |
| 67545 | nByte = sizeof(WalIterator) |
| 67546 | + (nSegment-1)*sizeof(struct WalSegment) |
| 67547 | + iLast*sizeof(ht_slot); |
| 67548 | p = (WalIterator *)sqlite3_malloc64(nByte |
| 67549 | + sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast) |
| 67550 | ); |
| 67551 | if( !p ){ |
| @@ -86029,16 +86089,14 @@ | |
| 86029 | int nArg, /* Number of argument */ |
| 86030 | const FuncDef *pFunc, /* The function to be invoked */ |
| 86031 | int eCallCtx /* Calling context */ |
| 86032 | ){ |
| 86033 | Vdbe *v = pParse->pVdbe; |
| 86034 | int nByte; |
| 86035 | int addr; |
| 86036 | sqlite3_context *pCtx; |
| 86037 | assert( v ); |
| 86038 | nByte = sizeof(*pCtx) + (nArg-1)*sizeof(sqlite3_value*); |
| 86039 | pCtx = sqlite3DbMallocRawNN(pParse->db, nByte); |
| 86040 | if( pCtx==0 ){ |
| 86041 | assert( pParse->db->mallocFailed ); |
| 86042 | freeEphemeralFunction(pParse->db, (FuncDef*)pFunc); |
| 86043 | return 0; |
| 86044 | } |
| @@ -91110,25 +91168,26 @@ | |
| 91110 | |
| 91111 | preupdate.v = v; |
| 91112 | preupdate.pCsr = pCsr; |
| 91113 | preupdate.op = op; |
| 91114 | preupdate.iNewReg = iReg; |
| 91115 | preupdate.keyinfo.db = db; |
| 91116 | preupdate.keyinfo.enc = ENC(db); |
| 91117 | preupdate.keyinfo.nKeyField = pTab->nCol; |
| 91118 | preupdate.keyinfo.aSortFlags = (u8*)&fakeSortOrder; |
| 91119 | preupdate.iKey1 = iKey1; |
| 91120 | preupdate.iKey2 = iKey2; |
| 91121 | preupdate.pTab = pTab; |
| 91122 | preupdate.iBlobWrite = iBlobWrite; |
| 91123 | |
| 91124 | db->pPreUpdate = &preupdate; |
| 91125 | db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2); |
| 91126 | db->pPreUpdate = 0; |
| 91127 | sqlite3DbFree(db, preupdate.aRecord); |
| 91128 | vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked); |
| 91129 | vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked); |
| 91130 | sqlite3VdbeMemRelease(&preupdate.oldipk); |
| 91131 | if( preupdate.aNew ){ |
| 91132 | int i; |
| 91133 | for(i=0; i<pCsr->nField; i++){ |
| 91134 | sqlite3VdbeMemRelease(&preupdate.aNew[i]); |
| @@ -93363,11 +93422,11 @@ | |
| 93363 | nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor); |
| 93364 | aRec = sqlite3DbMallocRaw(db, nRec); |
| 93365 | if( !aRec ) goto preupdate_old_out; |
| 93366 | rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec); |
| 93367 | if( rc==SQLITE_OK ){ |
| 93368 | p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec); |
| 93369 | if( !p->pUnpacked ) rc = SQLITE_NOMEM; |
| 93370 | } |
| 93371 | if( rc!=SQLITE_OK ){ |
| 93372 | sqlite3DbFree(db, aRec); |
| 93373 | goto preupdate_old_out; |
| @@ -93428,11 +93487,11 @@ | |
| 93428 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 93429 | p = db!=0 ? db->pPreUpdate : 0; |
| 93430 | #else |
| 93431 | p = db->pPreUpdate; |
| 93432 | #endif |
| 93433 | return (p ? p->keyinfo.nKeyField : 0); |
| 93434 | } |
| 93435 | #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ |
| 93436 | |
| 93437 | #ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
| 93438 | /* |
| @@ -93511,11 +93570,11 @@ | |
| 93511 | UnpackedRecord *pUnpack = p->pNewUnpacked; |
| 93512 | if( !pUnpack ){ |
| 93513 | Mem *pData = &p->v->aMem[p->iNewReg]; |
| 93514 | rc = ExpandBlob(pData); |
| 93515 | if( rc!=SQLITE_OK ) goto preupdate_new_out; |
| 93516 | pUnpack = vdbeUnpackRecord(&p->keyinfo, pData->n, pData->z); |
| 93517 | if( !pUnpack ){ |
| 93518 | rc = SQLITE_NOMEM; |
| 93519 | goto preupdate_new_out; |
| 93520 | } |
| 93521 | p->pNewUnpacked = pUnpack; |
| @@ -94305,13 +94364,13 @@ | |
| 94305 | */ |
| 94306 | Mem *pMem = iCur>0 ? &p->aMem[p->nMem-iCur] : p->aMem; |
| 94307 | |
| 94308 | i64 nByte; |
| 94309 | VdbeCursor *pCx = 0; |
| 94310 | nByte = |
| 94311 | ROUND8P(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField + |
| 94312 | (eCurType==CURTYPE_BTREE?sqlite3BtreeCursorSize():0); |
| 94313 | |
| 94314 | assert( iCur>=0 && iCur<p->nCursor ); |
| 94315 | if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/ |
| 94316 | sqlite3VdbeFreeCursorNN(p, p->apCsr[iCur]); |
| 94317 | p->apCsr[iCur] = 0; |
| @@ -94340,12 +94399,12 @@ | |
| 94340 | memset(pCx, 0, offsetof(VdbeCursor,pAltCursor)); |
| 94341 | pCx->eCurType = eCurType; |
| 94342 | pCx->nField = nField; |
| 94343 | pCx->aOffset = &pCx->aType[nField]; |
| 94344 | if( eCurType==CURTYPE_BTREE ){ |
| 94345 | pCx->uc.pCursor = (BtCursor*) |
| 94346 | &pMem->z[ROUND8P(sizeof(VdbeCursor))+2*sizeof(u32)*nField]; |
| 94347 | sqlite3BtreeCursorZero(pCx->uc.pCursor); |
| 94348 | } |
| 94349 | return pCx; |
| 94350 | } |
| 94351 | |
| @@ -100083,11 +100142,11 @@ | |
| 100083 | pCrsr = pC->uc.pCursor; |
| 100084 | |
| 100085 | /* The OP_RowData opcodes always follow OP_NotExists or |
| 100086 | ** OP_SeekRowid or OP_Rewind/Op_Next with no intervening instructions |
| 100087 | ** that might invalidate the cursor. |
| 100088 | ** If this where not the case, on of the following assert()s |
| 100089 | ** would fail. Should this ever change (because of changes in the code |
| 100090 | ** generator) then the fix would be to insert a call to |
| 100091 | ** sqlite3VdbeCursorMoveto(). |
| 100092 | */ |
| 100093 | assert( pC->deferredMoveto==0 ); |
| @@ -101732,11 +101791,11 @@ | |
| 101732 | ** cell in which to store the accumulation. Be careful that the memory |
| 101733 | ** cell is 8-byte aligned, even on platforms where a pointer is 32-bits. |
| 101734 | ** |
| 101735 | ** Note: We could avoid this by using a regular memory cell from aMem[] for |
| 101736 | ** the accumulator, instead of allocating one here. */ |
| 101737 | nAlloc = ROUND8P( sizeof(pCtx[0]) + (n-1)*sizeof(sqlite3_value*) ); |
| 101738 | pCtx = sqlite3DbMallocRawNN(db, nAlloc + sizeof(Mem)); |
| 101739 | if( pCtx==0 ) goto no_mem; |
| 101740 | pCtx->pOut = (Mem*)((u8*)pCtx + nAlloc); |
| 101741 | assert( EIGHT_BYTE_ALIGNMENT(pCtx->pOut) ); |
| 101742 | |
| @@ -103390,10 +103449,11 @@ | |
| 103390 | int iCol; /* Index of zColumn in row-record */ |
| 103391 | int rc = SQLITE_OK; |
| 103392 | char *zErr = 0; |
| 103393 | Table *pTab; |
| 103394 | Incrblob *pBlob = 0; |
| 103395 | Parse sParse; |
| 103396 | |
| 103397 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 103398 | if( ppBlob==0 ){ |
| 103399 | return SQLITE_MISUSE_BKPT; |
| @@ -103435,11 +103495,14 @@ | |
| 103435 | if( pTab && IsView(pTab) ){ |
| 103436 | pTab = 0; |
| 103437 | sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable); |
| 103438 | } |
| 103439 | #endif |
| 103440 | if( !pTab ){ |
| 103441 | if( sParse.zErrMsg ){ |
| 103442 | sqlite3DbFree(db, zErr); |
| 103443 | zErr = sParse.zErrMsg; |
| 103444 | sParse.zErrMsg = 0; |
| 103445 | } |
| @@ -103446,11 +103509,11 @@ | |
| 103446 | rc = SQLITE_ERROR; |
| 103447 | sqlite3BtreeLeaveAll(db); |
| 103448 | goto blob_open_out; |
| 103449 | } |
| 103450 | pBlob->pTab = pTab; |
| 103451 | pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName; |
| 103452 | |
| 103453 | /* Now search pTab for the exact column. */ |
| 103454 | iCol = sqlite3ColumnIndex(pTab, zColumn); |
| 103455 | if( iCol<0 ){ |
| 103456 | sqlite3DbFree(db, zErr); |
| @@ -103530,11 +103593,10 @@ | |
| 103530 | {OP_Column, 0, 0, 1}, /* 3 */ |
| 103531 | {OP_ResultRow, 1, 0, 0}, /* 4 */ |
| 103532 | {OP_Halt, 0, 0, 0}, /* 5 */ |
| 103533 | }; |
| 103534 | Vdbe *v = (Vdbe *)pBlob->pStmt; |
| 103535 | int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| 103536 | VdbeOp *aOp; |
| 103537 | |
| 103538 | sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag, |
| 103539 | pTab->pSchema->schema_cookie, |
| 103540 | pTab->pSchema->iGeneration); |
| @@ -104108,12 +104170,15 @@ | |
| 104108 | u8 bUsePMA; /* True if one or more PMAs created */ |
| 104109 | u8 bUseThreads; /* True to use background threads */ |
| 104110 | u8 iPrev; /* Previous thread used to flush PMA */ |
| 104111 | u8 nTask; /* Size of aTask[] array */ |
| 104112 | u8 typeMask; |
| 104113 | SortSubtask aTask[1]; /* One or more subtasks */ |
| 104114 | }; |
| 104115 | |
| 104116 | #define SORTER_TYPE_INTEGER 0x01 |
| 104117 | #define SORTER_TYPE_TEXT 0x02 |
| 104118 | |
| 104119 | /* |
| @@ -104742,12 +104807,12 @@ | |
| 104742 | assert( pCsr->pKeyInfo ); |
| 104743 | assert( !pCsr->isEphemeral ); |
| 104744 | assert( pCsr->eCurType==CURTYPE_SORTER ); |
| 104745 | assert( sizeof(KeyInfo) + UMXV(pCsr->pKeyInfo->nKeyField)*sizeof(CollSeq*) |
| 104746 | < 0x7fffffff ); |
| 104747 | szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nKeyField-1)*sizeof(CollSeq*); |
| 104748 | sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask); |
| 104749 | |
| 104750 | pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo); |
| 104751 | pCsr->uc.pSorter = pSorter; |
| 104752 | if( pSorter==0 ){ |
| 104753 | rc = SQLITE_NOMEM_BKPT; |
| @@ -105207,10 +105272,14 @@ | |
| 105207 | } |
| 105208 | |
| 105209 | p->u.pNext = 0; |
| 105210 | for(i=0; aSlot[i]; i++){ |
| 105211 | p = vdbeSorterMerge(pTask, p, aSlot[i]); |
| 105212 | aSlot[i] = 0; |
| 105213 | } |
| 105214 | aSlot[i] = p; |
| 105215 | p = pNext; |
| 105216 | } |
| @@ -109981,32 +110050,34 @@ | |
| 109981 | Table *pTab, /* The table being referenced, or NULL */ |
| 109982 | int type, /* NC_IsCheck, NC_PartIdx, NC_IdxExpr, NC_GenCol, or 0 */ |
| 109983 | Expr *pExpr, /* Expression to resolve. May be NULL. */ |
| 109984 | ExprList *pList /* Expression list to resolve. May be NULL. */ |
| 109985 | ){ |
| 109986 | SrcList sSrc; /* Fake SrcList for pParse->pNewTable */ |
| 109987 | NameContext sNC; /* Name context for pParse->pNewTable */ |
| 109988 | int rc; |
| 109989 | |
| 109990 | assert( type==0 || pTab!=0 ); |
| 109991 | assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr |
| 109992 | || type==NC_GenCol || pTab==0 ); |
| 109993 | memset(&sNC, 0, sizeof(sNC)); |
| 109994 | memset(&sSrc, 0, sizeof(sSrc)); |
| 109995 | if( pTab ){ |
| 109996 | sSrc.nSrc = 1; |
| 109997 | sSrc.a[0].zName = pTab->zName; |
| 109998 | sSrc.a[0].pSTab = pTab; |
| 109999 | sSrc.a[0].iCursor = -1; |
| 110000 | if( pTab->pSchema!=pParse->db->aDb[1].pSchema ){ |
| 110001 | /* Cause EP_FromDDL to be set on TK_FUNCTION nodes of non-TEMP |
| 110002 | ** schema elements */ |
| 110003 | type |= NC_FromDDL; |
| 110004 | } |
| 110005 | } |
| 110006 | sNC.pParse = pParse; |
| 110007 | sNC.pSrcList = &sSrc; |
| 110008 | sNC.ncFlags = type | NC_IsDDL; |
| 110009 | if( (rc = sqlite3ResolveExprNames(&sNC, pExpr))!=SQLITE_OK ) return rc; |
| 110010 | if( pList ) rc = sqlite3ResolveExprListNames(&sNC, pList); |
| 110011 | return rc; |
| 110012 | } |
| @@ -111751,11 +111822,11 @@ | |
| 111751 | */ |
| 111752 | #ifndef SQLITE_OMIT_CTE |
| 111753 | SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p){ |
| 111754 | With *pRet = 0; |
| 111755 | if( p ){ |
| 111756 | sqlite3_int64 nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1); |
| 111757 | pRet = sqlite3DbMallocZero(db, nByte); |
| 111758 | if( pRet ){ |
| 111759 | int i; |
| 111760 | pRet->nCte = p->nCte; |
| 111761 | for(i=0; i<p->nCte; i++){ |
| @@ -111878,15 +111949,13 @@ | |
| 111878 | #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \ |
| 111879 | || !defined(SQLITE_OMIT_SUBQUERY) |
| 111880 | SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int flags){ |
| 111881 | SrcList *pNew; |
| 111882 | int i; |
| 111883 | int nByte; |
| 111884 | assert( db!=0 ); |
| 111885 | if( p==0 ) return 0; |
| 111886 | nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0); |
| 111887 | pNew = sqlite3DbMallocRawNN(db, nByte ); |
| 111888 | if( pNew==0 ) return 0; |
| 111889 | pNew->nSrc = pNew->nAlloc = p->nSrc; |
| 111890 | for(i=0; i<p->nSrc; i++){ |
| 111891 | SrcItem *pNewItem = &pNew->a[i]; |
| 111892 | const SrcItem *pOldItem = &p->a[i]; |
| @@ -111944,11 +112013,11 @@ | |
| 111944 | SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, const IdList *p){ |
| 111945 | IdList *pNew; |
| 111946 | int i; |
| 111947 | assert( db!=0 ); |
| 111948 | if( p==0 ) return 0; |
| 111949 | pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew)+(p->nId-1)*sizeof(p->a[0]) ); |
| 111950 | if( pNew==0 ) return 0; |
| 111951 | pNew->nId = p->nId; |
| 111952 | for(i=0; i<p->nId; i++){ |
| 111953 | struct IdList_item *pNewItem = &pNew->a[i]; |
| 111954 | const struct IdList_item *pOldItem = &p->a[i]; |
| @@ -112028,11 +112097,11 @@ | |
| 112028 | Expr *pExpr /* Expression to be appended. Might be NULL */ |
| 112029 | ){ |
| 112030 | struct ExprList_item *pItem; |
| 112031 | ExprList *pList; |
| 112032 | |
| 112033 | pList = sqlite3DbMallocRawNN(db, sizeof(ExprList)+sizeof(pList->a[0])*4 ); |
| 112034 | if( pList==0 ){ |
| 112035 | sqlite3ExprDelete(db, pExpr); |
| 112036 | return 0; |
| 112037 | } |
| 112038 | pList->nAlloc = 4; |
| @@ -112048,12 +112117,11 @@ | |
| 112048 | Expr *pExpr /* Expression to be appended. Might be NULL */ |
| 112049 | ){ |
| 112050 | struct ExprList_item *pItem; |
| 112051 | ExprList *pNew; |
| 112052 | pList->nAlloc *= 2; |
| 112053 | pNew = sqlite3DbRealloc(db, pList, |
| 112054 | sizeof(*pList)+(pList->nAlloc-1)*sizeof(pList->a[0])); |
| 112055 | if( pNew==0 ){ |
| 112056 | sqlite3ExprListDelete(db, pList); |
| 112057 | sqlite3ExprDelete(db, pExpr); |
| 112058 | return 0; |
| 112059 | }else{ |
| @@ -114685,11 +114753,11 @@ | |
| 114685 | return -1; /* Not found */ |
| 114686 | } |
| 114687 | |
| 114688 | |
| 114689 | /* |
| 114690 | ** Expresion pExpr is guaranteed to be a TK_COLUMN or equivalent. This |
| 114691 | ** function checks the Parse.pIdxPartExpr list to see if this column |
| 114692 | ** can be replaced with a constant value. If so, it generates code to |
| 114693 | ** put the constant value in a register (ideally, but not necessarily, |
| 114694 | ** register iTarget) and returns the register number. |
| 114695 | ** |
| @@ -118531,10 +118599,11 @@ | |
| 118531 | sqlite3 *db, /* Database handle */ |
| 118532 | const char *zSql, /* SQL to parse */ |
| 118533 | int bTemp /* True if SQL is from temp schema */ |
| 118534 | ){ |
| 118535 | int rc; |
| 118536 | |
| 118537 | sqlite3ParseObjectInit(p, db); |
| 118538 | if( zSql==0 ){ |
| 118539 | return SQLITE_NOMEM; |
| 118540 | } |
| @@ -118549,11 +118618,15 @@ | |
| 118549 | db->init.iDb = (u8)iDb; |
| 118550 | } |
| 118551 | p->eParseMode = PARSE_MODE_RENAME; |
| 118552 | p->db = db; |
| 118553 | p->nQueryLoop = 1; |
| 118554 | rc = sqlite3RunParser(p, zSql); |
| 118555 | if( db->mallocFailed ) rc = SQLITE_NOMEM; |
| 118556 | if( rc==SQLITE_OK |
| 118557 | && NEVER(p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0) |
| 118558 | ){ |
| 118559 | rc = SQLITE_CORRUPT_BKPT; |
| @@ -119445,11 +119518,11 @@ | |
| 119445 | int rc; |
| 119446 | Parse sParse; |
| 119447 | u64 flags = db->flags; |
| 119448 | if( bNoDQS ) db->flags &= ~(SQLITE_DqsDML|SQLITE_DqsDDL); |
| 119449 | rc = renameParseSql(&sParse, zDb, db, zInput, bTemp); |
| 119450 | db->flags |= (flags & (SQLITE_DqsDML|SQLITE_DqsDDL)); |
| 119451 | if( rc==SQLITE_OK ){ |
| 119452 | if( isLegacy==0 && sParse.pNewTable && IsView(sParse.pNewTable) ){ |
| 119453 | NameContext sNC; |
| 119454 | memset(&sNC, 0, sizeof(sNC)); |
| 119455 | sNC.pParse = &sParse; |
| @@ -126268,11 +126341,11 @@ | |
| 126268 | "columns in the referenced table"); |
| 126269 | goto fk_end; |
| 126270 | }else{ |
| 126271 | nCol = pFromCol->nExpr; |
| 126272 | } |
| 126273 | nByte = sizeof(*pFKey) + (nCol-1)*sizeof(pFKey->aCol[0]) + pTo->n + 1; |
| 126274 | if( pToCol ){ |
| 126275 | for(i=0; i<pToCol->nExpr; i++){ |
| 126276 | nByte += sqlite3Strlen30(pToCol->a[i].zEName) + 1; |
| 126277 | } |
| 126278 | } |
| @@ -127327,16 +127400,15 @@ | |
| 127327 | */ |
| 127328 | SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *pToken){ |
| 127329 | sqlite3 *db = pParse->db; |
| 127330 | int i; |
| 127331 | if( pList==0 ){ |
| 127332 | pList = sqlite3DbMallocZero(db, sizeof(IdList) ); |
| 127333 | if( pList==0 ) return 0; |
| 127334 | }else{ |
| 127335 | IdList *pNew; |
| 127336 | pNew = sqlite3DbRealloc(db, pList, |
| 127337 | sizeof(IdList) + pList->nId*sizeof(pList->a)); |
| 127338 | if( pNew==0 ){ |
| 127339 | sqlite3IdListDelete(db, pList); |
| 127340 | return 0; |
| 127341 | } |
| 127342 | pList = pNew; |
| @@ -127431,12 +127503,11 @@ | |
| 127431 | sqlite3ErrorMsg(pParse, "too many FROM clause terms, max: %d", |
| 127432 | SQLITE_MAX_SRCLIST); |
| 127433 | return 0; |
| 127434 | } |
| 127435 | if( nAlloc>SQLITE_MAX_SRCLIST ) nAlloc = SQLITE_MAX_SRCLIST; |
| 127436 | pNew = sqlite3DbRealloc(db, pSrc, |
| 127437 | sizeof(*pSrc) + (nAlloc-1)*sizeof(pSrc->a[0]) ); |
| 127438 | if( pNew==0 ){ |
| 127439 | assert( db->mallocFailed ); |
| 127440 | return 0; |
| 127441 | } |
| 127442 | pSrc = pNew; |
| @@ -127507,11 +127578,11 @@ | |
| 127507 | assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */ |
| 127508 | assert( pParse!=0 ); |
| 127509 | assert( pParse->db!=0 ); |
| 127510 | db = pParse->db; |
| 127511 | if( pList==0 ){ |
| 127512 | pList = sqlite3DbMallocRawNN(pParse->db, sizeof(SrcList) ); |
| 127513 | if( pList==0 ) return 0; |
| 127514 | pList->nAlloc = 1; |
| 127515 | pList->nSrc = 1; |
| 127516 | memset(&pList->a[0], 0, sizeof(pList->a[0])); |
| 127517 | pList->a[0].iCursor = -1; |
| @@ -128393,14 +128464,13 @@ | |
| 128393 | } |
| 128394 | } |
| 128395 | } |
| 128396 | |
| 128397 | if( pWith ){ |
| 128398 | sqlite3_int64 nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte); |
| 128399 | pNew = sqlite3DbRealloc(db, pWith, nByte); |
| 128400 | }else{ |
| 128401 | pNew = sqlite3DbMallocZero(db, sizeof(*pWith)); |
| 128402 | } |
| 128403 | assert( (pNew!=0 && zName!=0) || db->mallocFailed ); |
| 128404 | |
| 128405 | if( db->mallocFailed ){ |
| 128406 | sqlite3CteDelete(db, pCte); |
| @@ -131933,11 +132003,11 @@ | |
| 131933 | ** |
| 131934 | ** The SUM() function follows the (broken) SQL standard which means |
| 131935 | ** that it returns NULL if it sums over no inputs. TOTAL returns |
| 131936 | ** 0.0 in that case. In addition, TOTAL always returns a float where |
| 131937 | ** SUM might return an integer if it never encounters a floating point |
| 131938 | ** value. TOTAL never fails, but SUM might through an exception if |
| 131939 | ** it overflows an integer. |
| 131940 | */ |
| 131941 | static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){ |
| 131942 | SumCtx *p; |
| 131943 | int type; |
| @@ -139748,52 +139818,52 @@ | |
| 139748 | /* 11 */ "notnull", |
| 139749 | /* 12 */ "dflt_value", |
| 139750 | /* 13 */ "pk", |
| 139751 | /* 14 */ "hidden", |
| 139752 | /* table_info reuses 8 */ |
| 139753 | /* 15 */ "schema", /* Used by: table_list */ |
| 139754 | /* 16 */ "name", |
| 139755 | /* 17 */ "type", |
| 139756 | /* 18 */ "ncol", |
| 139757 | /* 19 */ "wr", |
| 139758 | /* 20 */ "strict", |
| 139759 | /* 21 */ "seqno", /* Used by: index_xinfo */ |
| 139760 | /* 22 */ "cid", |
| 139761 | /* 23 */ "name", |
| 139762 | /* 24 */ "desc", |
| 139763 | /* 25 */ "coll", |
| 139764 | /* 26 */ "key", |
| 139765 | /* 27 */ "name", /* Used by: function_list */ |
| 139766 | /* 28 */ "builtin", |
| 139767 | /* 29 */ "type", |
| 139768 | /* 30 */ "enc", |
| 139769 | /* 31 */ "narg", |
| 139770 | /* 32 */ "flags", |
| 139771 | /* 33 */ "tbl", /* Used by: stats */ |
| 139772 | /* 34 */ "idx", |
| 139773 | /* 35 */ "wdth", |
| 139774 | /* 36 */ "hght", |
| 139775 | /* 37 */ "flgs", |
| 139776 | /* 38 */ "seq", /* Used by: index_list */ |
| 139777 | /* 39 */ "name", |
| 139778 | /* 40 */ "unique", |
| 139779 | /* 41 */ "origin", |
| 139780 | /* 42 */ "partial", |
| 139781 | /* 43 */ "table", /* Used by: foreign_key_check */ |
| 139782 | /* 44 */ "rowid", |
| 139783 | /* 45 */ "parent", |
| 139784 | /* 46 */ "fkid", |
| 139785 | /* index_info reuses 21 */ |
| 139786 | /* 47 */ "seq", /* Used by: database_list */ |
| 139787 | /* 48 */ "name", |
| 139788 | /* 49 */ "file", |
| 139789 | /* 50 */ "busy", /* Used by: wal_checkpoint */ |
| 139790 | /* 51 */ "log", |
| 139791 | /* 52 */ "checkpointed", |
| 139792 | /* collation_list reuses 38 */ |
| 139793 | /* 53 */ "database", /* Used by: lock_status */ |
| 139794 | /* 54 */ "status", |
| 139795 | /* 55 */ "cache_size", /* Used by: default_cache_size */ |
| 139796 | /* module_list pragma_list reuses 9 */ |
| 139797 | /* 56 */ "timeout", /* Used by: busy_timeout */ |
| 139798 | }; |
| 139799 | |
| @@ -139882,11 +139952,11 @@ | |
| 139882 | #endif |
| 139883 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 139884 | {/* zName: */ "collation_list", |
| 139885 | /* ePragTyp: */ PragTyp_COLLATION_LIST, |
| 139886 | /* ePragFlg: */ PragFlg_Result0, |
| 139887 | /* ColNames: */ 38, 2, |
| 139888 | /* iArg: */ 0 }, |
| 139889 | #endif |
| 139890 | #if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS) |
| 139891 | {/* zName: */ "compile_options", |
| 139892 | /* ePragTyp: */ PragTyp_COMPILE_OPTIONS, |
| @@ -139917,11 +139987,11 @@ | |
| 139917 | #endif |
| 139918 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 139919 | {/* zName: */ "database_list", |
| 139920 | /* ePragTyp: */ PragTyp_DATABASE_LIST, |
| 139921 | /* ePragFlg: */ PragFlg_Result0, |
| 139922 | /* ColNames: */ 47, 3, |
| 139923 | /* iArg: */ 0 }, |
| 139924 | #endif |
| 139925 | #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED) |
| 139926 | {/* zName: */ "default_cache_size", |
| 139927 | /* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE, |
| @@ -139997,11 +140067,11 @@ | |
| 139997 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 139998 | #if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS) |
| 139999 | {/* zName: */ "function_list", |
| 140000 | /* ePragTyp: */ PragTyp_FUNCTION_LIST, |
| 140001 | /* ePragFlg: */ PragFlg_Result0, |
| 140002 | /* ColNames: */ 27, 6, |
| 140003 | /* iArg: */ 0 }, |
| 140004 | #endif |
| 140005 | #endif |
| 140006 | {/* zName: */ "hard_heap_limit", |
| 140007 | /* ePragTyp: */ PragTyp_HARD_HEAP_LIMIT, |
| @@ -140026,21 +140096,21 @@ | |
| 140026 | #endif |
| 140027 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 140028 | {/* zName: */ "index_info", |
| 140029 | /* ePragTyp: */ PragTyp_INDEX_INFO, |
| 140030 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 140031 | /* ColNames: */ 21, 3, |
| 140032 | /* iArg: */ 0 }, |
| 140033 | {/* zName: */ "index_list", |
| 140034 | /* ePragTyp: */ PragTyp_INDEX_LIST, |
| 140035 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 140036 | /* ColNames: */ 38, 5, |
| 140037 | /* iArg: */ 0 }, |
| 140038 | {/* zName: */ "index_xinfo", |
| 140039 | /* ePragTyp: */ PragTyp_INDEX_INFO, |
| 140040 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 140041 | /* ColNames: */ 21, 6, |
| 140042 | /* iArg: */ 1 }, |
| 140043 | #endif |
| 140044 | #if !defined(SQLITE_OMIT_INTEGRITY_CHECK) |
| 140045 | {/* zName: */ "integrity_check", |
| 140046 | /* ePragTyp: */ PragTyp_INTEGRITY_CHECK, |
| @@ -140215,11 +140285,11 @@ | |
| 140215 | #endif |
| 140216 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && defined(SQLITE_DEBUG) |
| 140217 | {/* zName: */ "stats", |
| 140218 | /* ePragTyp: */ PragTyp_STATS, |
| 140219 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq, |
| 140220 | /* ColNames: */ 33, 5, |
| 140221 | /* iArg: */ 0 }, |
| 140222 | #endif |
| 140223 | #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) |
| 140224 | {/* zName: */ "synchronous", |
| 140225 | /* ePragTyp: */ PragTyp_SYNCHRONOUS, |
| @@ -140234,11 +140304,11 @@ | |
| 140234 | /* ColNames: */ 8, 6, |
| 140235 | /* iArg: */ 0 }, |
| 140236 | {/* zName: */ "table_list", |
| 140237 | /* ePragTyp: */ PragTyp_TABLE_LIST, |
| 140238 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1, |
| 140239 | /* ColNames: */ 15, 6, |
| 140240 | /* iArg: */ 0 }, |
| 140241 | {/* zName: */ "table_xinfo", |
| 140242 | /* ePragTyp: */ PragTyp_TABLE_INFO, |
| 140243 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 140244 | /* ColNames: */ 8, 7, |
| @@ -140311,11 +140381,11 @@ | |
| 140311 | /* ColNames: */ 0, 0, |
| 140312 | /* iArg: */ 0 }, |
| 140313 | {/* zName: */ "wal_checkpoint", |
| 140314 | /* ePragTyp: */ PragTyp_WAL_CHECKPOINT, |
| 140315 | /* ePragFlg: */ PragFlg_NeedSchema, |
| 140316 | /* ColNames: */ 50, 3, |
| 140317 | /* iArg: */ 0 }, |
| 140318 | #endif |
| 140319 | #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) |
| 140320 | {/* zName: */ "writable_schema", |
| 140321 | /* ePragTyp: */ PragTyp_FLAG, |
| @@ -140333,11 +140403,11 @@ | |
| 140333 | ** When the 0x10 bit of PRAGMA optimize is set, any ANALYZE commands |
| 140334 | ** will be run with an analysis_limit set to the lessor of the value of |
| 140335 | ** the following macro or to the actual analysis_limit if it is non-zero, |
| 140336 | ** in order to prevent PRAGMA optimize from running for too long. |
| 140337 | ** |
| 140338 | ** The value of 2000 is chosen emperically so that the worst-case run-time |
| 140339 | ** for PRAGMA optimize does not exceed 100 milliseconds against a variety |
| 140340 | ** of test databases on a RaspberryPI-4 compiled using -Os and without |
| 140341 | ** -DSQLITE_DEBUG. Of course, your mileage may vary. For the purpose of |
| 140342 | ** this paragraph, "worst-case" means that ANALYZE ends up being |
| 140343 | ** run on every table in the database. The worst case typically only |
| @@ -144622,11 +144692,11 @@ | |
| 144622 | pNew->iOffset = 0; |
| 144623 | pNew->selId = ++pParse->nSelect; |
| 144624 | pNew->addrOpenEphm[0] = -1; |
| 144625 | pNew->addrOpenEphm[1] = -1; |
| 144626 | pNew->nSelectRow = 0; |
| 144627 | if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*pSrc)); |
| 144628 | pNew->pSrc = pSrc; |
| 144629 | pNew->pWhere = pWhere; |
| 144630 | pNew->pGroupBy = pGroupBy; |
| 144631 | pNew->pHaving = pHaving; |
| 144632 | pNew->pOrderBy = pOrderBy; |
| @@ -146005,20 +146075,20 @@ | |
| 146005 | /* |
| 146006 | ** Allocate a KeyInfo object sufficient for an index of N key columns and |
| 146007 | ** X extra columns. |
| 146008 | */ |
| 146009 | SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){ |
| 146010 | int nExtra = (N+X)*(sizeof(CollSeq*)+1) - sizeof(CollSeq*); |
| 146011 | KeyInfo *p = sqlite3DbMallocRawNN(db, sizeof(KeyInfo) + nExtra); |
| 146012 | if( p ){ |
| 146013 | p->aSortFlags = (u8*)&p->aColl[N+X]; |
| 146014 | p->nKeyField = (u16)N; |
| 146015 | p->nAllField = (u16)(N+X); |
| 146016 | p->enc = ENC(db); |
| 146017 | p->db = db; |
| 146018 | p->nRef = 1; |
| 146019 | memset(&p[1], 0, nExtra); |
| 146020 | }else{ |
| 146021 | return (KeyInfo*)sqlite3OomFault(db); |
| 146022 | } |
| 146023 | return p; |
| 146024 | } |
| @@ -150530,11 +150600,11 @@ | |
| 150530 | } |
| 150531 | pTabList = p->pSrc; |
| 150532 | pEList = p->pEList; |
| 150533 | if( pParse->pWith && (p->selFlags & SF_View) ){ |
| 150534 | if( p->pWith==0 ){ |
| 150535 | p->pWith = (With*)sqlite3DbMallocZero(db, sizeof(With)); |
| 150536 | if( p->pWith==0 ){ |
| 150537 | return WRC_Abort; |
| 150538 | } |
| 150539 | } |
| 150540 | p->pWith->bView = 1; |
| @@ -151669,10 +151739,11 @@ | |
| 151669 | ** * The subquery is a UNION ALL of two or more terms |
| 151670 | ** * The subquery does not have a LIMIT clause |
| 151671 | ** * There is no WHERE or GROUP BY or HAVING clauses on the subqueries |
| 151672 | ** * The outer query is a simple count(*) with no WHERE clause or other |
| 151673 | ** extraneous syntax. |
| 151674 | ** |
| 151675 | ** Return TRUE if the optimization is undertaken. |
| 151676 | */ |
| 151677 | static int countOfViewOptimization(Parse *pParse, Select *p){ |
| 151678 | Select *pSub, *pPrior; |
| @@ -151701,11 +151772,15 @@ | |
| 151701 | if( pSub->selFlags & SF_CopyCte ) return 0; /* Not a CTE */ |
| 151702 | do{ |
| 151703 | if( pSub->op!=TK_ALL && pSub->pPrior ) return 0; /* Must be UNION ALL */ |
| 151704 | if( pSub->pWhere ) return 0; /* No WHERE clause */ |
| 151705 | if( pSub->pLimit ) return 0; /* No LIMIT clause */ |
| 151706 | if( pSub->selFlags & SF_Aggregate ) return 0; /* Not an aggregate */ |
| 151707 | assert( pSub->pHaving==0 ); /* Due to the previous */ |
| 151708 | pSub = pSub->pPrior; /* Repeat over compound */ |
| 151709 | }while( pSub ); |
| 151710 | |
| 151711 | /* If we reach this point then it is OK to perform the transformation */ |
| @@ -151713,11 +151788,11 @@ | |
| 151713 | db = pParse->db; |
| 151714 | pCount = pExpr; |
| 151715 | pExpr = 0; |
| 151716 | pSub = sqlite3SubqueryDetach(db, pFrom); |
| 151717 | sqlite3SrcListDelete(db, p->pSrc); |
| 151718 | p->pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*p->pSrc)); |
| 151719 | while( pSub ){ |
| 151720 | Expr *pTerm; |
| 151721 | pPrior = pSub->pPrior; |
| 151722 | pSub->pPrior = 0; |
| 151723 | pSub->pNext = 0; |
| @@ -154504,11 +154579,12 @@ | |
| 154504 | Vdbe *v = pParse->pVdbe; |
| 154505 | sqlite3 *db = pParse->db; |
| 154506 | ExprList *pNew; |
| 154507 | Returning *pReturning; |
| 154508 | Select sSelect; |
| 154509 | SrcList sFrom; |
| 154510 | |
| 154511 | assert( v!=0 ); |
| 154512 | if( !pParse->bReturning ){ |
| 154513 | /* This RETURNING trigger must be for a different statement as |
| 154514 | ** this statement lacks a RETURNING clause. */ |
| @@ -154520,17 +154596,18 @@ | |
| 154520 | if( pTrigger != &(pReturning->retTrig) ){ |
| 154521 | /* This RETURNING trigger is for a different statement */ |
| 154522 | return; |
| 154523 | } |
| 154524 | memset(&sSelect, 0, sizeof(sSelect)); |
| 154525 | memset(&sFrom, 0, sizeof(sFrom)); |
| 154526 | sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0); |
| 154527 | sSelect.pSrc = &sFrom; |
| 154528 | sFrom.nSrc = 1; |
| 154529 | sFrom.a[0].pSTab = pTab; |
| 154530 | sFrom.a[0].zName = pTab->zName; /* tag-20240424-1 */ |
| 154531 | sFrom.a[0].iCursor = -1; |
| 154532 | sqlite3SelectPrep(pParse, &sSelect, 0); |
| 154533 | if( pParse->nErr==0 ){ |
| 154534 | assert( db->mallocFailed==0 ); |
| 154535 | sqlite3GenerateColumnNames(pParse, &sSelect); |
| 154536 | } |
| @@ -156927,11 +157004,11 @@ | |
| 156927 | saved_flags = db->flags; |
| 156928 | saved_mDbFlags = db->mDbFlags; |
| 156929 | saved_nChange = db->nChange; |
| 156930 | saved_nTotalChange = db->nTotalChange; |
| 156931 | saved_mTrace = db->mTrace; |
| 156932 | db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks; |
| 156933 | db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum; |
| 156934 | db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder |
| 156935 | | SQLITE_Defensive | SQLITE_CountRows); |
| 156936 | db->mTrace = 0; |
| 156937 | |
| @@ -159056,13 +159133,18 @@ | |
| 159056 | WhereLoop *pLoops; /* List of all WhereLoop objects */ |
| 159057 | WhereMemBlock *pMemToFree;/* Memory to free when this object destroyed */ |
| 159058 | Bitmask revMask; /* Mask of ORDER BY terms that need reversing */ |
| 159059 | WhereClause sWC; /* Decomposition of the WHERE clause */ |
| 159060 | WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */ |
| 159061 | WhereLevel a[1]; /* Information about each nest loop in WHERE */ |
| 159062 | }; |
| 159063 | |
| 159064 | /* |
| 159065 | ** Private interfaces - callable only by other where.c routines. |
| 159066 | ** |
| 159067 | ** where.c: |
| 159068 | */ |
| @@ -161509,12 +161591,11 @@ | |
| 161509 | */ |
| 161510 | if( pWInfo->nLevel>1 ){ |
| 161511 | int nNotReady; /* The number of notReady tables */ |
| 161512 | SrcItem *origSrc; /* Original list of tables */ |
| 161513 | nNotReady = pWInfo->nLevel - iLevel - 1; |
| 161514 | pOrTab = sqlite3DbMallocRawNN(db, |
| 161515 | sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0])); |
| 161516 | if( pOrTab==0 ) return notReady; |
| 161517 | pOrTab->nAlloc = (u8)(nNotReady + 1); |
| 161518 | pOrTab->nSrc = pOrTab->nAlloc; |
| 161519 | memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem)); |
| 161520 | origSrc = pWInfo->pTabList->a; |
| @@ -162053,11 +162134,12 @@ | |
| 162053 | Expr *pSubWhere = 0; |
| 162054 | WhereClause *pWC = &pWInfo->sWC; |
| 162055 | WhereInfo *pSubWInfo; |
| 162056 | WhereLoop *pLoop = pLevel->pWLoop; |
| 162057 | SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; |
| 162058 | SrcList sFrom; |
| 162059 | Bitmask mAll = 0; |
| 162060 | int k; |
| 162061 | |
| 162062 | ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pSTab->zName)); |
| 162063 | sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn, |
| @@ -162097,17 +162179,18 @@ | |
| 162097 | if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue; |
| 162098 | pSubWhere = sqlite3ExprAnd(pParse, pSubWhere, |
| 162099 | sqlite3ExprDup(pParse->db, pTerm->pExpr, 0)); |
| 162100 | } |
| 162101 | } |
| 162102 | sFrom.nSrc = 1; |
| 162103 | sFrom.nAlloc = 1; |
| 162104 | memcpy(&sFrom.a[0], pTabItem, sizeof(SrcItem)); |
| 162105 | sFrom.a[0].fg.jointype = 0; |
| 162106 | assert( pParse->withinRJSubrtn < 100 ); |
| 162107 | pParse->withinRJSubrtn++; |
| 162108 | pSubWInfo = sqlite3WhereBegin(pParse, &sFrom, pSubWhere, 0, 0, 0, |
| 162109 | WHERE_RIGHT_JOIN, 0); |
| 162110 | if( pSubWInfo ){ |
| 162111 | int iCur = pLevel->iTabCur; |
| 162112 | int r = ++pParse->nMem; |
| 162113 | int nPk; |
| @@ -164091,15 +164174,20 @@ | |
| 164091 | WhereClause *pWC; /* The Where clause being analyzed */ |
| 164092 | Parse *pParse; /* The parsing context */ |
| 164093 | int eDistinct; /* Value to return from sqlite3_vtab_distinct() */ |
| 164094 | u32 mIn; /* Mask of terms that are <col> IN (...) */ |
| 164095 | u32 mHandleIn; /* Terms that vtab will handle as <col> IN (...) */ |
| 164096 | sqlite3_value *aRhs[1]; /* RHS values for constraints. MUST BE LAST |
| 164097 | ** because extra space is allocated to hold up |
| 164098 | ** to nTerm such values */ |
| 164099 | }; |
| 164100 | |
| 164101 | /* Forward declaration of methods */ |
| 164102 | static int whereLoopResize(sqlite3*, WhereLoop*, int); |
| 164103 | |
| 164104 | /* |
| 164105 | ** Return the estimated number of output rows from a WHERE clause |
| @@ -165573,12 +165661,12 @@ | |
| 165573 | |
| 165574 | /* Allocate the sqlite3_index_info structure |
| 165575 | */ |
| 165576 | pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo) |
| 165577 | + (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm |
| 165578 | + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden) |
| 165579 | + sizeof(sqlite3_value*)*nTerm ); |
| 165580 | if( pIdxInfo==0 ){ |
| 165581 | sqlite3ErrorMsg(pParse, "out of memory"); |
| 165582 | return 0; |
| 165583 | } |
| 165584 | pHidden = (struct HiddenIndexInfo*)&pIdxInfo[1]; |
| @@ -170768,14 +170856,11 @@ | |
| 170768 | ** struct, the contents of WhereInfo.a[], the WhereClause structure |
| 170769 | ** and the WhereMaskSet structure. Since WhereClause contains an 8-byte |
| 170770 | ** field (type Bitmask) it must be aligned on an 8-byte boundary on |
| 170771 | ** some architectures. Hence the ROUND8() below. |
| 170772 | */ |
| 170773 | nByteWInfo = ROUND8P(sizeof(WhereInfo)); |
| 170774 | if( nTabList>1 ){ |
| 170775 | nByteWInfo = ROUND8P(nByteWInfo + (nTabList-1)*sizeof(WhereLevel)); |
| 170776 | } |
| 170777 | pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop)); |
| 170778 | if( db->mallocFailed ){ |
| 170779 | sqlite3DbFree(db, pWInfo); |
| 170780 | pWInfo = 0; |
| 170781 | goto whereBeginError; |
| @@ -181607,11 +181692,15 @@ | |
| 181607 | tokenType = analyzeOverKeyword((const u8*)&zSql[4], lastTokenParsed); |
| 181608 | }else if( tokenType==TK_FILTER ){ |
| 181609 | assert( n==6 ); |
| 181610 | tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed); |
| 181611 | #endif /* SQLITE_OMIT_WINDOWFUNC */ |
| 181612 | }else if( tokenType==TK_COMMENT && (db->flags & SQLITE_Comments)!=0 ){ |
| 181613 | zSql += n; |
| 181614 | continue; |
| 181615 | }else if( tokenType!=TK_QNUMBER ){ |
| 181616 | Token x; |
| 181617 | x.z = zSql; |
| @@ -182502,10 +182591,18 @@ | |
| 182502 | } |
| 182503 | #endif |
| 182504 | if( rc==SQLITE_OK ){ |
| 182505 | sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage, |
| 182506 | sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage); |
| 182507 | sqlite3MemoryBarrier(); |
| 182508 | sqlite3GlobalConfig.isInit = 1; |
| 182509 | #ifdef SQLITE_EXTRA_INIT |
| 182510 | bRunExtraInit = 1; |
| 182511 | #endif |
| @@ -184063,10 +184160,14 @@ | |
| 184063 | sqlite3_file *fd = sqlite3PagerFile(sqlite3BtreePager(pBt)); |
| 184064 | sqlite3OsFileControlHint(fd, SQLITE_FCNTL_BLOCK_ON_CONNECT, (void*)&bBOC); |
| 184065 | } |
| 184066 | } |
| 184067 | sqlite3BtreeLeaveAll(db); |
| 184068 | #endif |
| 184069 | return SQLITE_OK; |
| 184070 | } |
| 184071 | |
| 184072 | /* |
| @@ -186032,11 +186133,11 @@ | |
| 186032 | }else if( pData==0 ){ |
| 186033 | sqlite3_mutex_leave(db->mutex); |
| 186034 | return SQLITE_OK; |
| 186035 | }else{ |
| 186036 | size_t n = strlen(zName); |
| 186037 | p = sqlite3_malloc64( sizeof(DbClientData)+n+1 ); |
| 186038 | if( p==0 ){ |
| 186039 | if( xDestructor ) xDestructor(pData); |
| 186040 | sqlite3_mutex_leave(db->mutex); |
| 186041 | return SQLITE_NOMEM; |
| 186042 | } |
| @@ -186398,12 +186499,12 @@ | |
| 186398 | #endif |
| 186399 | |
| 186400 | /* sqlite3_test_control(SQLITE_TESTCTRL_FK_NO_ACTION, sqlite3 *db, int b); |
| 186401 | ** |
| 186402 | ** If b is true, then activate the SQLITE_FkNoAction setting. If b is |
| 186403 | ** false then clearn that setting. If the SQLITE_FkNoAction setting is |
| 186404 | ** abled, all foreign key ON DELETE and ON UPDATE actions behave as if |
| 186405 | ** they were NO ACTION, regardless of how they are defined. |
| 186406 | ** |
| 186407 | ** NB: One must usually run "PRAGMA writable_schema=RESET" after |
| 186408 | ** using this test-control, before it will take full effect. failing |
| 186409 | ** to reset the schema can result in some unexpected behavior. |
| @@ -187746,11 +187847,11 @@ | |
| 187746 | ** } |
| 187747 | ** |
| 187748 | ** Here, array { X } means zero or more occurrences of X, adjacent in |
| 187749 | ** memory. A "position" is an index of a token in the token stream |
| 187750 | ** generated by the tokenizer. Note that POS_END and POS_COLUMN occur |
| 187751 | ** in the same logical place as the position element, and act as sentinals |
| 187752 | ** ending a position list array. POS_END is 0. POS_COLUMN is 1. |
| 187753 | ** The positions numbers are not stored literally but rather as two more |
| 187754 | ** than the difference from the prior position, or the just the position plus |
| 187755 | ** 2 for the first position. Example: |
| 187756 | ** |
| @@ -188433,10 +188534,23 @@ | |
| 188433 | |
| 188434 | #define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) |
| 188435 | #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) |
| 188436 | |
| 188437 | #define deliberate_fall_through |
| 188438 | |
| 188439 | #endif /* SQLITE_AMALGAMATION */ |
| 188440 | |
| 188441 | #ifdef SQLITE_DEBUG |
| 188442 | SQLITE_PRIVATE int sqlite3Fts3Corrupt(void); |
| @@ -188538,11 +188652,11 @@ | |
| 188538 | int inTransaction; /* True after xBegin but before xCommit/xRollback */ |
| 188539 | int mxSavepoint; /* Largest valid xSavepoint integer */ |
| 188540 | #endif |
| 188541 | |
| 188542 | #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) |
| 188543 | /* True to disable the incremental doclist optimization. This is controled |
| 188544 | ** by special insert command 'test-no-incr-doclist'. */ |
| 188545 | int bNoIncrDoclist; |
| 188546 | |
| 188547 | /* Number of segments in a level */ |
| 188548 | int nMergeCount; |
| @@ -188590,11 +188704,11 @@ | |
| 188590 | #define FTS3_EVAL_NEXT 1 |
| 188591 | #define FTS3_EVAL_MATCHINFO 2 |
| 188592 | |
| 188593 | /* |
| 188594 | ** The Fts3Cursor.eSearch member is always set to one of the following. |
| 188595 | ** Actualy, Fts3Cursor.eSearch can be greater than or equal to |
| 188596 | ** FTS3_FULLTEXT_SEARCH. If so, then Fts3Cursor.eSearch - 2 is the index |
| 188597 | ** of the column to be searched. For example, in |
| 188598 | ** |
| 188599 | ** CREATE VIRTUAL TABLE ex1 USING fts3(a,b,c,d); |
| 188600 | ** SELECT docid FROM ex1 WHERE b MATCH 'one two three'; |
| @@ -188663,12 +188777,16 @@ | |
| 188663 | /* Variables below this point are populated by fts3_expr.c when parsing |
| 188664 | ** a MATCH expression. Everything above is part of the evaluation phase. |
| 188665 | */ |
| 188666 | int nToken; /* Number of tokens in the phrase */ |
| 188667 | int iColumn; /* Index of column this phrase must match */ |
| 188668 | Fts3PhraseToken aToken[1]; /* One entry for each token in the phrase */ |
| 188669 | }; |
| 188670 | |
| 188671 | /* |
| 188672 | ** A tree of these objects forms the RHS of a MATCH operator. |
| 188673 | ** |
| 188674 | ** If Fts3Expr.eType is FTSQUERY_PHRASE and isLoaded is true, then aDoclist |
| @@ -191243,11 +191361,11 @@ | |
| 191243 | ** |
| 191244 | ** The space required to store the output is therefore the sum of the |
| 191245 | ** sizes of the two inputs, plus enough space for exactly one of the input |
| 191246 | ** docids to grow. |
| 191247 | ** |
| 191248 | ** A symetric argument may be made if the doclists are in descending |
| 191249 | ** order. |
| 191250 | */ |
| 191251 | aOut = sqlite3_malloc64((i64)n1+n2+FTS3_VARINT_MAX-1+FTS3_BUFFER_PADDING); |
| 191252 | if( !aOut ) return SQLITE_NOMEM; |
| 191253 | |
| @@ -193341,11 +193459,11 @@ | |
| 193341 | ** |
| 193342 | ** * features at least one token that uses an incremental doclist, and |
| 193343 | ** |
| 193344 | ** * does not contain any deferred tokens. |
| 193345 | ** |
| 193346 | ** Advance it to the next matching documnent in the database and populate |
| 193347 | ** the Fts3Doclist.pList and nList fields. |
| 193348 | ** |
| 193349 | ** If there is no "next" entry and no error occurs, then *pbEof is set to |
| 193350 | ** 1 before returning. Otherwise, if no error occurs and the iterator is |
| 193351 | ** successfully advanced, *pbEof is set to 0. |
| @@ -194348,11 +194466,11 @@ | |
| 194348 | |
| 194349 | return rc; |
| 194350 | } |
| 194351 | |
| 194352 | /* |
| 194353 | ** Restart interation for expression pExpr so that the next call to |
| 194354 | ** fts3EvalNext() visits the first row. Do not allow incremental |
| 194355 | ** loading or merging of phrase doclists for this iteration. |
| 194356 | ** |
| 194357 | ** If *pRc is other than SQLITE_OK when this function is called, it is |
| 194358 | ** a no-op. If an error occurs within this function, *pRc is set to an |
| @@ -195539,10 +195657,27 @@ | |
| 195539 | /* |
| 195540 | ** Function getNextNode(), which is called by fts3ExprParse(), may itself |
| 195541 | ** call fts3ExprParse(). So this forward declaration is required. |
| 195542 | */ |
| 195543 | static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *); |
| 195544 | |
| 195545 | /* |
| 195546 | ** Extract the next token from buffer z (length n) using the tokenizer |
| 195547 | ** and other information (column names etc.) in pParse. Create an Fts3Expr |
| 195548 | ** structure of type FTSQUERY_PHRASE containing a phrase consisting of this |
| @@ -195564,38 +195699,42 @@ | |
| 195564 | sqlite3_tokenizer *pTokenizer = pParse->pTokenizer; |
| 195565 | sqlite3_tokenizer_module const *pModule = pTokenizer->pModule; |
| 195566 | int rc; |
| 195567 | sqlite3_tokenizer_cursor *pCursor; |
| 195568 | Fts3Expr *pRet = 0; |
| 195569 | int i = 0; |
| 195570 | |
| 195571 | /* Set variable i to the maximum number of bytes of input to tokenize. */ |
| 195572 | for(i=0; i<n; i++){ |
| 195573 | if( sqlite3_fts3_enable_parentheses && (z[i]=='(' || z[i]==')') ) break; |
| 195574 | if( z[i]=='"' ) break; |
| 195575 | } |
| 195576 | |
| 195577 | *pnConsumed = i; |
| 195578 | rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, i, &pCursor); |
| 195579 | if( rc==SQLITE_OK ){ |
| 195580 | const char *zToken; |
| 195581 | int nToken = 0, iStart = 0, iEnd = 0, iPosition = 0; |
| 195582 | sqlite3_int64 nByte; /* total space to allocate */ |
| 195583 | |
| 195584 | rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition); |
| 195585 | if( rc==SQLITE_OK ){ |
| 195586 | nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken; |
| 195587 | pRet = (Fts3Expr *)sqlite3Fts3MallocZero(nByte); |
| 195588 | if( !pRet ){ |
| 195589 | rc = SQLITE_NOMEM; |
| 195590 | }else{ |
| 195591 | pRet->eType = FTSQUERY_PHRASE; |
| 195592 | pRet->pPhrase = (Fts3Phrase *)&pRet[1]; |
| 195593 | pRet->pPhrase->nToken = 1; |
| 195594 | pRet->pPhrase->iColumn = iCol; |
| 195595 | pRet->pPhrase->aToken[0].n = nToken; |
| 195596 | pRet->pPhrase->aToken[0].z = (char *)&pRet->pPhrase[1]; |
| 195597 | memcpy(pRet->pPhrase->aToken[0].z, zToken, nToken); |
| 195598 | |
| 195599 | if( iEnd<n && z[iEnd]=='*' ){ |
| 195600 | pRet->pPhrase->aToken[0].isPrefix = 1; |
| 195601 | iEnd++; |
| @@ -195615,11 +195754,15 @@ | |
| 195615 | } |
| 195616 | } |
| 195617 | |
| 195618 | } |
| 195619 | *pnConsumed = iEnd; |
| 195620 | }else if( i && rc==SQLITE_DONE ){ |
| 195621 | rc = SQLITE_OK; |
| 195622 | } |
| 195623 | |
| 195624 | pModule->xClose(pCursor); |
| 195625 | } |
| @@ -195664,11 +195807,11 @@ | |
| 195664 | Fts3Expr *p = 0; |
| 195665 | sqlite3_tokenizer_cursor *pCursor = 0; |
| 195666 | char *zTemp = 0; |
| 195667 | i64 nTemp = 0; |
| 195668 | |
| 195669 | const int nSpace = sizeof(Fts3Expr) + sizeof(Fts3Phrase); |
| 195670 | int nToken = 0; |
| 195671 | |
| 195672 | /* The final Fts3Expr data structure, including the Fts3Phrase, |
| 195673 | ** Fts3PhraseToken structures token buffers are all stored as a single |
| 195674 | ** allocation so that the expression can be freed with a single call to |
| @@ -196036,11 +196179,11 @@ | |
| 196036 | int eType = p->eType; |
| 196037 | isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft); |
| 196038 | |
| 196039 | /* The isRequirePhrase variable is set to true if a phrase or |
| 196040 | ** an expression contained in parenthesis is required. If a |
| 196041 | ** binary operator (AND, OR, NOT or NEAR) is encounted when |
| 196042 | ** isRequirePhrase is set, this is a syntax error. |
| 196043 | */ |
| 196044 | if( !isPhrase && isRequirePhrase ){ |
| 196045 | sqlite3Fts3ExprFree(p); |
| 196046 | rc = SQLITE_ERROR; |
| @@ -196618,11 +196761,10 @@ | |
| 196618 | pTokenizer, 0, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr |
| 196619 | ); |
| 196620 | } |
| 196621 | |
| 196622 | if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){ |
| 196623 | sqlite3Fts3ExprFree(pExpr); |
| 196624 | sqlite3_result_error(context, "Error parsing expression", -1); |
| 196625 | }else if( rc==SQLITE_NOMEM || !(zBuf = exprToString(pExpr, 0)) ){ |
| 196626 | sqlite3_result_error_nomem(context); |
| 196627 | }else{ |
| 196628 | sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); |
| @@ -196861,11 +197003,11 @@ | |
| 196861 | pEntry->count++; |
| 196862 | pEntry->chain = pNew; |
| 196863 | } |
| 196864 | |
| 196865 | |
| 196866 | /* Resize the hash table so that it cantains "new_size" buckets. |
| 196867 | ** "new_size" must be a power of 2. The hash table might fail |
| 196868 | ** to resize if sqliteMalloc() fails. |
| 196869 | ** |
| 196870 | ** Return non-zero if a memory allocation error occurs. |
| 196871 | */ |
| @@ -197316,11 +197458,11 @@ | |
| 197316 | isConsonant(z+2); |
| 197317 | } |
| 197318 | |
| 197319 | /* |
| 197320 | ** If the word ends with zFrom and xCond() is true for the stem |
| 197321 | ** of the word that preceeds the zFrom ending, then change the |
| 197322 | ** ending to zTo. |
| 197323 | ** |
| 197324 | ** The input word *pz and zFrom are both in reverse order. zTo |
| 197325 | ** is in normal order. |
| 197326 | ** |
| @@ -202899,11 +203041,11 @@ | |
| 202899 | ** previous term. Before this function returns, it is updated to contain a |
| 202900 | ** copy of zTerm/nTerm. |
| 202901 | ** |
| 202902 | ** It is assumed that the buffer associated with pNode is already large |
| 202903 | ** enough to accommodate the new entry. The buffer associated with pPrev |
| 202904 | ** is extended by this function if requrired. |
| 202905 | ** |
| 202906 | ** If an error (i.e. OOM condition) occurs, an SQLite error code is |
| 202907 | ** returned. Otherwise, SQLITE_OK. |
| 202908 | */ |
| 202909 | static int fts3AppendToNode( |
| @@ -204562,11 +204704,11 @@ | |
| 204562 | #endif |
| 204563 | |
| 204564 | /* |
| 204565 | ** SQLite value pRowid contains the rowid of a row that may or may not be |
| 204566 | ** present in the FTS3 table. If it is, delete it and adjust the contents |
| 204567 | ** of subsiduary data structures accordingly. |
| 204568 | */ |
| 204569 | static int fts3DeleteByRowid( |
| 204570 | Fts3Table *p, |
| 204571 | sqlite3_value *pRowid, |
| 204572 | int *pnChng, /* IN/OUT: Decrement if row is deleted */ |
| @@ -204888,12 +205030,16 @@ | |
| 204888 | struct MatchinfoBuffer { |
| 204889 | u8 aRef[3]; |
| 204890 | int nElem; |
| 204891 | int bGlobal; /* Set if global data is loaded */ |
| 204892 | char *zMatchinfo; |
| 204893 | u32 aMatchinfo[1]; |
| 204894 | }; |
| 204895 | |
| 204896 | |
| 204897 | /* |
| 204898 | ** The snippet() and offsets() functions both return text values. An instance |
| 204899 | ** of the following structure is used to accumulate those values while the |
| @@ -204915,17 +205061,17 @@ | |
| 204915 | ** Allocate a two-slot MatchinfoBuffer object. |
| 204916 | */ |
| 204917 | static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){ |
| 204918 | MatchinfoBuffer *pRet; |
| 204919 | sqlite3_int64 nByte = sizeof(u32) * (2*(sqlite3_int64)nElem + 1) |
| 204920 | + sizeof(MatchinfoBuffer); |
| 204921 | sqlite3_int64 nStr = strlen(zMatchinfo); |
| 204922 | |
| 204923 | pRet = sqlite3Fts3MallocZero(nByte + nStr+1); |
| 204924 | if( pRet ){ |
| 204925 | pRet->aMatchinfo[0] = (u8*)(&pRet->aMatchinfo[1]) - (u8*)pRet; |
| 204926 | pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0] |
| 204927 | + sizeof(u32)*((int)nElem+1); |
| 204928 | pRet->nElem = (int)nElem; |
| 204929 | pRet->zMatchinfo = ((char*)pRet) + nByte; |
| 204930 | memcpy(pRet->zMatchinfo, zMatchinfo, nStr+1); |
| 204931 | pRet->aRef[0] = 1; |
| @@ -204935,14 +205081,14 @@ | |
| 204935 | } |
| 204936 | |
| 204937 | static void fts3MIBufferFree(void *p){ |
| 204938 | MatchinfoBuffer *pBuf = (MatchinfoBuffer*)((u8*)p - ((u32*)p)[-1]); |
| 204939 | |
| 204940 | assert( (u32*)p==&pBuf->aMatchinfo[1] |
| 204941 | || (u32*)p==&pBuf->aMatchinfo[pBuf->nElem+2] |
| 204942 | ); |
| 204943 | if( (u32*)p==&pBuf->aMatchinfo[1] ){ |
| 204944 | pBuf->aRef[1] = 0; |
| 204945 | }else{ |
| 204946 | pBuf->aRef[2] = 0; |
| 204947 | } |
| 204948 | |
| @@ -204955,32 +205101,32 @@ | |
| 204955 | void (*xRet)(void*) = 0; |
| 204956 | u32 *aOut = 0; |
| 204957 | |
| 204958 | if( p->aRef[1]==0 ){ |
| 204959 | p->aRef[1] = 1; |
| 204960 | aOut = &p->aMatchinfo[1]; |
| 204961 | xRet = fts3MIBufferFree; |
| 204962 | } |
| 204963 | else if( p->aRef[2]==0 ){ |
| 204964 | p->aRef[2] = 1; |
| 204965 | aOut = &p->aMatchinfo[p->nElem+2]; |
| 204966 | xRet = fts3MIBufferFree; |
| 204967 | }else{ |
| 204968 | aOut = (u32*)sqlite3_malloc64(p->nElem * sizeof(u32)); |
| 204969 | if( aOut ){ |
| 204970 | xRet = sqlite3_free; |
| 204971 | if( p->bGlobal ) memcpy(aOut, &p->aMatchinfo[1], p->nElem*sizeof(u32)); |
| 204972 | } |
| 204973 | } |
| 204974 | |
| 204975 | *paOut = aOut; |
| 204976 | return xRet; |
| 204977 | } |
| 204978 | |
| 204979 | static void fts3MIBufferSetGlobal(MatchinfoBuffer *p){ |
| 204980 | p->bGlobal = 1; |
| 204981 | memcpy(&p->aMatchinfo[2+p->nElem], &p->aMatchinfo[1], p->nElem*sizeof(u32)); |
| 204982 | } |
| 204983 | |
| 204984 | /* |
| 204985 | ** Free a MatchinfoBuffer object allocated using fts3MIBufferNew() |
| 204986 | */ |
| @@ -205391,11 +205537,11 @@ | |
| 205391 | if( nAppend<0 ){ |
| 205392 | nAppend = (int)strlen(zAppend); |
| 205393 | } |
| 205394 | |
| 205395 | /* If there is insufficient space allocated at StrBuffer.z, use realloc() |
| 205396 | ** to grow the buffer until so that it is big enough to accomadate the |
| 205397 | ** appended data. |
| 205398 | */ |
| 205399 | if( pStr->n+nAppend+1>=pStr->nAlloc ){ |
| 205400 | sqlite3_int64 nAlloc = pStr->nAlloc+(sqlite3_int64)nAppend+100; |
| 205401 | char *zNew = sqlite3_realloc64(pStr->z, nAlloc); |
| @@ -207766,11 +207912,11 @@ | |
| 207766 | ** |
| 207767 | ** When a match if found, the matching entry is moved to become the |
| 207768 | ** most-recently used entry if it isn't so already. |
| 207769 | ** |
| 207770 | ** The JsonParse object returned still belongs to the Cache and might |
| 207771 | ** be deleted at any moment. If the caller whants the JsonParse to |
| 207772 | ** linger, it needs to increment the nPJRef reference counter. |
| 207773 | */ |
| 207774 | static JsonParse *jsonCacheSearch( |
| 207775 | sqlite3_context *ctx, /* The SQL statement context holding the cache */ |
| 207776 | sqlite3_value *pArg /* Function argument containing SQL text */ |
| @@ -210811,11 +210957,11 @@ | |
| 210811 | ** |
| 210812 | ** This goes against all historical documentation about how the SQLite |
| 210813 | ** JSON functions were suppose to work. From the beginning, blob was |
| 210814 | ** reserved for expansion and a blob value should have raised an error. |
| 210815 | ** But it did not, due to a bug. And many applications came to depend |
| 210816 | ** upon this buggy behavior, espeically when using the CLI and reading |
| 210817 | ** JSON text using readfile(), which returns a blob. For this reason |
| 210818 | ** we will continue to support the bug moving forward. |
| 210819 | ** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d |
| 210820 | */ |
| 210821 | } |
| @@ -212911,10 +213057,18 @@ | |
| 212911 | # define NEVER(X) ((X)?(assert(0),1):0) |
| 212912 | #else |
| 212913 | # define ALWAYS(X) (X) |
| 212914 | # define NEVER(X) (X) |
| 212915 | #endif |
| 212916 | #endif /* !defined(SQLITE_AMALGAMATION) */ |
| 212917 | |
| 212918 | /* Macro to check for 4-byte alignment. Only used inside of assert() */ |
| 212919 | #ifdef SQLITE_DEBUG |
| 212920 | # define FOUR_BYTE_ALIGNED(X) ((((char*)(X) - (char*)0) & 3)==0) |
| @@ -213231,13 +213385,17 @@ | |
| 213231 | struct RtreeMatchArg { |
| 213232 | u32 iSize; /* Size of this object */ |
| 213233 | RtreeGeomCallback cb; /* Info about the callback functions */ |
| 213234 | int nParam; /* Number of parameters to the SQL function */ |
| 213235 | sqlite3_value **apSqlParam; /* Original SQL parameter values */ |
| 213236 | RtreeDValue aParam[1]; /* Values for parameters to the SQL function */ |
| 213237 | }; |
| 213238 | |
| 213239 | #ifndef MAX |
| 213240 | # define MAX(x,y) ((x) < (y) ? (y) : (x)) |
| 213241 | #endif |
| 213242 | #ifndef MIN |
| 213243 | # define MIN(x,y) ((x) > (y) ? (y) : (x)) |
| @@ -214922,11 +215080,11 @@ | |
| 214922 | |
| 214923 | return rc; |
| 214924 | } |
| 214925 | |
| 214926 | /* |
| 214927 | ** Return the N-dimensional volumn of the cell stored in *p. |
| 214928 | */ |
| 214929 | static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){ |
| 214930 | RtreeDValue area = (RtreeDValue)1; |
| 214931 | assert( pRtree->nDim>=1 && pRtree->nDim<=5 ); |
| 214932 | #ifndef SQLITE_RTREE_INT_ONLY |
| @@ -216688,11 +216846,11 @@ | |
| 216688 | } |
| 216689 | |
| 216690 | /* |
| 216691 | ** The second and subsequent arguments to this function are a printf() |
| 216692 | ** style format string and arguments. This function formats the string and |
| 216693 | ** appends it to the report being accumuated in pCheck. |
| 216694 | */ |
| 216695 | static void rtreeCheckAppendMsg(RtreeCheck *pCheck, const char *zFmt, ...){ |
| 216696 | va_list ap; |
| 216697 | va_start(ap, zFmt); |
| 216698 | if( pCheck->rc==SQLITE_OK && pCheck->nErr<RTREE_CHECK_MAX_ERROR ){ |
| @@ -217876,11 +218034,11 @@ | |
| 217876 | |
| 217877 | /* |
| 217878 | ** Determine if point (x0,y0) is beneath line segment (x1,y1)->(x2,y2). |
| 217879 | ** Returns: |
| 217880 | ** |
| 217881 | ** +2 x0,y0 is on the line segement |
| 217882 | ** |
| 217883 | ** +1 x0,y0 is beneath line segment |
| 217884 | ** |
| 217885 | ** 0 x0,y0 is not on or beneath the line segment or the line segment |
| 217886 | ** is vertical and x0,y0 is not on the line segment |
| @@ -217982,11 +218140,11 @@ | |
| 217982 | } |
| 217983 | sqlite3_free(p1); |
| 217984 | sqlite3_free(p2); |
| 217985 | } |
| 217986 | |
| 217987 | /* Objects used by the overlap algorihm. */ |
| 217988 | typedef struct GeoEvent GeoEvent; |
| 217989 | typedef struct GeoSegment GeoSegment; |
| 217990 | typedef struct GeoOverlap GeoOverlap; |
| 217991 | struct GeoEvent { |
| 217992 | double x; /* X coordinate at which event occurs */ |
| @@ -219029,12 +219187,11 @@ | |
| 219029 | RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx); |
| 219030 | RtreeMatchArg *pBlob; |
| 219031 | sqlite3_int64 nBlob; |
| 219032 | int memErr = 0; |
| 219033 | |
| 219034 | nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(RtreeDValue) |
| 219035 | + nArg*sizeof(sqlite3_value*); |
| 219036 | pBlob = (RtreeMatchArg *)sqlite3_malloc64(nBlob); |
| 219037 | if( !pBlob ){ |
| 219038 | sqlite3_result_error_nomem(ctx); |
| 219039 | }else{ |
| 219040 | int i; |
| @@ -220125,11 +220282,11 @@ | |
| 220125 | ** to read from the original database snapshot. In other words, partially |
| 220126 | ** applied transactions are not visible to other clients. |
| 220127 | ** |
| 220128 | ** "RBU" stands for "Resumable Bulk Update". As in a large database update |
| 220129 | ** transmitted via a wireless network to a mobile device. A transaction |
| 220130 | ** applied using this extension is hence refered to as an "RBU update". |
| 220131 | ** |
| 220132 | ** |
| 220133 | ** LIMITATIONS |
| 220134 | ** |
| 220135 | ** An "RBU update" transaction is subject to the following limitations: |
| @@ -220422,11 +220579,11 @@ | |
| 220422 | ** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents |
| 220423 | ** of the state tables within the state database are zeroed. This way, |
| 220424 | ** the next call to sqlite3rbu_vacuum() opens a handle that starts a |
| 220425 | ** new RBU vacuum operation. |
| 220426 | ** |
| 220427 | ** As with sqlite3rbu_open(), Zipvfs users should rever to the comment |
| 220428 | ** describing the sqlite3rbu_create_vfs() API function below for |
| 220429 | ** a description of the complications associated with using RBU with |
| 220430 | ** zipvfs databases. |
| 220431 | */ |
| 220432 | SQLITE_API sqlite3rbu *sqlite3rbu_vacuum( |
| @@ -220518,11 +220675,11 @@ | |
| 220518 | /* |
| 220519 | ** Close an RBU handle. |
| 220520 | ** |
| 220521 | ** If the RBU update has been completely applied, mark the RBU database |
| 220522 | ** as fully applied. Otherwise, assuming no error has occurred, save the |
| 220523 | ** current state of the RBU update appliation to the RBU database. |
| 220524 | ** |
| 220525 | ** If an error has already occurred as part of an sqlite3rbu_step() |
| 220526 | ** or sqlite3rbu_open() call, or if one occurs within this function, an |
| 220527 | ** SQLite error code is returned. Additionally, if pzErrmsg is not NULL, |
| 220528 | ** *pzErrmsg may be set to point to a buffer containing a utf-8 formatted |
| @@ -225444,11 +225601,11 @@ | |
| 225444 | int rc; |
| 225445 | rc = p->pReal->pMethods->xFileSize(p->pReal, pSize); |
| 225446 | |
| 225447 | /* If this is an RBU vacuum operation and this is the target database, |
| 225448 | ** pretend that it has at least one page. Otherwise, SQLite will not |
| 225449 | ** check for the existance of a *-wal file. rbuVfsRead() contains |
| 225450 | ** similar logic. */ |
| 225451 | if( rc==SQLITE_OK && *pSize==0 |
| 225452 | && p->pRbu && rbuIsVacuum(p->pRbu) |
| 225453 | && (p->openFlags & SQLITE_OPEN_MAIN_DB) |
| 225454 | ){ |
| @@ -228674,11 +228831,11 @@ | |
| 228674 | } |
| 228675 | |
| 228676 | /* |
| 228677 | ** This function is called to initialize the SessionTable.nCol, azCol[] |
| 228678 | ** abPK[] and azDflt[] members of SessionTable object pTab. If these |
| 228679 | ** fields are already initilialized, this function is a no-op. |
| 228680 | ** |
| 228681 | ** If an error occurs, an error code is stored in sqlite3_session.rc and |
| 228682 | ** non-zero returned. Or, if no error occurs but the table has no primary |
| 228683 | ** key, sqlite3_session.rc is left set to SQLITE_OK and non-zero returned to |
| 228684 | ** indicate that updates on this table should be ignored. SessionTable.abPK |
| @@ -230497,11 +230654,11 @@ | |
| 230497 | int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */ |
| 230498 | void **ppChangeset /* OUT: Buffer containing changeset */ |
| 230499 | ){ |
| 230500 | sqlite3 *db = pSession->db; /* Source database handle */ |
| 230501 | SessionTable *pTab; /* Used to iterate through attached tables */ |
| 230502 | SessionBuffer buf = {0,0,0}; /* Buffer in which to accumlate changeset */ |
| 230503 | int rc; /* Return code */ |
| 230504 | |
| 230505 | assert( xOutput==0 || (pnChangeset==0 && ppChangeset==0) ); |
| 230506 | assert( xOutput!=0 || (pnChangeset!=0 && ppChangeset!=0) ); |
| 230507 | |
| @@ -234931,10 +235088,22 @@ | |
| 234931 | # define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&3)==0) |
| 234932 | #else |
| 234933 | # define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0) |
| 234934 | #endif |
| 234935 | |
| 234936 | #endif |
| 234937 | |
| 234938 | /* Truncate very long tokens to this many bytes. Hard limit is |
| 234939 | ** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset |
| 234940 | ** field that occurs at the start of each leaf page (see fts5_index.c). */ |
| @@ -235003,14 +235172,15 @@ | |
| 235003 | ** |
| 235004 | ** This object is used by fts5_expr.c and fts5_index.c. |
| 235005 | */ |
| 235006 | struct Fts5Colset { |
| 235007 | int nCol; |
| 235008 | int aiCol[1]; |
| 235009 | }; |
| 235010 | |
| 235011 | |
| 235012 | |
| 235013 | /************************************************************************** |
| 235014 | ** Interface to code in fts5_config.c. fts5_config.c contains contains code |
| 235015 | ** to parse the arguments passed to the CREATE VIRTUAL TABLE statement. |
| 235016 | */ |
| @@ -235835,11 +236005,11 @@ | |
| 235835 | ************************************************************************* |
| 235836 | ** Driver template for the LEMON parser generator. |
| 235837 | ** |
| 235838 | ** The "lemon" program processes an LALR(1) input grammar file, then uses |
| 235839 | ** this template to construct a parser. The "lemon" program inserts text |
| 235840 | ** at each "%%" line. Also, any "P-a-r-s-e" identifer prefix (without the |
| 235841 | ** interstitial "-" characters) contained in this template is changed into |
| 235842 | ** the value of the %name directive from the grammar. Otherwise, the content |
| 235843 | ** of this template is copied straight through into the generate parser |
| 235844 | ** source file. |
| 235845 | ** |
| @@ -237989,11 +238159,11 @@ | |
| 237989 | ** where "N" is the total number of documents in the set and nHit |
| 237990 | ** is the number that contain at least one instance of the phrase |
| 237991 | ** under consideration. |
| 237992 | ** |
| 237993 | ** The problem with this is that if (N < 2*nHit), the IDF is |
| 237994 | ** negative. Which is undesirable. So the mimimum allowable IDF is |
| 237995 | ** (1e-6) - roughly the same as a term that appears in just over |
| 237996 | ** half of set of 5,000,000 documents. */ |
| 237997 | double idf = log( (nRow - nHit + 0.5) / (nHit + 0.5) ); |
| 237998 | if( idf<=0.0 ) idf = 1e-6; |
| 237999 | p->aIDF[i] = idf; |
| @@ -238452,11 +238622,11 @@ | |
| 238452 | ** |
| 238453 | ** * All non-ASCII characters, |
| 238454 | ** * The 52 upper and lower case ASCII characters, and |
| 238455 | ** * The 10 integer ASCII characters. |
| 238456 | ** * The underscore character "_" (0x5F). |
| 238457 | ** * The unicode "subsitute" character (0x1A). |
| 238458 | */ |
| 238459 | static int sqlite3Fts5IsBareword(char t){ |
| 238460 | u8 aBareword[128] = { |
| 238461 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 .. 0x0F */ |
| 238462 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* 0x10 .. 0x1F */ |
| @@ -239770,12 +239940,16 @@ | |
| 239770 | Fts5ExprNearset *pNear; /* For FTS5_STRING - cluster of phrases */ |
| 239771 | |
| 239772 | /* Child nodes. For a NOT node, this array always contains 2 entries. For |
| 239773 | ** AND or OR nodes, it contains 2 or more entries. */ |
| 239774 | int nChild; /* Number of child nodes */ |
| 239775 | Fts5ExprNode *apChild[1]; /* Array of child nodes */ |
| 239776 | }; |
| 239777 | |
| 239778 | #define Fts5NodeIsString(p) ((p)->eType==FTS5_TERM || (p)->eType==FTS5_STRING) |
| 239779 | |
| 239780 | /* |
| 239781 | ** Invoke the xNext method of an Fts5ExprNode object. This macro should be |
| @@ -239803,24 +239977,31 @@ | |
| 239803 | */ |
| 239804 | struct Fts5ExprPhrase { |
| 239805 | Fts5ExprNode *pNode; /* FTS5_STRING node this phrase is part of */ |
| 239806 | Fts5Buffer poslist; /* Current position list */ |
| 239807 | int nTerm; /* Number of entries in aTerm[] */ |
| 239808 | Fts5ExprTerm aTerm[1]; /* Terms that make up this phrase */ |
| 239809 | }; |
| 239810 | |
| 239811 | /* |
| 239812 | ** One or more phrases that must appear within a certain token distance of |
| 239813 | ** each other within each matching document. |
| 239814 | */ |
| 239815 | struct Fts5ExprNearset { |
| 239816 | int nNear; /* NEAR parameter */ |
| 239817 | Fts5Colset *pColset; /* Columns to search (NULL -> all columns) */ |
| 239818 | int nPhrase; /* Number of entries in aPhrase[] array */ |
| 239819 | Fts5ExprPhrase *apPhrase[1]; /* Array of phrase pointers */ |
| 239820 | }; |
| 239821 | |
| 239822 | |
| 239823 | /* |
| 239824 | ** Parse context. |
| 239825 | */ |
| 239826 | struct Fts5Parse { |
| @@ -239976,11 +240157,11 @@ | |
| 239976 | assert_expr_depth_ok(sParse.rc, sParse.pExpr); |
| 239977 | |
| 239978 | /* If the LHS of the MATCH expression was a user column, apply the |
| 239979 | ** implicit column-filter. */ |
| 239980 | if( sParse.rc==SQLITE_OK && iCol<pConfig->nCol ){ |
| 239981 | int n = sizeof(Fts5Colset); |
| 239982 | Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&sParse.rc, n); |
| 239983 | if( pColset ){ |
| 239984 | pColset->nCol = 1; |
| 239985 | pColset->aiCol[0] = iCol; |
| 239986 | sqlite3Fts5ParseSetColset(&sParse, sParse.pExpr, pColset); |
| @@ -241334,11 +241515,11 @@ | |
| 241334 | Fts5ExprNearset *pRet = 0; |
| 241335 | |
| 241336 | if( pParse->rc==SQLITE_OK ){ |
| 241337 | if( pNear==0 ){ |
| 241338 | sqlite3_int64 nByte; |
| 241339 | nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*); |
| 241340 | pRet = sqlite3_malloc64(nByte); |
| 241341 | if( pRet==0 ){ |
| 241342 | pParse->rc = SQLITE_NOMEM; |
| 241343 | }else{ |
| 241344 | memset(pRet, 0, (size_t)nByte); |
| @@ -241345,11 +241526,11 @@ | |
| 241345 | } |
| 241346 | }else if( (pNear->nPhrase % SZALLOC)==0 ){ |
| 241347 | int nNew = pNear->nPhrase + SZALLOC; |
| 241348 | sqlite3_int64 nByte; |
| 241349 | |
| 241350 | nByte = sizeof(Fts5ExprNearset) + nNew * sizeof(Fts5ExprPhrase*); |
| 241351 | pRet = (Fts5ExprNearset*)sqlite3_realloc64(pNear, nByte); |
| 241352 | if( pRet==0 ){ |
| 241353 | pParse->rc = SQLITE_NOMEM; |
| 241354 | } |
| 241355 | }else{ |
| @@ -241436,16 +241617,16 @@ | |
| 241436 | if( pPhrase==0 || (pPhrase->nTerm % SZALLOC)==0 ){ |
| 241437 | Fts5ExprPhrase *pNew; |
| 241438 | int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0); |
| 241439 | |
| 241440 | pNew = (Fts5ExprPhrase*)sqlite3_realloc64(pPhrase, |
| 241441 | sizeof(Fts5ExprPhrase) + sizeof(Fts5ExprTerm) * nNew |
| 241442 | ); |
| 241443 | if( pNew==0 ){ |
| 241444 | rc = SQLITE_NOMEM; |
| 241445 | }else{ |
| 241446 | if( pPhrase==0 ) memset(pNew, 0, sizeof(Fts5ExprPhrase)); |
| 241447 | pCtx->pPhrase = pPhrase = pNew; |
| 241448 | pNew->nTerm = nNew - SZALLOC; |
| 241449 | } |
| 241450 | } |
| 241451 | |
| @@ -241549,11 +241730,11 @@ | |
| 241549 | } |
| 241550 | |
| 241551 | if( sCtx.pPhrase==0 ){ |
| 241552 | /* This happens when parsing a token or quoted phrase that contains |
| 241553 | ** no token characters at all. (e.g ... MATCH '""'). */ |
| 241554 | sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, sizeof(Fts5ExprPhrase)); |
| 241555 | }else if( sCtx.pPhrase->nTerm ){ |
| 241556 | sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = (u8)bPrefix; |
| 241557 | } |
| 241558 | assert( pParse->apPhrase!=0 ); |
| 241559 | pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase; |
| @@ -241584,23 +241765,22 @@ | |
| 241584 | if( rc==SQLITE_OK ){ |
| 241585 | pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc, |
| 241586 | sizeof(Fts5ExprPhrase*)); |
| 241587 | } |
| 241588 | if( rc==SQLITE_OK ){ |
| 241589 | pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc, |
| 241590 | sizeof(Fts5ExprNode)); |
| 241591 | } |
| 241592 | if( rc==SQLITE_OK ){ |
| 241593 | pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc, |
| 241594 | sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*)); |
| 241595 | } |
| 241596 | if( rc==SQLITE_OK && ALWAYS(pOrig!=0) ){ |
| 241597 | Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset; |
| 241598 | if( pColsetOrig ){ |
| 241599 | sqlite3_int64 nByte; |
| 241600 | Fts5Colset *pColset; |
| 241601 | nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int); |
| 241602 | pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte); |
| 241603 | if( pColset ){ |
| 241604 | memcpy(pColset, pColsetOrig, (size_t)nByte); |
| 241605 | } |
| 241606 | pNew->pRoot->pNear->pColset = pColset; |
| @@ -241624,11 +241804,11 @@ | |
| 241624 | } |
| 241625 | } |
| 241626 | }else{ |
| 241627 | /* This happens when parsing a token or quoted phrase that contains |
| 241628 | ** no token characters at all. (e.g ... MATCH '""'). */ |
| 241629 | sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase)); |
| 241630 | } |
| 241631 | } |
| 241632 | |
| 241633 | if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){ |
| 241634 | /* All the allocations succeeded. Put the expression object together. */ |
| @@ -241718,11 +241898,11 @@ | |
| 241718 | Fts5Colset *pNew; /* New colset object to return */ |
| 241719 | |
| 241720 | assert( pParse->rc==SQLITE_OK ); |
| 241721 | assert( iCol>=0 && iCol<pParse->pConfig->nCol ); |
| 241722 | |
| 241723 | pNew = sqlite3_realloc64(p, sizeof(Fts5Colset) + sizeof(int)*nCol); |
| 241724 | if( pNew==0 ){ |
| 241725 | pParse->rc = SQLITE_NOMEM; |
| 241726 | }else{ |
| 241727 | int *aiCol = pNew->aiCol; |
| 241728 | int i, j; |
| @@ -241753,11 +241933,11 @@ | |
| 241753 | static Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse *pParse, Fts5Colset *p){ |
| 241754 | Fts5Colset *pRet; |
| 241755 | int nCol = pParse->pConfig->nCol; |
| 241756 | |
| 241757 | pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc, |
| 241758 | sizeof(Fts5Colset) + sizeof(int)*nCol |
| 241759 | ); |
| 241760 | if( pRet ){ |
| 241761 | int i; |
| 241762 | int iOld = 0; |
| 241763 | for(i=0; i<nCol; i++){ |
| @@ -241814,11 +241994,11 @@ | |
| 241814 | ** fails, (*pRc) is set to SQLITE_NOMEM and NULL is returned. |
| 241815 | */ |
| 241816 | static Fts5Colset *fts5CloneColset(int *pRc, Fts5Colset *pOrig){ |
| 241817 | Fts5Colset *pRet; |
| 241818 | if( pOrig ){ |
| 241819 | sqlite3_int64 nByte = sizeof(Fts5Colset) + (pOrig->nCol-1) * sizeof(int); |
| 241820 | pRet = (Fts5Colset*)sqlite3Fts5MallocZero(pRc, nByte); |
| 241821 | if( pRet ){ |
| 241822 | memcpy(pRet, pOrig, (size_t)nByte); |
| 241823 | } |
| 241824 | }else{ |
| @@ -241982,21 +242162,21 @@ | |
| 241982 | Fts5ExprNode *pRet; |
| 241983 | |
| 241984 | assert( pNear->nPhrase==1 ); |
| 241985 | assert( pParse->bPhraseToAnd ); |
| 241986 | |
| 241987 | nByte = sizeof(Fts5ExprNode) + nTerm*sizeof(Fts5ExprNode*); |
| 241988 | pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); |
| 241989 | if( pRet ){ |
| 241990 | pRet->eType = FTS5_AND; |
| 241991 | pRet->nChild = nTerm; |
| 241992 | pRet->iHeight = 1; |
| 241993 | fts5ExprAssignXNext(pRet); |
| 241994 | pParse->nPhrase--; |
| 241995 | for(ii=0; ii<nTerm; ii++){ |
| 241996 | Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero( |
| 241997 | &pParse->rc, sizeof(Fts5ExprPhrase) |
| 241998 | ); |
| 241999 | if( pPhrase ){ |
| 242000 | if( parseGrowPhraseArray(pParse) ){ |
| 242001 | fts5ExprPhraseFree(pPhrase); |
| 242002 | }else{ |
| @@ -242061,11 +242241,11 @@ | |
| 242061 | nChild = 2; |
| 242062 | if( pLeft->eType==eType ) nChild += pLeft->nChild-1; |
| 242063 | if( pRight->eType==eType ) nChild += pRight->nChild-1; |
| 242064 | } |
| 242065 | |
| 242066 | nByte = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*(nChild-1); |
| 242067 | pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); |
| 242068 | |
| 242069 | if( pRet ){ |
| 242070 | pRet->eType = eType; |
| 242071 | pRet->pNear = pNear; |
| @@ -242936,11 +243116,11 @@ | |
| 242936 | } |
| 242937 | return rc; |
| 242938 | } |
| 242939 | |
| 242940 | /* |
| 242941 | ** Clear the token mappings for all Fts5IndexIter objects mannaged by |
| 242942 | ** the expression passed as the only argument. |
| 242943 | */ |
| 242944 | static void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){ |
| 242945 | int ii; |
| 242946 | for(ii=0; ii<pExpr->nPhrase; ii++){ |
| @@ -242971,11 +243151,11 @@ | |
| 242971 | |
| 242972 | typedef struct Fts5HashEntry Fts5HashEntry; |
| 242973 | |
| 242974 | /* |
| 242975 | ** This file contains the implementation of an in-memory hash table used |
| 242976 | ** to accumuluate "term -> doclist" content before it is flused to a level-0 |
| 242977 | ** segment. |
| 242978 | */ |
| 242979 | |
| 242980 | |
| 242981 | struct Fts5Hash { |
| @@ -243028,11 +243208,11 @@ | |
| 243028 | int iPos; /* Position of last value written */ |
| 243029 | i64 iRowid; /* Rowid of last value written */ |
| 243030 | }; |
| 243031 | |
| 243032 | /* |
| 243033 | ** Eqivalent to: |
| 243034 | ** |
| 243035 | ** char *fts5EntryKey(Fts5HashEntry *pEntry){ return zKey; } |
| 243036 | */ |
| 243037 | #define fts5EntryKey(p) ( ((char *)(&(p)[1])) ) |
| 243038 | |
| @@ -243964,13 +244144,17 @@ | |
| 243964 | int nRef; /* Object reference count */ |
| 243965 | u64 nWriteCounter; /* Total leaves written to level 0 */ |
| 243966 | u64 nOriginCntr; /* Origin value for next top-level segment */ |
| 243967 | int nSegment; /* Total segments in this structure */ |
| 243968 | int nLevel; /* Number of levels in this index */ |
| 243969 | Fts5StructureLevel aLevel[1]; /* Array of nLevel level objects */ |
| 243970 | }; |
| 243971 | |
| 243972 | /* |
| 243973 | ** An object of type Fts5SegWriter is used to write to segments. |
| 243974 | */ |
| 243975 | struct Fts5PageWriter { |
| 243976 | int pgno; /* Page number for this page */ |
| @@ -244096,14 +244280,18 @@ | |
| 244096 | |
| 244097 | /* |
| 244098 | ** Array of tombstone pages. Reference counted. |
| 244099 | */ |
| 244100 | struct Fts5TombstoneArray { |
| 244101 | int nRef; /* Number of pointers to this object */ |
| 244102 | int nTombstone; |
| 244103 | Fts5Data *apTombstone[1]; /* Array of tombstone pages */ |
| 244104 | }; |
| 244105 | |
| 244106 | /* |
| 244107 | ** Argument is a pointer to an Fts5Data structure that contains a |
| 244108 | ** leaf page. |
| 244109 | */ |
| @@ -244169,12 +244357,15 @@ | |
| 244169 | int bRev; /* True to iterate in reverse order */ |
| 244170 | u8 bSkipEmpty; /* True to skip deleted entries */ |
| 244171 | |
| 244172 | i64 iSwitchRowid; /* Firstest rowid of other than aFirst[1] */ |
| 244173 | Fts5CResult *aFirst; /* Current merge state (see above) */ |
| 244174 | Fts5SegIter aSeg[1]; /* Array of segment iterators */ |
| 244175 | }; |
| 244176 | |
| 244177 | /* |
| 244178 | ** An instance of the following type is used to iterate through the contents |
| 244179 | ** of a doclist-index record. |
| 244180 | ** |
| @@ -244198,12 +244389,16 @@ | |
| 244198 | i64 iRowid; /* First rowid on leaf iLeafPgno */ |
| 244199 | }; |
| 244200 | struct Fts5DlidxIter { |
| 244201 | int nLvl; |
| 244202 | int iSegid; |
| 244203 | Fts5DlidxLvl aLvl[1]; |
| 244204 | }; |
| 244205 | |
| 244206 | static void fts5PutU16(u8 *aOut, u16 iVal){ |
| 244207 | aOut[0] = (iVal>>8); |
| 244208 | aOut[1] = (iVal&0xFF); |
| 244209 | } |
| @@ -244568,11 +244763,11 @@ | |
| 244568 | ** an error occurs, (*pRc) is set to an SQLite error code before returning. |
| 244569 | */ |
| 244570 | static void fts5StructureMakeWritable(int *pRc, Fts5Structure **pp){ |
| 244571 | Fts5Structure *p = *pp; |
| 244572 | if( *pRc==SQLITE_OK && p->nRef>1 ){ |
| 244573 | i64 nByte = sizeof(Fts5Structure)+(p->nLevel-1)*sizeof(Fts5StructureLevel); |
| 244574 | Fts5Structure *pNew; |
| 244575 | pNew = (Fts5Structure*)sqlite3Fts5MallocZero(pRc, nByte); |
| 244576 | if( pNew ){ |
| 244577 | int i; |
| 244578 | memcpy(pNew, p, nByte); |
| @@ -244642,14 +244837,11 @@ | |
| 244642 | if( nLevel>FTS5_MAX_SEGMENT || nLevel<0 |
| 244643 | || nSegment>FTS5_MAX_SEGMENT || nSegment<0 |
| 244644 | ){ |
| 244645 | return FTS5_CORRUPT; |
| 244646 | } |
| 244647 | nByte = ( |
| 244648 | sizeof(Fts5Structure) + /* Main structure */ |
| 244649 | sizeof(Fts5StructureLevel) * (nLevel-1) /* aLevel[] array */ |
| 244650 | ); |
| 244651 | pRet = (Fts5Structure*)sqlite3Fts5MallocZero(&rc, nByte); |
| 244652 | |
| 244653 | if( pRet ){ |
| 244654 | pRet->nRef = 1; |
| 244655 | pRet->nLevel = nLevel; |
| @@ -244725,14 +244917,11 @@ | |
| 244725 | fts5StructureMakeWritable(pRc, ppStruct); |
| 244726 | assert( (ppStruct!=0 && (*ppStruct)!=0) || (*pRc)!=SQLITE_OK ); |
| 244727 | if( *pRc==SQLITE_OK ){ |
| 244728 | Fts5Structure *pStruct = *ppStruct; |
| 244729 | int nLevel = pStruct->nLevel; |
| 244730 | sqlite3_int64 nByte = ( |
| 244731 | sizeof(Fts5Structure) + /* Main structure */ |
| 244732 | sizeof(Fts5StructureLevel) * (nLevel+1) /* aLevel[] array */ |
| 244733 | ); |
| 244734 | |
| 244735 | pStruct = sqlite3_realloc64(pStruct, nByte); |
| 244736 | if( pStruct ){ |
| 244737 | memset(&pStruct->aLevel[nLevel], 0, sizeof(Fts5StructureLevel)); |
| 244738 | pStruct->nLevel++; |
| @@ -245267,11 +245456,11 @@ | |
| 245267 | Fts5DlidxIter *pIter = 0; |
| 245268 | int i; |
| 245269 | int bDone = 0; |
| 245270 | |
| 245271 | for(i=0; p->rc==SQLITE_OK && bDone==0; i++){ |
| 245272 | sqlite3_int64 nByte = sizeof(Fts5DlidxIter) + i * sizeof(Fts5DlidxLvl); |
| 245273 | Fts5DlidxIter *pNew; |
| 245274 | |
| 245275 | pNew = (Fts5DlidxIter*)sqlite3_realloc64(pIter, nByte); |
| 245276 | if( pNew==0 ){ |
| 245277 | p->rc = SQLITE_NOMEM; |
| @@ -245485,11 +245674,11 @@ | |
| 245485 | ** leave an error in the Fts5Index object. |
| 245486 | */ |
| 245487 | static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){ |
| 245488 | const int nTomb = pIter->pSeg->nPgTombstone; |
| 245489 | if( nTomb>0 ){ |
| 245490 | int nByte = nTomb * sizeof(Fts5Data*) + sizeof(Fts5TombstoneArray); |
| 245491 | Fts5TombstoneArray *pNew; |
| 245492 | pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte); |
| 245493 | if( pNew ){ |
| 245494 | pNew->nTombstone = nTomb; |
| 245495 | pNew->nRef = 1; |
| @@ -246946,12 +247135,11 @@ | |
| 246946 | Fts5Iter *pNew; |
| 246947 | i64 nSlot; /* Power of two >= nSeg */ |
| 246948 | |
| 246949 | for(nSlot=2; nSlot<nSeg; nSlot=nSlot*2); |
| 246950 | pNew = fts5IdxMalloc(p, |
| 246951 | sizeof(Fts5Iter) + /* pNew */ |
| 246952 | sizeof(Fts5SegIter) * (nSlot-1) + /* pNew->aSeg[] */ |
| 246953 | sizeof(Fts5CResult) * nSlot /* pNew->aFirst[] */ |
| 246954 | ); |
| 246955 | if( pNew ){ |
| 246956 | pNew->nSeg = nSlot; |
| 246957 | pNew->aFirst = (Fts5CResult*)&pNew->aSeg[nSlot]; |
| @@ -249313,11 +249501,11 @@ | |
| 249313 | static Fts5Structure *fts5IndexOptimizeStruct( |
| 249314 | Fts5Index *p, |
| 249315 | Fts5Structure *pStruct |
| 249316 | ){ |
| 249317 | Fts5Structure *pNew = 0; |
| 249318 | sqlite3_int64 nByte = sizeof(Fts5Structure); |
| 249319 | int nSeg = pStruct->nSegment; |
| 249320 | int i; |
| 249321 | |
| 249322 | /* Figure out if this structure requires optimization. A structure does |
| 249323 | ** not require optimization if either: |
| @@ -249343,10 +249531,11 @@ | |
| 249343 | } |
| 249344 | assert( pStruct->aLevel[i].nMerge<=nThis ); |
| 249345 | } |
| 249346 | |
| 249347 | nByte += (((i64)pStruct->nLevel)+1) * sizeof(Fts5StructureLevel); |
| 249348 | pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte); |
| 249349 | |
| 249350 | if( pNew ){ |
| 249351 | Fts5StructureLevel *pLvl; |
| 249352 | nByte = nSeg * sizeof(Fts5StructureSegment); |
| @@ -249919,12 +250108,16 @@ | |
| 249919 | /* The following are used for other full-token tokendata queries only. */ |
| 249920 | int nIter; |
| 249921 | int nIterAlloc; |
| 249922 | Fts5PoslistReader *aPoslistReader; |
| 249923 | int *aPoslistToIter; |
| 249924 | Fts5Iter *apIter[1]; |
| 249925 | }; |
| 249926 | |
| 249927 | /* |
| 249928 | ** The two input arrays - a1[] and a2[] - are in sorted order. This function |
| 249929 | ** merges the two arrays together and writes the result to output array |
| 249930 | ** aOut[]. aOut[] is guaranteed to be large enough to hold the result. |
| @@ -249993,11 +250186,11 @@ | |
| 249993 | } |
| 249994 | |
| 249995 | /* |
| 249996 | ** Sort the contents of the pT->aMap[] array. |
| 249997 | ** |
| 249998 | ** The sorting algorithm requries a malloc(). If this fails, an error code |
| 249999 | ** is left in Fts5Index.rc before returning. |
| 250000 | */ |
| 250001 | static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){ |
| 250002 | Fts5TokenDataMap *aTmp = 0; |
| 250003 | int nByte = pT->nMap * sizeof(Fts5TokenDataMap); |
| @@ -250184,11 +250377,11 @@ | |
| 250184 | if( iIdx==0 |
| 250185 | && p->pConfig->eDetail==FTS5_DETAIL_FULL |
| 250186 | && p->pConfig->bPrefixInsttoken |
| 250187 | ){ |
| 250188 | s.pTokendata = &s2; |
| 250189 | s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, sizeof(*s2.pT)); |
| 250190 | } |
| 250191 | |
| 250192 | if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){ |
| 250193 | s.xMerge = fts5MergeRowidLists; |
| 250194 | s.xAppend = fts5AppendRowid; |
| @@ -250312,19 +250505,21 @@ | |
| 250312 | ** The %_data table is completely empty when this function is called. This |
| 250313 | ** function populates it with the initial structure objects for each index, |
| 250314 | ** and the initial version of the "averages" record (a zero-byte blob). |
| 250315 | */ |
| 250316 | static int sqlite3Fts5IndexReinit(Fts5Index *p){ |
| 250317 | Fts5Structure s; |
| 250318 | fts5StructureInvalidate(p); |
| 250319 | fts5IndexDiscardData(p); |
| 250320 | memset(&s, 0, sizeof(Fts5Structure)); |
| 250321 | if( p->pConfig->bContentlessDelete ){ |
| 250322 | s.nOriginCntr = 1; |
| 250323 | } |
| 250324 | fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0); |
| 250325 | fts5StructureWrite(p, &s); |
| 250326 | return fts5IndexReturn(p); |
| 250327 | } |
| 250328 | |
| 250329 | /* |
| 250330 | ** Open a new Fts5Index handle. If the bCreate argument is true, create |
| @@ -250528,11 +250723,11 @@ | |
| 250528 | Fts5TokenDataIter *pRet = pIn; |
| 250529 | |
| 250530 | if( p->rc==SQLITE_OK ){ |
| 250531 | if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){ |
| 250532 | int nAlloc = pIn ? pIn->nIterAlloc*2 : 16; |
| 250533 | int nByte = nAlloc * sizeof(Fts5Iter*) + sizeof(Fts5TokenDataIter); |
| 250534 | Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte); |
| 250535 | |
| 250536 | if( pNew==0 ){ |
| 250537 | p->rc = SQLITE_NOMEM; |
| 250538 | }else{ |
| @@ -251044,11 +251239,12 @@ | |
| 251044 | |
| 251045 | memset(&ctx, 0, sizeof(ctx)); |
| 251046 | |
| 251047 | fts5BufferGrow(&p->rc, &token, nToken+1); |
| 251048 | assert( token.p!=0 || p->rc!=SQLITE_OK ); |
| 251049 | ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*ctx.pT)); |
| 251050 | |
| 251051 | if( p->rc==SQLITE_OK ){ |
| 251052 | |
| 251053 | /* Fill in the token prefix to search for */ |
| 251054 | token.p[0] = FTS5_MAIN_PREFIX; |
| @@ -251175,11 +251371,12 @@ | |
| 251175 | assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL ); |
| 251176 | assert( pIter->pTokenDataIter || pIter->nSeg>0 ); |
| 251177 | if( pIter->nSeg>0 ){ |
| 251178 | /* This is a prefix term iterator. */ |
| 251179 | if( pT==0 ){ |
| 251180 | pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*pT)); |
| 251181 | pIter->pTokenDataIter = pT; |
| 251182 | } |
| 251183 | if( pT ){ |
| 251184 | fts5TokendataIterAppendMap(p, pT, pT->terms.n, nToken, iRowid, iPos); |
| 251185 | fts5BufferAppendBlob(&p->rc, &pT->terms, nToken, (const u8*)pToken); |
| @@ -252209,11 +252406,11 @@ | |
| 252209 | } |
| 252210 | #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ |
| 252211 | |
| 252212 | #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) |
| 252213 | static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){ |
| 252214 | int iSegid, iHeight, iPgno, bDlidx, bTomb; /* Rowid compenents */ |
| 252215 | fts5DecodeRowid(iKey, &bTomb, &iSegid, &bDlidx, &iHeight, &iPgno); |
| 252216 | |
| 252217 | if( iSegid==0 ){ |
| 252218 | if( iKey==FTS5_AVERAGES_ROWID ){ |
| 252219 | sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{averages} "); |
| @@ -253170,13 +253367,15 @@ | |
| 253170 | struct Fts5Sorter { |
| 253171 | sqlite3_stmt *pStmt; |
| 253172 | i64 iRowid; /* Current rowid */ |
| 253173 | const u8 *aPoslist; /* Position lists for current row */ |
| 253174 | int nIdx; /* Number of entries in aIdx[] */ |
| 253175 | int aIdx[1]; /* Offsets into aPoslist for current row */ |
| 253176 | }; |
| 253177 | |
| 253178 | |
| 253179 | /* |
| 253180 | ** Virtual-table cursor object. |
| 253181 | ** |
| 253182 | ** iSpecial: |
| @@ -254050,11 +254249,11 @@ | |
| 254050 | int rc; |
| 254051 | const char *zRank = pCsr->zRank; |
| 254052 | const char *zRankArgs = pCsr->zRankArgs; |
| 254053 | |
| 254054 | nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); |
| 254055 | nByte = sizeof(Fts5Sorter) + sizeof(int) * (nPhrase-1); |
| 254056 | pSorter = (Fts5Sorter*)sqlite3_malloc64(nByte); |
| 254057 | if( pSorter==0 ) return SQLITE_NOMEM; |
| 254058 | memset(pSorter, 0, (size_t)nByte); |
| 254059 | pSorter->nIdx = nPhrase; |
| 254060 | |
| @@ -256576,11 +256775,11 @@ | |
| 256576 | int nArg, /* Number of args */ |
| 256577 | sqlite3_value **apUnused /* Function arguments */ |
| 256578 | ){ |
| 256579 | assert( nArg==0 ); |
| 256580 | UNUSED_PARAM2(nArg, apUnused); |
| 256581 | sqlite3_result_text(pCtx, "fts5: 2025-02-25 16:39:51 6f0b6d95db17e69ac7e46a39f52770291ac4cfe43eea09add224946a6e11f04e", -1, SQLITE_TRANSIENT); |
| 256582 | } |
| 256583 | |
| 256584 | /* |
| 256585 | ** Implementation of fts5_locale(LOCALE, TEXT) function. |
| 256586 | ** |
| 256587 |
| --- extsrc/sqlite3.c | |
| +++ extsrc/sqlite3.c | |
| @@ -16,11 +16,11 @@ | |
| 16 | ** if you want a wrapper to interface SQLite with your choice of programming |
| 17 | ** language. The code for the "sqlite3" command-line shell is also in a |
| 18 | ** separate file. This file contains only code for the core SQLite library. |
| 19 | ** |
| 20 | ** The content in this amalgamation comes from Fossil check-in |
| 21 | ** 18bda13e197e4b4ec7464b3e70012f71edc0 with changes in files: |
| 22 | ** |
| 23 | ** |
| 24 | */ |
| 25 | #ifndef SQLITE_AMALGAMATION |
| 26 | #define SQLITE_CORE 1 |
| @@ -465,11 +465,11 @@ | |
| 465 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 466 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 467 | */ |
| 468 | #define SQLITE_VERSION "3.50.0" |
| 469 | #define SQLITE_VERSION_NUMBER 3050000 |
| 470 | #define SQLITE_SOURCE_ID "2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185" |
| 471 | |
| 472 | /* |
| 473 | ** CAPI3REF: Run-Time Library Version Numbers |
| 474 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 475 | ** |
| @@ -5492,11 +5492,11 @@ | |
| 5492 | ** For all versions of SQLite up to and including 3.6.23.1, a call to |
| 5493 | ** [sqlite3_reset()] was required after sqlite3_step() returned anything |
| 5494 | ** other than [SQLITE_ROW] before any subsequent invocation of |
| 5495 | ** sqlite3_step(). Failure to reset the prepared statement using |
| 5496 | ** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from |
| 5497 | ** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]), |
| 5498 | ** sqlite3_step() began |
| 5499 | ** calling [sqlite3_reset()] automatically in this circumstance rather |
| 5500 | ** than returning [SQLITE_MISUSE]. This is not considered a compatibility |
| 5501 | ** break because any application that ever receives an SQLITE_MISUSE error |
| 5502 | ** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option |
| @@ -7388,10 +7388,12 @@ | |
| 7388 | ** ^Any callback set by a previous call to this function |
| 7389 | ** for the same database connection is overridden. |
| 7390 | ** |
| 7391 | ** ^The second argument is a pointer to the function to invoke when a |
| 7392 | ** row is updated, inserted or deleted in a rowid table. |
| 7393 | ** ^The update hook is disabled by invoking sqlite3_update_hook() |
| 7394 | ** with a NULL pointer as the second parameter. |
| 7395 | ** ^The first argument to the callback is a copy of the third argument |
| 7396 | ** to sqlite3_update_hook(). |
| 7397 | ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], |
| 7398 | ** or [SQLITE_UPDATE], depending on the operation that caused the callback |
| 7399 | ** to be invoked. |
| @@ -15170,11 +15172,21 @@ | |
| 15172 | /* |
| 15173 | ** GCC does not define the offsetof() macro so we'll have to do it |
| 15174 | ** ourselves. |
| 15175 | */ |
| 15176 | #ifndef offsetof |
| 15177 | #define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) |
| 15178 | #endif |
| 15179 | |
| 15180 | /* |
| 15181 | ** Work around C99 "flex-array" syntax for pre-C99 compilers, so as |
| 15182 | ** to avoid complaints from -fsanitize=strict-bounds. |
| 15183 | */ |
| 15184 | #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) |
| 15185 | # define FLEXARRAY |
| 15186 | #else |
| 15187 | # define FLEXARRAY 1 |
| 15188 | #endif |
| 15189 | |
| 15190 | /* |
| 15191 | ** Macros to compute minimum and maximum of two numbers. |
| 15192 | */ |
| @@ -17405,12 +17417,12 @@ | |
| 17417 | SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*); |
| 17418 | #ifdef SQLITE_ENABLE_BYTECODE_VTAB |
| 17419 | SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*); |
| 17420 | #endif |
| 17421 | |
| 17422 | /* Use SQLITE_ENABLE_EXPLAIN_COMMENTS to enable generation of extra |
| 17423 | ** comments on each VDBE opcode. |
| 17424 | ** |
| 17425 | ** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op |
| 17426 | ** comments in VDBE programs that show key decision points in the code |
| 17427 | ** generator. |
| 17428 | */ |
| @@ -18945,12 +18957,16 @@ | |
| 18957 | u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */ |
| 18958 | Trigger *apTrigger[2];/* Triggers for aAction[] actions */ |
| 18959 | struct sColMap { /* Mapping of columns in pFrom to columns in zTo */ |
| 18960 | int iFrom; /* Index of column in pFrom */ |
| 18961 | char *zCol; /* Name of column in zTo. If NULL use PRIMARY KEY */ |
| 18962 | } aCol[FLEXARRAY]; /* One entry for each of nCol columns */ |
| 18963 | }; |
| 18964 | |
| 18965 | /* The size (in bytes) of an FKey object holding N columns. The answer |
| 18966 | ** does NOT include space to hold the zTo name. */ |
| 18967 | #define SZ_FKEY(N) (offsetof(FKey,aCol)+(N)*sizeof(struct sColMap)) |
| 18968 | |
| 18969 | /* |
| 18970 | ** SQLite supports many different ways to resolve a constraint |
| 18971 | ** error. ROLLBACK processing means that a constraint violation |
| 18972 | ** causes the operation in process to fail and for the current transaction |
| @@ -19009,13 +19025,16 @@ | |
| 19025 | u8 enc; /* Text encoding - one of the SQLITE_UTF* values */ |
| 19026 | u16 nKeyField; /* Number of key columns in the index */ |
| 19027 | u16 nAllField; /* Total columns, including key plus others */ |
| 19028 | sqlite3 *db; /* The database connection */ |
| 19029 | u8 *aSortFlags; /* Sort order for each column. */ |
| 19030 | CollSeq *aColl[FLEXARRAY]; /* Collating sequence for each term of the key */ |
| 19031 | }; |
| 19032 | |
| 19033 | /* The size (in bytes) of a KeyInfo object with up to N fields */ |
| 19034 | #define SZ_KEYINFO(N) (offsetof(KeyInfo,aColl) + (N)*sizeof(CollSeq*)) |
| 19035 | |
| 19036 | /* |
| 19037 | ** Allowed bit values for entries in the KeyInfo.aSortFlags[] array. |
| 19038 | */ |
| 19039 | #define KEYINFO_ORDER_DESC 0x01 /* DESC sort order */ |
| 19040 | #define KEYINFO_ORDER_BIGNULL 0x02 /* NULL is larger than any other value */ |
| @@ -19584,12 +19603,17 @@ | |
| 19603 | u16 iAlias; /* Index into Parse.aAlias[] for zName */ |
| 19604 | } x; |
| 19605 | int iConstExprReg; /* Register in which Expr value is cached. Used only |
| 19606 | ** by Parse.pConstExpr */ |
| 19607 | } u; |
| 19608 | } a[FLEXARRAY]; /* One slot for each expression in the list */ |
| 19609 | }; |
| 19610 | |
| 19611 | /* The size (in bytes) of an ExprList object that is big enough to hold |
| 19612 | ** as many as N expressions. */ |
| 19613 | #define SZ_EXPRLIST(N) \ |
| 19614 | (offsetof(ExprList,a) + (N)*sizeof(struct ExprList_item)) |
| 19615 | |
| 19616 | /* |
| 19617 | ** Allowed values for Expr.a.eEName |
| 19618 | */ |
| 19619 | #define ENAME_NAME 0 /* The AS clause of a result set */ |
| @@ -19614,13 +19638,16 @@ | |
| 19638 | */ |
| 19639 | struct IdList { |
| 19640 | int nId; /* Number of identifiers on the list */ |
| 19641 | struct IdList_item { |
| 19642 | char *zName; /* Name of the identifier */ |
| 19643 | } a[FLEXARRAY]; |
| 19644 | }; |
| 19645 | |
| 19646 | /* The size (in bytes) of an IdList object that can hold up to N IDs. */ |
| 19647 | #define SZ_IDLIST(N) (offsetof(IdList,a)+(N)*sizeof(struct IdList_item)) |
| 19648 | |
| 19649 | /* |
| 19650 | ** Allowed values for IdList.eType, which determines which value of the a.u4 |
| 19651 | ** is valid. |
| 19652 | */ |
| 19653 | #define EU4_NONE 0 /* Does not use IdList.a.u4 */ |
| @@ -19736,14 +19763,22 @@ | |
| 19763 | ** is used to hold the FROM clause of a SELECT statement. SrcList also |
| 19764 | ** represents the target tables for DELETE, INSERT, and UPDATE statements. |
| 19765 | ** |
| 19766 | */ |
| 19767 | struct SrcList { |
| 19768 | int nSrc; /* Number of tables or subqueries in the FROM clause */ |
| 19769 | u32 nAlloc; /* Number of entries allocated in a[] below */ |
| 19770 | SrcItem a[FLEXARRAY]; /* One entry for each identifier on the list */ |
| 19771 | }; |
| 19772 | |
| 19773 | /* Size (in bytes) of a SrcList object that can hold as many as N |
| 19774 | ** SrcItem objects. */ |
| 19775 | #define SZ_SRCLIST(N) (offsetof(SrcList,a)+(N)*sizeof(SrcItem)) |
| 19776 | |
| 19777 | /* Size (in bytes( of a SrcList object that holds 1 SrcItem. This is a |
| 19778 | ** special case of SZ_SRCITEM(1) that comes up often. */ |
| 19779 | #define SZ_SRCLIST_1 (offsetof(SrcList,a)+sizeof(SrcItem)) |
| 19780 | |
| 19781 | /* |
| 19782 | ** Permitted values of the SrcList.a.jointype field |
| 19783 | */ |
| 19784 | #define JT_INNER 0x01 /* Any kind of inner or cross join */ |
| @@ -20804,12 +20839,16 @@ | |
| 20839 | */ |
| 20840 | struct With { |
| 20841 | int nCte; /* Number of CTEs in the WITH clause */ |
| 20842 | int bView; /* Belongs to the outermost Select of a view */ |
| 20843 | With *pOuter; /* Containing WITH clause, or NULL */ |
| 20844 | Cte a[FLEXARRAY]; /* For each CTE in the WITH clause.... */ |
| 20845 | }; |
| 20846 | |
| 20847 | /* The size (in bytes) of a With object that can hold as many |
| 20848 | ** as N different CTEs. */ |
| 20849 | #define SZ_WITH(N) (offsetof(With,a) + (N)*sizeof(Cte)) |
| 20850 | |
| 20851 | /* |
| 20852 | ** The Cte object is not guaranteed to persist for the entire duration |
| 20853 | ** of code generation. (The query flattener or other parser tree |
| 20854 | ** edits might delete it.) The following object records information |
| @@ -20835,12 +20874,16 @@ | |
| 20874 | */ |
| 20875 | struct DbClientData { |
| 20876 | DbClientData *pNext; /* Next in a linked list */ |
| 20877 | void *pData; /* The data */ |
| 20878 | void (*xDestructor)(void*); /* Destructor. Might be NULL */ |
| 20879 | char zName[FLEXARRAY]; /* Name of this client data. MUST BE LAST */ |
| 20880 | }; |
| 20881 | |
| 20882 | /* The size (in bytes) of a DbClientData object that can has a name |
| 20883 | ** that is N bytes long, including the zero-terminator. */ |
| 20884 | #define SZ_DBCLIENTDATA(N) (offsetof(DbClientData,zName)+(N)) |
| 20885 | |
| 20886 | #ifdef SQLITE_DEBUG |
| 20887 | /* |
| 20888 | ** An instance of the TreeView object is used for printing the content of |
| 20889 | ** data structures on sqlite3DebugPrintf() using a tree-like view. |
| @@ -22673,10 +22716,13 @@ | |
| 22716 | "EXTRA_IFNULLROW", |
| 22717 | #endif |
| 22718 | #ifdef SQLITE_EXTRA_INIT |
| 22719 | "EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT), |
| 22720 | #endif |
| 22721 | #ifdef SQLITE_EXTRA_INIT_MUTEXED |
| 22722 | "EXTRA_INIT_MUTEXED=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT_MUTEXED), |
| 22723 | #endif |
| 22724 | #ifdef SQLITE_EXTRA_SHUTDOWN |
| 22725 | "EXTRA_SHUTDOWN=" CTIMEOPT_VAL(SQLITE_EXTRA_SHUTDOWN), |
| 22726 | #endif |
| 22727 | #ifdef SQLITE_FTS3_MAX_EXPR_DEPTH |
| 22728 | "FTS3_MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_FTS3_MAX_EXPR_DEPTH), |
| @@ -23657,16 +23703,23 @@ | |
| 23703 | #ifdef SQLITE_ENABLE_COLUMN_USED_MASK |
| 23704 | u64 maskUsed; /* Mask of columns used by this cursor */ |
| 23705 | #endif |
| 23706 | VdbeTxtBlbCache *pCache; /* Cache of large TEXT or BLOB values */ |
| 23707 | |
| 23708 | /* Space is allocated for aType to hold at least 2*nField+1 entries: |
| 23709 | ** nField slots for aType[] and nField+1 array slots for aOffset[] */ |
| 23710 | u32 aType[FLEXARRAY]; /* Type values record decode. MUST BE LAST */ |
| 23711 | }; |
| 23712 | |
| 23713 | /* |
| 23714 | ** The size (in bytes) of a VdbeCursor object that has an nField value of N |
| 23715 | ** or less. The value of SZ_VDBECURSOR(n) is guaranteed to be a multiple |
| 23716 | ** of 8. |
| 23717 | */ |
| 23718 | #define SZ_VDBECURSOR(N) \ |
| 23719 | (ROUND8(offsetof(VdbeCursor,aType)) + ((N)+1)*sizeof(u64)) |
| 23720 | |
| 23721 | /* Return true if P is a null-only cursor |
| 23722 | */ |
| 23723 | #define IsNullCursor(P) \ |
| 23724 | ((P)->eCurType==CURTYPE_PSEUDO && (P)->nullRow && (P)->seekResult==0) |
| 23725 | |
| @@ -23919,13 +23972,20 @@ | |
| 23972 | int iOp; /* Instruction number of OP_Function */ |
| 23973 | int isError; /* Error code returned by the function. */ |
| 23974 | u8 enc; /* Encoding to use for results */ |
| 23975 | u8 skipFlag; /* Skip accumulator loading if true */ |
| 23976 | u16 argc; /* Number of arguments */ |
| 23977 | sqlite3_value *argv[FLEXARRAY]; /* Argument set */ |
| 23978 | }; |
| 23979 | |
| 23980 | /* |
| 23981 | ** The size (in bytes) of an sqlite3_context object that holds N |
| 23982 | ** argv[] arguments. |
| 23983 | */ |
| 23984 | #define SZ_CONTEXT(N) \ |
| 23985 | (offsetof(sqlite3_context,argv)+(N)*sizeof(sqlite3_value*)) |
| 23986 | |
| 23987 | |
| 23988 | /* The ScanStatus object holds a single value for the |
| 23989 | ** sqlite3_stmt_scanstatus() interface. |
| 23990 | ** |
| 23991 | ** aAddrRange[]: |
| @@ -24055,11 +24115,11 @@ | |
| 24115 | struct PreUpdate { |
| 24116 | Vdbe *v; |
| 24117 | VdbeCursor *pCsr; /* Cursor to read old values from */ |
| 24118 | int op; /* One of SQLITE_INSERT, UPDATE, DELETE */ |
| 24119 | u8 *aRecord; /* old.* database record */ |
| 24120 | KeyInfo *pKeyinfo; /* Key information */ |
| 24121 | UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */ |
| 24122 | UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */ |
| 24123 | int iNewReg; /* Register for new.* values */ |
| 24124 | int iBlobWrite; /* Value returned by preupdate_blobwrite() */ |
| 24125 | i64 iKey1; /* First key value passed to hook */ |
| @@ -24067,10 +24127,11 @@ | |
| 24127 | Mem oldipk; /* Memory cell holding "old" IPK value */ |
| 24128 | Mem *aNew; /* Array of new.* values */ |
| 24129 | Table *pTab; /* Schema object being updated */ |
| 24130 | Index *pPk; /* PK index if pTab is WITHOUT ROWID */ |
| 24131 | sqlite3_value **apDflt; /* Array of default values, if required */ |
| 24132 | u8 keyinfoSpace[SZ_KEYINFO(0)]; /* Space to hold pKeyinfo[0] content */ |
| 24133 | }; |
| 24134 | |
| 24135 | /* |
| 24136 | ** An instance of this object is used to pass an vector of values into |
| 24137 | ** OP_VFilter, the xFilter method of a virtual table. The vector is the |
| @@ -26000,11 +26061,11 @@ | |
| 26061 | ** Return the number of days after the most recent Sunday. |
| 26062 | ** |
| 26063 | ** In other words, return the day of the week according |
| 26064 | ** to this code: |
| 26065 | ** |
| 26066 | ** 0=Sunday, 1=Monday, 2=Tuesday, ..., 6=Saturday |
| 26067 | */ |
| 26068 | static int daysAfterSunday(DateTime *pDate){ |
| 26069 | assert( pDate->validJD ); |
| 26070 | return (int)((pDate->iJD+129600000)/86400000) % 7; |
| 26071 | } |
| @@ -32378,11 +32439,11 @@ | |
| 32439 | u32 nBack = 0; |
| 32440 | u32 nCtrl = 0; |
| 32441 | for(k=0; k<i; k++){ |
| 32442 | if( escarg[k]=='\\' ){ |
| 32443 | nBack++; |
| 32444 | }else if( ((u8*)escarg)[k]<=0x1f ){ |
| 32445 | nCtrl++; |
| 32446 | } |
| 32447 | } |
| 32448 | if( nCtrl || xtype==etESCAPE_q ){ |
| 32449 | n += nBack + 5*nCtrl; |
| @@ -32416,11 +32477,11 @@ | |
| 32477 | bufpt[j++] = ch = escarg[i]; |
| 32478 | if( ch==q ){ |
| 32479 | bufpt[j++] = ch; |
| 32480 | }else if( ch=='\\' ){ |
| 32481 | bufpt[j++] = '\\'; |
| 32482 | }else if( ((unsigned char)ch)<=0x1f ){ |
| 32483 | bufpt[j-1] = '\\'; |
| 32484 | bufpt[j++] = 'u'; |
| 32485 | bufpt[j++] = '0'; |
| 32486 | bufpt[j++] = '0'; |
| 32487 | bufpt[j++] = ch>=0x10 ? '1' : '0'; |
| @@ -37067,11 +37128,11 @@ | |
| 37128 | return 0; |
| 37129 | #endif |
| 37130 | } |
| 37131 | |
| 37132 | /* |
| 37133 | ** Compute the absolute value of a 32-bit signed integer, if possible. Or |
| 37134 | ** if the integer has a value of -2147483648, return +2147483647 |
| 37135 | */ |
| 37136 | SQLITE_PRIVATE int sqlite3AbsInt32(int x){ |
| 37137 | if( x>=0 ) return x; |
| 37138 | if( x==(int)0x80000000 ) return 0x7fffffff; |
| @@ -45614,11 +45675,11 @@ | |
| 45675 | sp.tv_sec = microseconds / 1000000; |
| 45676 | sp.tv_nsec = (microseconds % 1000000) * 1000; |
| 45677 | |
| 45678 | /* Almost all modern unix systems support nanosleep(). But if you are |
| 45679 | ** compiling for one of the rare exceptions, you can use |
| 45680 | ** -DHAVE_NANOSLEEP=0 (perhaps in conjunction with -DHAVE_USLEEP if |
| 45681 | ** usleep() is available) in order to bypass the use of nanosleep() */ |
| 45682 | nanosleep(&sp, NULL); |
| 45683 | |
| 45684 | UNUSED_PARAMETER(NotUsed); |
| 45685 | return microseconds; |
| @@ -56047,14 +56108,10 @@ | |
| 56108 | void *pStart, *pEnd; /* Bounds of global page cache memory */ |
| 56109 | /* Above requires no mutex. Use mutex below for variable that follow. */ |
| 56110 | sqlite3_mutex *mutex; /* Mutex for accessing the following: */ |
| 56111 | PgFreeslot *pFree; /* Free page blocks */ |
| 56112 | int nFreeSlot; /* Number of unused pcache slots */ |
| 56113 | int bUnderPressure; /* True if low on PAGECACHE memory */ |
| 56114 | } pcache1_g; |
| 56115 | |
| 56116 | /* |
| 56117 | ** All code in this file should access the global structure above via the |
| @@ -56098,11 +56155,11 @@ | |
| 56155 | pcache1.szSlot = sz; |
| 56156 | pcache1.nSlot = pcache1.nFreeSlot = n; |
| 56157 | pcache1.nReserve = n>90 ? 10 : (n/10 + 1); |
| 56158 | pcache1.pStart = pBuf; |
| 56159 | pcache1.pFree = 0; |
| 56160 | AtomicStore(&pcache1.bUnderPressure,0); |
| 56161 | while( n-- ){ |
| 56162 | p = (PgFreeslot*)pBuf; |
| 56163 | p->pNext = pcache1.pFree; |
| 56164 | pcache1.pFree = p; |
| 56165 | pBuf = (void*)&((char*)pBuf)[sz]; |
| @@ -56166,11 +56223,11 @@ | |
| 56223 | sqlite3_mutex_enter(pcache1.mutex); |
| 56224 | p = (PgHdr1 *)pcache1.pFree; |
| 56225 | if( p ){ |
| 56226 | pcache1.pFree = pcache1.pFree->pNext; |
| 56227 | pcache1.nFreeSlot--; |
| 56228 | AtomicStore(&pcache1.bUnderPressure,pcache1.nFreeSlot<pcache1.nReserve); |
| 56229 | assert( pcache1.nFreeSlot>=0 ); |
| 56230 | sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte); |
| 56231 | sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1); |
| 56232 | } |
| 56233 | sqlite3_mutex_leave(pcache1.mutex); |
| @@ -56205,11 +56262,11 @@ | |
| 56262 | sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1); |
| 56263 | pSlot = (PgFreeslot*)p; |
| 56264 | pSlot->pNext = pcache1.pFree; |
| 56265 | pcache1.pFree = pSlot; |
| 56266 | pcache1.nFreeSlot++; |
| 56267 | AtomicStore(&pcache1.bUnderPressure,pcache1.nFreeSlot<pcache1.nReserve); |
| 56268 | assert( pcache1.nFreeSlot<=pcache1.nSlot ); |
| 56269 | sqlite3_mutex_leave(pcache1.mutex); |
| 56270 | }else{ |
| 56271 | assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) ); |
| 56272 | sqlite3MemdebugSetType(p, MEMTYPE_HEAP); |
| @@ -56336,11 +56393,11 @@ | |
| 56393 | ** allocating a new page cache entry in order to avoid stressing |
| 56394 | ** the heap even further. |
| 56395 | */ |
| 56396 | static int pcache1UnderMemoryPressure(PCache1 *pCache){ |
| 56397 | if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){ |
| 56398 | return AtomicLoad(&pcache1.bUnderPressure); |
| 56399 | }else{ |
| 56400 | return sqlite3HeapNearlyFull(); |
| 56401 | } |
| 56402 | } |
| 56403 | |
| @@ -66177,12 +66234,16 @@ | |
| 66234 | int iNext; /* Next slot in aIndex[] not yet returned */ |
| 66235 | ht_slot *aIndex; /* i0, i1, i2... such that aPgno[iN] ascend */ |
| 66236 | u32 *aPgno; /* Array of page numbers. */ |
| 66237 | int nEntry; /* Nr. of entries in aPgno[] and aIndex[] */ |
| 66238 | int iZero; /* Frame number associated with aPgno[0] */ |
| 66239 | } aSegment[FLEXARRAY]; /* One for every 32KB page in the wal-index */ |
| 66240 | }; |
| 66241 | |
| 66242 | /* Size (in bytes) of a WalIterator object suitable for N or fewer segments */ |
| 66243 | #define SZ_WALITERATOR(N) \ |
| 66244 | (offsetof(WalIterator,aSegment)*(N)*sizeof(struct WalSegment)) |
| 66245 | |
| 66246 | /* |
| 66247 | ** Define the parameters of the hash tables in the wal-index file. There |
| 66248 | ** is a hash-table following every HASHTABLE_NPAGE page numbers in the |
| 66249 | ** wal-index. |
| @@ -67540,12 +67601,11 @@ | |
| 67601 | assert( pWal->ckptLock && pWal->hdr.mxFrame>0 ); |
| 67602 | iLast = pWal->hdr.mxFrame; |
| 67603 | |
| 67604 | /* Allocate space for the WalIterator object. */ |
| 67605 | nSegment = walFramePage(iLast) + 1; |
| 67606 | nByte = SZ_WALITERATOR(nSegment) |
| 67607 | + iLast*sizeof(ht_slot); |
| 67608 | p = (WalIterator *)sqlite3_malloc64(nByte |
| 67609 | + sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast) |
| 67610 | ); |
| 67611 | if( !p ){ |
| @@ -86029,16 +86089,14 @@ | |
| 86089 | int nArg, /* Number of argument */ |
| 86090 | const FuncDef *pFunc, /* The function to be invoked */ |
| 86091 | int eCallCtx /* Calling context */ |
| 86092 | ){ |
| 86093 | Vdbe *v = pParse->pVdbe; |
| 86094 | int addr; |
| 86095 | sqlite3_context *pCtx; |
| 86096 | assert( v ); |
| 86097 | pCtx = sqlite3DbMallocRawNN(pParse->db, SZ_CONTEXT(nArg)); |
| 86098 | if( pCtx==0 ){ |
| 86099 | assert( pParse->db->mallocFailed ); |
| 86100 | freeEphemeralFunction(pParse->db, (FuncDef*)pFunc); |
| 86101 | return 0; |
| 86102 | } |
| @@ -91110,25 +91168,26 @@ | |
| 91168 | |
| 91169 | preupdate.v = v; |
| 91170 | preupdate.pCsr = pCsr; |
| 91171 | preupdate.op = op; |
| 91172 | preupdate.iNewReg = iReg; |
| 91173 | preupdate.pKeyinfo = (KeyInfo*)&preupdate.keyinfoSpace; |
| 91174 | preupdate.pKeyinfo->db = db; |
| 91175 | preupdate.pKeyinfo->enc = ENC(db); |
| 91176 | preupdate.pKeyinfo->nKeyField = pTab->nCol; |
| 91177 | preupdate.pKeyinfo->aSortFlags = (u8*)&fakeSortOrder; |
| 91178 | preupdate.iKey1 = iKey1; |
| 91179 | preupdate.iKey2 = iKey2; |
| 91180 | preupdate.pTab = pTab; |
| 91181 | preupdate.iBlobWrite = iBlobWrite; |
| 91182 | |
| 91183 | db->pPreUpdate = &preupdate; |
| 91184 | db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2); |
| 91185 | db->pPreUpdate = 0; |
| 91186 | sqlite3DbFree(db, preupdate.aRecord); |
| 91187 | vdbeFreeUnpacked(db, preupdate.pKeyinfo->nKeyField+1,preupdate.pUnpacked); |
| 91188 | vdbeFreeUnpacked(db, preupdate.pKeyinfo->nKeyField+1,preupdate.pNewUnpacked); |
| 91189 | sqlite3VdbeMemRelease(&preupdate.oldipk); |
| 91190 | if( preupdate.aNew ){ |
| 91191 | int i; |
| 91192 | for(i=0; i<pCsr->nField; i++){ |
| 91193 | sqlite3VdbeMemRelease(&preupdate.aNew[i]); |
| @@ -93363,11 +93422,11 @@ | |
| 93422 | nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor); |
| 93423 | aRec = sqlite3DbMallocRaw(db, nRec); |
| 93424 | if( !aRec ) goto preupdate_old_out; |
| 93425 | rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec); |
| 93426 | if( rc==SQLITE_OK ){ |
| 93427 | p->pUnpacked = vdbeUnpackRecord(p->pKeyinfo, nRec, aRec); |
| 93428 | if( !p->pUnpacked ) rc = SQLITE_NOMEM; |
| 93429 | } |
| 93430 | if( rc!=SQLITE_OK ){ |
| 93431 | sqlite3DbFree(db, aRec); |
| 93432 | goto preupdate_old_out; |
| @@ -93428,11 +93487,11 @@ | |
| 93487 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 93488 | p = db!=0 ? db->pPreUpdate : 0; |
| 93489 | #else |
| 93490 | p = db->pPreUpdate; |
| 93491 | #endif |
| 93492 | return (p ? p->pKeyinfo->nKeyField : 0); |
| 93493 | } |
| 93494 | #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ |
| 93495 | |
| 93496 | #ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
| 93497 | /* |
| @@ -93511,11 +93570,11 @@ | |
| 93570 | UnpackedRecord *pUnpack = p->pNewUnpacked; |
| 93571 | if( !pUnpack ){ |
| 93572 | Mem *pData = &p->v->aMem[p->iNewReg]; |
| 93573 | rc = ExpandBlob(pData); |
| 93574 | if( rc!=SQLITE_OK ) goto preupdate_new_out; |
| 93575 | pUnpack = vdbeUnpackRecord(p->pKeyinfo, pData->n, pData->z); |
| 93576 | if( !pUnpack ){ |
| 93577 | rc = SQLITE_NOMEM; |
| 93578 | goto preupdate_new_out; |
| 93579 | } |
| 93580 | p->pNewUnpacked = pUnpack; |
| @@ -94305,13 +94364,13 @@ | |
| 94364 | */ |
| 94365 | Mem *pMem = iCur>0 ? &p->aMem[p->nMem-iCur] : p->aMem; |
| 94366 | |
| 94367 | i64 nByte; |
| 94368 | VdbeCursor *pCx = 0; |
| 94369 | nByte = SZ_VDBECURSOR(nField); |
| 94370 | assert( ROUND8(nByte)==nByte ); |
| 94371 | if( eCurType==CURTYPE_BTREE ) nByte += sqlite3BtreeCursorSize(); |
| 94372 | |
| 94373 | assert( iCur>=0 && iCur<p->nCursor ); |
| 94374 | if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/ |
| 94375 | sqlite3VdbeFreeCursorNN(p, p->apCsr[iCur]); |
| 94376 | p->apCsr[iCur] = 0; |
| @@ -94340,12 +94399,12 @@ | |
| 94399 | memset(pCx, 0, offsetof(VdbeCursor,pAltCursor)); |
| 94400 | pCx->eCurType = eCurType; |
| 94401 | pCx->nField = nField; |
| 94402 | pCx->aOffset = &pCx->aType[nField]; |
| 94403 | if( eCurType==CURTYPE_BTREE ){ |
| 94404 | assert( ROUND8(SZ_VDBECURSOR(nField))==SZ_VDBECURSOR(nField) ); |
| 94405 | pCx->uc.pCursor = (BtCursor*)&pMem->z[SZ_VDBECURSOR(nField)]; |
| 94406 | sqlite3BtreeCursorZero(pCx->uc.pCursor); |
| 94407 | } |
| 94408 | return pCx; |
| 94409 | } |
| 94410 | |
| @@ -100083,11 +100142,11 @@ | |
| 100142 | pCrsr = pC->uc.pCursor; |
| 100143 | |
| 100144 | /* The OP_RowData opcodes always follow OP_NotExists or |
| 100145 | ** OP_SeekRowid or OP_Rewind/Op_Next with no intervening instructions |
| 100146 | ** that might invalidate the cursor. |
| 100147 | ** If this were not the case, one of the following assert()s |
| 100148 | ** would fail. Should this ever change (because of changes in the code |
| 100149 | ** generator) then the fix would be to insert a call to |
| 100150 | ** sqlite3VdbeCursorMoveto(). |
| 100151 | */ |
| 100152 | assert( pC->deferredMoveto==0 ); |
| @@ -101732,11 +101791,11 @@ | |
| 101791 | ** cell in which to store the accumulation. Be careful that the memory |
| 101792 | ** cell is 8-byte aligned, even on platforms where a pointer is 32-bits. |
| 101793 | ** |
| 101794 | ** Note: We could avoid this by using a regular memory cell from aMem[] for |
| 101795 | ** the accumulator, instead of allocating one here. */ |
| 101796 | nAlloc = ROUND8P( SZ_CONTEXT(n) ); |
| 101797 | pCtx = sqlite3DbMallocRawNN(db, nAlloc + sizeof(Mem)); |
| 101798 | if( pCtx==0 ) goto no_mem; |
| 101799 | pCtx->pOut = (Mem*)((u8*)pCtx + nAlloc); |
| 101800 | assert( EIGHT_BYTE_ALIGNMENT(pCtx->pOut) ); |
| 101801 | |
| @@ -103390,10 +103449,11 @@ | |
| 103449 | int iCol; /* Index of zColumn in row-record */ |
| 103450 | int rc = SQLITE_OK; |
| 103451 | char *zErr = 0; |
| 103452 | Table *pTab; |
| 103453 | Incrblob *pBlob = 0; |
| 103454 | int iDb; |
| 103455 | Parse sParse; |
| 103456 | |
| 103457 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 103458 | if( ppBlob==0 ){ |
| 103459 | return SQLITE_MISUSE_BKPT; |
| @@ -103435,11 +103495,14 @@ | |
| 103495 | if( pTab && IsView(pTab) ){ |
| 103496 | pTab = 0; |
| 103497 | sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable); |
| 103498 | } |
| 103499 | #endif |
| 103500 | if( pTab==0 |
| 103501 | || ((iDb = sqlite3SchemaToIndex(db, pTab->pSchema))==1 && |
| 103502 | sqlite3OpenTempDatabase(&sParse)) |
| 103503 | ){ |
| 103504 | if( sParse.zErrMsg ){ |
| 103505 | sqlite3DbFree(db, zErr); |
| 103506 | zErr = sParse.zErrMsg; |
| 103507 | sParse.zErrMsg = 0; |
| 103508 | } |
| @@ -103446,11 +103509,11 @@ | |
| 103509 | rc = SQLITE_ERROR; |
| 103510 | sqlite3BtreeLeaveAll(db); |
| 103511 | goto blob_open_out; |
| 103512 | } |
| 103513 | pBlob->pTab = pTab; |
| 103514 | pBlob->zDb = db->aDb[iDb].zDbSName; |
| 103515 | |
| 103516 | /* Now search pTab for the exact column. */ |
| 103517 | iCol = sqlite3ColumnIndex(pTab, zColumn); |
| 103518 | if( iCol<0 ){ |
| 103519 | sqlite3DbFree(db, zErr); |
| @@ -103530,11 +103593,10 @@ | |
| 103593 | {OP_Column, 0, 0, 1}, /* 3 */ |
| 103594 | {OP_ResultRow, 1, 0, 0}, /* 4 */ |
| 103595 | {OP_Halt, 0, 0, 0}, /* 5 */ |
| 103596 | }; |
| 103597 | Vdbe *v = (Vdbe *)pBlob->pStmt; |
| 103598 | VdbeOp *aOp; |
| 103599 | |
| 103600 | sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag, |
| 103601 | pTab->pSchema->schema_cookie, |
| 103602 | pTab->pSchema->iGeneration); |
| @@ -104108,12 +104170,15 @@ | |
| 104170 | u8 bUsePMA; /* True if one or more PMAs created */ |
| 104171 | u8 bUseThreads; /* True to use background threads */ |
| 104172 | u8 iPrev; /* Previous thread used to flush PMA */ |
| 104173 | u8 nTask; /* Size of aTask[] array */ |
| 104174 | u8 typeMask; |
| 104175 | SortSubtask aTask[FLEXARRAY]; /* One or more subtasks */ |
| 104176 | }; |
| 104177 | |
| 104178 | /* Size (in bytes) of a VdbeSorter object that works with N or fewer subtasks */ |
| 104179 | #define SZ_VDBESORTER(N) (offsetof(VdbeSorter,aTask)+(N)*sizeof(SortSubtask)) |
| 104180 | |
| 104181 | #define SORTER_TYPE_INTEGER 0x01 |
| 104182 | #define SORTER_TYPE_TEXT 0x02 |
| 104183 | |
| 104184 | /* |
| @@ -104742,12 +104807,12 @@ | |
| 104807 | assert( pCsr->pKeyInfo ); |
| 104808 | assert( !pCsr->isEphemeral ); |
| 104809 | assert( pCsr->eCurType==CURTYPE_SORTER ); |
| 104810 | assert( sizeof(KeyInfo) + UMXV(pCsr->pKeyInfo->nKeyField)*sizeof(CollSeq*) |
| 104811 | < 0x7fffffff ); |
| 104812 | szKeyInfo = SZ_KEYINFO(pCsr->pKeyInfo->nKeyField+1); |
| 104813 | sz = SZ_VDBESORTER(nWorker+1); |
| 104814 | |
| 104815 | pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo); |
| 104816 | pCsr->uc.pSorter = pSorter; |
| 104817 | if( pSorter==0 ){ |
| 104818 | rc = SQLITE_NOMEM_BKPT; |
| @@ -105207,10 +105272,14 @@ | |
| 105272 | } |
| 105273 | |
| 105274 | p->u.pNext = 0; |
| 105275 | for(i=0; aSlot[i]; i++){ |
| 105276 | p = vdbeSorterMerge(pTask, p, aSlot[i]); |
| 105277 | /* ,--Each aSlot[] holds twice as much as the previous. So we cannot use |
| 105278 | ** | up all 64 aSlots[] with only a 64-bit address space. |
| 105279 | ** v */ |
| 105280 | assert( i<ArraySize(aSlot) ); |
| 105281 | aSlot[i] = 0; |
| 105282 | } |
| 105283 | aSlot[i] = p; |
| 105284 | p = pNext; |
| 105285 | } |
| @@ -109981,32 +110050,34 @@ | |
| 110050 | Table *pTab, /* The table being referenced, or NULL */ |
| 110051 | int type, /* NC_IsCheck, NC_PartIdx, NC_IdxExpr, NC_GenCol, or 0 */ |
| 110052 | Expr *pExpr, /* Expression to resolve. May be NULL. */ |
| 110053 | ExprList *pList /* Expression list to resolve. May be NULL. */ |
| 110054 | ){ |
| 110055 | SrcList *pSrc; /* Fake SrcList for pParse->pNewTable */ |
| 110056 | NameContext sNC; /* Name context for pParse->pNewTable */ |
| 110057 | int rc; |
| 110058 | u8 srcSpace[SZ_SRCLIST_1]; /* Memory space for the fake SrcList */ |
| 110059 | |
| 110060 | assert( type==0 || pTab!=0 ); |
| 110061 | assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr |
| 110062 | || type==NC_GenCol || pTab==0 ); |
| 110063 | memset(&sNC, 0, sizeof(sNC)); |
| 110064 | pSrc = (SrcList*)srcSpace; |
| 110065 | memset(pSrc, 0, SZ_SRCLIST_1); |
| 110066 | if( pTab ){ |
| 110067 | pSrc->nSrc = 1; |
| 110068 | pSrc->a[0].zName = pTab->zName; |
| 110069 | pSrc->a[0].pSTab = pTab; |
| 110070 | pSrc->a[0].iCursor = -1; |
| 110071 | if( pTab->pSchema!=pParse->db->aDb[1].pSchema ){ |
| 110072 | /* Cause EP_FromDDL to be set on TK_FUNCTION nodes of non-TEMP |
| 110073 | ** schema elements */ |
| 110074 | type |= NC_FromDDL; |
| 110075 | } |
| 110076 | } |
| 110077 | sNC.pParse = pParse; |
| 110078 | sNC.pSrcList = pSrc; |
| 110079 | sNC.ncFlags = type | NC_IsDDL; |
| 110080 | if( (rc = sqlite3ResolveExprNames(&sNC, pExpr))!=SQLITE_OK ) return rc; |
| 110081 | if( pList ) rc = sqlite3ResolveExprListNames(&sNC, pList); |
| 110082 | return rc; |
| 110083 | } |
| @@ -111751,11 +111822,11 @@ | |
| 111822 | */ |
| 111823 | #ifndef SQLITE_OMIT_CTE |
| 111824 | SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p){ |
| 111825 | With *pRet = 0; |
| 111826 | if( p ){ |
| 111827 | sqlite3_int64 nByte = SZ_WITH(p->nCte); |
| 111828 | pRet = sqlite3DbMallocZero(db, nByte); |
| 111829 | if( pRet ){ |
| 111830 | int i; |
| 111831 | pRet->nCte = p->nCte; |
| 111832 | for(i=0; i<p->nCte; i++){ |
| @@ -111878,15 +111949,13 @@ | |
| 111949 | #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \ |
| 111950 | || !defined(SQLITE_OMIT_SUBQUERY) |
| 111951 | SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int flags){ |
| 111952 | SrcList *pNew; |
| 111953 | int i; |
| 111954 | assert( db!=0 ); |
| 111955 | if( p==0 ) return 0; |
| 111956 | pNew = sqlite3DbMallocRawNN(db, SZ_SRCLIST(p->nSrc) ); |
| 111957 | if( pNew==0 ) return 0; |
| 111958 | pNew->nSrc = pNew->nAlloc = p->nSrc; |
| 111959 | for(i=0; i<p->nSrc; i++){ |
| 111960 | SrcItem *pNewItem = &pNew->a[i]; |
| 111961 | const SrcItem *pOldItem = &p->a[i]; |
| @@ -111944,11 +112013,11 @@ | |
| 112013 | SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, const IdList *p){ |
| 112014 | IdList *pNew; |
| 112015 | int i; |
| 112016 | assert( db!=0 ); |
| 112017 | if( p==0 ) return 0; |
| 112018 | pNew = sqlite3DbMallocRawNN(db, SZ_IDLIST(p->nId)); |
| 112019 | if( pNew==0 ) return 0; |
| 112020 | pNew->nId = p->nId; |
| 112021 | for(i=0; i<p->nId; i++){ |
| 112022 | struct IdList_item *pNewItem = &pNew->a[i]; |
| 112023 | const struct IdList_item *pOldItem = &p->a[i]; |
| @@ -112028,11 +112097,11 @@ | |
| 112097 | Expr *pExpr /* Expression to be appended. Might be NULL */ |
| 112098 | ){ |
| 112099 | struct ExprList_item *pItem; |
| 112100 | ExprList *pList; |
| 112101 | |
| 112102 | pList = sqlite3DbMallocRawNN(db, SZ_EXPRLIST(4)); |
| 112103 | if( pList==0 ){ |
| 112104 | sqlite3ExprDelete(db, pExpr); |
| 112105 | return 0; |
| 112106 | } |
| 112107 | pList->nAlloc = 4; |
| @@ -112048,12 +112117,11 @@ | |
| 112117 | Expr *pExpr /* Expression to be appended. Might be NULL */ |
| 112118 | ){ |
| 112119 | struct ExprList_item *pItem; |
| 112120 | ExprList *pNew; |
| 112121 | pList->nAlloc *= 2; |
| 112122 | pNew = sqlite3DbRealloc(db, pList, SZ_EXPRLIST(pList->nAlloc)); |
| 112123 | if( pNew==0 ){ |
| 112124 | sqlite3ExprListDelete(db, pList); |
| 112125 | sqlite3ExprDelete(db, pExpr); |
| 112126 | return 0; |
| 112127 | }else{ |
| @@ -114685,11 +114753,11 @@ | |
| 114753 | return -1; /* Not found */ |
| 114754 | } |
| 114755 | |
| 114756 | |
| 114757 | /* |
| 114758 | ** Expression pExpr is guaranteed to be a TK_COLUMN or equivalent. This |
| 114759 | ** function checks the Parse.pIdxPartExpr list to see if this column |
| 114760 | ** can be replaced with a constant value. If so, it generates code to |
| 114761 | ** put the constant value in a register (ideally, but not necessarily, |
| 114762 | ** register iTarget) and returns the register number. |
| 114763 | ** |
| @@ -118531,10 +118599,11 @@ | |
| 118599 | sqlite3 *db, /* Database handle */ |
| 118600 | const char *zSql, /* SQL to parse */ |
| 118601 | int bTemp /* True if SQL is from temp schema */ |
| 118602 | ){ |
| 118603 | int rc; |
| 118604 | u64 flags; |
| 118605 | |
| 118606 | sqlite3ParseObjectInit(p, db); |
| 118607 | if( zSql==0 ){ |
| 118608 | return SQLITE_NOMEM; |
| 118609 | } |
| @@ -118549,11 +118618,15 @@ | |
| 118618 | db->init.iDb = (u8)iDb; |
| 118619 | } |
| 118620 | p->eParseMode = PARSE_MODE_RENAME; |
| 118621 | p->db = db; |
| 118622 | p->nQueryLoop = 1; |
| 118623 | flags = db->flags; |
| 118624 | testcase( (db->flags & SQLITE_Comments)==0 && strstr(zSql," /* ")!=0 ); |
| 118625 | db->flags |= SQLITE_Comments; |
| 118626 | rc = sqlite3RunParser(p, zSql); |
| 118627 | db->flags = flags; |
| 118628 | if( db->mallocFailed ) rc = SQLITE_NOMEM; |
| 118629 | if( rc==SQLITE_OK |
| 118630 | && NEVER(p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0) |
| 118631 | ){ |
| 118632 | rc = SQLITE_CORRUPT_BKPT; |
| @@ -119445,11 +119518,11 @@ | |
| 119518 | int rc; |
| 119519 | Parse sParse; |
| 119520 | u64 flags = db->flags; |
| 119521 | if( bNoDQS ) db->flags &= ~(SQLITE_DqsDML|SQLITE_DqsDDL); |
| 119522 | rc = renameParseSql(&sParse, zDb, db, zInput, bTemp); |
| 119523 | db->flags = flags; |
| 119524 | if( rc==SQLITE_OK ){ |
| 119525 | if( isLegacy==0 && sParse.pNewTable && IsView(sParse.pNewTable) ){ |
| 119526 | NameContext sNC; |
| 119527 | memset(&sNC, 0, sizeof(sNC)); |
| 119528 | sNC.pParse = &sParse; |
| @@ -126268,11 +126341,11 @@ | |
| 126341 | "columns in the referenced table"); |
| 126342 | goto fk_end; |
| 126343 | }else{ |
| 126344 | nCol = pFromCol->nExpr; |
| 126345 | } |
| 126346 | nByte = SZ_FKEY(nCol) + pTo->n + 1; |
| 126347 | if( pToCol ){ |
| 126348 | for(i=0; i<pToCol->nExpr; i++){ |
| 126349 | nByte += sqlite3Strlen30(pToCol->a[i].zEName) + 1; |
| 126350 | } |
| 126351 | } |
| @@ -127327,16 +127400,15 @@ | |
| 127400 | */ |
| 127401 | SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *pToken){ |
| 127402 | sqlite3 *db = pParse->db; |
| 127403 | int i; |
| 127404 | if( pList==0 ){ |
| 127405 | pList = sqlite3DbMallocZero(db, SZ_IDLIST(1)); |
| 127406 | if( pList==0 ) return 0; |
| 127407 | }else{ |
| 127408 | IdList *pNew; |
| 127409 | pNew = sqlite3DbRealloc(db, pList, SZ_IDLIST(pList->nId+1)); |
| 127410 | if( pNew==0 ){ |
| 127411 | sqlite3IdListDelete(db, pList); |
| 127412 | return 0; |
| 127413 | } |
| 127414 | pList = pNew; |
| @@ -127431,12 +127503,11 @@ | |
| 127503 | sqlite3ErrorMsg(pParse, "too many FROM clause terms, max: %d", |
| 127504 | SQLITE_MAX_SRCLIST); |
| 127505 | return 0; |
| 127506 | } |
| 127507 | if( nAlloc>SQLITE_MAX_SRCLIST ) nAlloc = SQLITE_MAX_SRCLIST; |
| 127508 | pNew = sqlite3DbRealloc(db, pSrc, SZ_SRCLIST(nAlloc)); |
| 127509 | if( pNew==0 ){ |
| 127510 | assert( db->mallocFailed ); |
| 127511 | return 0; |
| 127512 | } |
| 127513 | pSrc = pNew; |
| @@ -127507,11 +127578,11 @@ | |
| 127578 | assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */ |
| 127579 | assert( pParse!=0 ); |
| 127580 | assert( pParse->db!=0 ); |
| 127581 | db = pParse->db; |
| 127582 | if( pList==0 ){ |
| 127583 | pList = sqlite3DbMallocRawNN(pParse->db, SZ_SRCLIST(1)); |
| 127584 | if( pList==0 ) return 0; |
| 127585 | pList->nAlloc = 1; |
| 127586 | pList->nSrc = 1; |
| 127587 | memset(&pList->a[0], 0, sizeof(pList->a[0])); |
| 127588 | pList->a[0].iCursor = -1; |
| @@ -128393,14 +128464,13 @@ | |
| 128464 | } |
| 128465 | } |
| 128466 | } |
| 128467 | |
| 128468 | if( pWith ){ |
| 128469 | pNew = sqlite3DbRealloc(db, pWith, SZ_WITH(pWith->nCte+1)); |
| 128470 | }else{ |
| 128471 | pNew = sqlite3DbMallocZero(db, SZ_WITH(1)); |
| 128472 | } |
| 128473 | assert( (pNew!=0 && zName!=0) || db->mallocFailed ); |
| 128474 | |
| 128475 | if( db->mallocFailed ){ |
| 128476 | sqlite3CteDelete(db, pCte); |
| @@ -131933,11 +132003,11 @@ | |
| 132003 | ** |
| 132004 | ** The SUM() function follows the (broken) SQL standard which means |
| 132005 | ** that it returns NULL if it sums over no inputs. TOTAL returns |
| 132006 | ** 0.0 in that case. In addition, TOTAL always returns a float where |
| 132007 | ** SUM might return an integer if it never encounters a floating point |
| 132008 | ** value. TOTAL never fails, but SUM might throw an exception if |
| 132009 | ** it overflows an integer. |
| 132010 | */ |
| 132011 | static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){ |
| 132012 | SumCtx *p; |
| 132013 | int type; |
| @@ -139748,52 +139818,52 @@ | |
| 139818 | /* 11 */ "notnull", |
| 139819 | /* 12 */ "dflt_value", |
| 139820 | /* 13 */ "pk", |
| 139821 | /* 14 */ "hidden", |
| 139822 | /* table_info reuses 8 */ |
| 139823 | /* 15 */ "name", /* Used by: function_list */ |
| 139824 | /* 16 */ "builtin", |
| 139825 | /* 17 */ "type", |
| 139826 | /* 18 */ "enc", |
| 139827 | /* 19 */ "narg", |
| 139828 | /* 20 */ "flags", |
| 139829 | /* 21 */ "schema", /* Used by: table_list */ |
| 139830 | /* 22 */ "name", |
| 139831 | /* 23 */ "type", |
| 139832 | /* 24 */ "ncol", |
| 139833 | /* 25 */ "wr", |
| 139834 | /* 26 */ "strict", |
| 139835 | /* 27 */ "seqno", /* Used by: index_xinfo */ |
| 139836 | /* 28 */ "cid", |
| 139837 | /* 29 */ "name", |
| 139838 | /* 30 */ "desc", |
| 139839 | /* 31 */ "coll", |
| 139840 | /* 32 */ "key", |
| 139841 | /* 33 */ "seq", /* Used by: index_list */ |
| 139842 | /* 34 */ "name", |
| 139843 | /* 35 */ "unique", |
| 139844 | /* 36 */ "origin", |
| 139845 | /* 37 */ "partial", |
| 139846 | /* 38 */ "tbl", /* Used by: stats */ |
| 139847 | /* 39 */ "idx", |
| 139848 | /* 40 */ "wdth", |
| 139849 | /* 41 */ "hght", |
| 139850 | /* 42 */ "flgs", |
| 139851 | /* 43 */ "table", /* Used by: foreign_key_check */ |
| 139852 | /* 44 */ "rowid", |
| 139853 | /* 45 */ "parent", |
| 139854 | /* 46 */ "fkid", |
| 139855 | /* 47 */ "busy", /* Used by: wal_checkpoint */ |
| 139856 | /* 48 */ "log", |
| 139857 | /* 49 */ "checkpointed", |
| 139858 | /* 50 */ "seq", /* Used by: database_list */ |
| 139859 | /* 51 */ "name", |
| 139860 | /* 52 */ "file", |
| 139861 | /* index_info reuses 27 */ |
| 139862 | /* 53 */ "database", /* Used by: lock_status */ |
| 139863 | /* 54 */ "status", |
| 139864 | /* collation_list reuses 33 */ |
| 139865 | /* 55 */ "cache_size", /* Used by: default_cache_size */ |
| 139866 | /* module_list pragma_list reuses 9 */ |
| 139867 | /* 56 */ "timeout", /* Used by: busy_timeout */ |
| 139868 | }; |
| 139869 | |
| @@ -139882,11 +139952,11 @@ | |
| 139952 | #endif |
| 139953 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 139954 | {/* zName: */ "collation_list", |
| 139955 | /* ePragTyp: */ PragTyp_COLLATION_LIST, |
| 139956 | /* ePragFlg: */ PragFlg_Result0, |
| 139957 | /* ColNames: */ 33, 2, |
| 139958 | /* iArg: */ 0 }, |
| 139959 | #endif |
| 139960 | #if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS) |
| 139961 | {/* zName: */ "compile_options", |
| 139962 | /* ePragTyp: */ PragTyp_COMPILE_OPTIONS, |
| @@ -139917,11 +139987,11 @@ | |
| 139987 | #endif |
| 139988 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 139989 | {/* zName: */ "database_list", |
| 139990 | /* ePragTyp: */ PragTyp_DATABASE_LIST, |
| 139991 | /* ePragFlg: */ PragFlg_Result0, |
| 139992 | /* ColNames: */ 50, 3, |
| 139993 | /* iArg: */ 0 }, |
| 139994 | #endif |
| 139995 | #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED) |
| 139996 | {/* zName: */ "default_cache_size", |
| 139997 | /* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE, |
| @@ -139997,11 +140067,11 @@ | |
| 140067 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 140068 | #if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS) |
| 140069 | {/* zName: */ "function_list", |
| 140070 | /* ePragTyp: */ PragTyp_FUNCTION_LIST, |
| 140071 | /* ePragFlg: */ PragFlg_Result0, |
| 140072 | /* ColNames: */ 15, 6, |
| 140073 | /* iArg: */ 0 }, |
| 140074 | #endif |
| 140075 | #endif |
| 140076 | {/* zName: */ "hard_heap_limit", |
| 140077 | /* ePragTyp: */ PragTyp_HARD_HEAP_LIMIT, |
| @@ -140026,21 +140096,21 @@ | |
| 140096 | #endif |
| 140097 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 140098 | {/* zName: */ "index_info", |
| 140099 | /* ePragTyp: */ PragTyp_INDEX_INFO, |
| 140100 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 140101 | /* ColNames: */ 27, 3, |
| 140102 | /* iArg: */ 0 }, |
| 140103 | {/* zName: */ "index_list", |
| 140104 | /* ePragTyp: */ PragTyp_INDEX_LIST, |
| 140105 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 140106 | /* ColNames: */ 33, 5, |
| 140107 | /* iArg: */ 0 }, |
| 140108 | {/* zName: */ "index_xinfo", |
| 140109 | /* ePragTyp: */ PragTyp_INDEX_INFO, |
| 140110 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 140111 | /* ColNames: */ 27, 6, |
| 140112 | /* iArg: */ 1 }, |
| 140113 | #endif |
| 140114 | #if !defined(SQLITE_OMIT_INTEGRITY_CHECK) |
| 140115 | {/* zName: */ "integrity_check", |
| 140116 | /* ePragTyp: */ PragTyp_INTEGRITY_CHECK, |
| @@ -140215,11 +140285,11 @@ | |
| 140285 | #endif |
| 140286 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && defined(SQLITE_DEBUG) |
| 140287 | {/* zName: */ "stats", |
| 140288 | /* ePragTyp: */ PragTyp_STATS, |
| 140289 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq, |
| 140290 | /* ColNames: */ 38, 5, |
| 140291 | /* iArg: */ 0 }, |
| 140292 | #endif |
| 140293 | #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) |
| 140294 | {/* zName: */ "synchronous", |
| 140295 | /* ePragTyp: */ PragTyp_SYNCHRONOUS, |
| @@ -140234,11 +140304,11 @@ | |
| 140304 | /* ColNames: */ 8, 6, |
| 140305 | /* iArg: */ 0 }, |
| 140306 | {/* zName: */ "table_list", |
| 140307 | /* ePragTyp: */ PragTyp_TABLE_LIST, |
| 140308 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1, |
| 140309 | /* ColNames: */ 21, 6, |
| 140310 | /* iArg: */ 0 }, |
| 140311 | {/* zName: */ "table_xinfo", |
| 140312 | /* ePragTyp: */ PragTyp_TABLE_INFO, |
| 140313 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 140314 | /* ColNames: */ 8, 7, |
| @@ -140311,11 +140381,11 @@ | |
| 140381 | /* ColNames: */ 0, 0, |
| 140382 | /* iArg: */ 0 }, |
| 140383 | {/* zName: */ "wal_checkpoint", |
| 140384 | /* ePragTyp: */ PragTyp_WAL_CHECKPOINT, |
| 140385 | /* ePragFlg: */ PragFlg_NeedSchema, |
| 140386 | /* ColNames: */ 47, 3, |
| 140387 | /* iArg: */ 0 }, |
| 140388 | #endif |
| 140389 | #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) |
| 140390 | {/* zName: */ "writable_schema", |
| 140391 | /* ePragTyp: */ PragTyp_FLAG, |
| @@ -140333,11 +140403,11 @@ | |
| 140403 | ** When the 0x10 bit of PRAGMA optimize is set, any ANALYZE commands |
| 140404 | ** will be run with an analysis_limit set to the lessor of the value of |
| 140405 | ** the following macro or to the actual analysis_limit if it is non-zero, |
| 140406 | ** in order to prevent PRAGMA optimize from running for too long. |
| 140407 | ** |
| 140408 | ** The value of 2000 is chosen empirically so that the worst-case run-time |
| 140409 | ** for PRAGMA optimize does not exceed 100 milliseconds against a variety |
| 140410 | ** of test databases on a RaspberryPI-4 compiled using -Os and without |
| 140411 | ** -DSQLITE_DEBUG. Of course, your mileage may vary. For the purpose of |
| 140412 | ** this paragraph, "worst-case" means that ANALYZE ends up being |
| 140413 | ** run on every table in the database. The worst case typically only |
| @@ -144622,11 +144692,11 @@ | |
| 144692 | pNew->iOffset = 0; |
| 144693 | pNew->selId = ++pParse->nSelect; |
| 144694 | pNew->addrOpenEphm[0] = -1; |
| 144695 | pNew->addrOpenEphm[1] = -1; |
| 144696 | pNew->nSelectRow = 0; |
| 144697 | if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1); |
| 144698 | pNew->pSrc = pSrc; |
| 144699 | pNew->pWhere = pWhere; |
| 144700 | pNew->pGroupBy = pGroupBy; |
| 144701 | pNew->pHaving = pHaving; |
| 144702 | pNew->pOrderBy = pOrderBy; |
| @@ -146005,20 +146075,20 @@ | |
| 146075 | /* |
| 146076 | ** Allocate a KeyInfo object sufficient for an index of N key columns and |
| 146077 | ** X extra columns. |
| 146078 | */ |
| 146079 | SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){ |
| 146080 | int nExtra = (N+X)*(sizeof(CollSeq*)+1); |
| 146081 | KeyInfo *p = sqlite3DbMallocRawNN(db, SZ_KEYINFO(0) + nExtra); |
| 146082 | if( p ){ |
| 146083 | p->aSortFlags = (u8*)&p->aColl[N+X]; |
| 146084 | p->nKeyField = (u16)N; |
| 146085 | p->nAllField = (u16)(N+X); |
| 146086 | p->enc = ENC(db); |
| 146087 | p->db = db; |
| 146088 | p->nRef = 1; |
| 146089 | memset(p->aColl, 0, nExtra); |
| 146090 | }else{ |
| 146091 | return (KeyInfo*)sqlite3OomFault(db); |
| 146092 | } |
| 146093 | return p; |
| 146094 | } |
| @@ -150530,11 +150600,11 @@ | |
| 150600 | } |
| 150601 | pTabList = p->pSrc; |
| 150602 | pEList = p->pEList; |
| 150603 | if( pParse->pWith && (p->selFlags & SF_View) ){ |
| 150604 | if( p->pWith==0 ){ |
| 150605 | p->pWith = (With*)sqlite3DbMallocZero(db, SZ_WITH(1) ); |
| 150606 | if( p->pWith==0 ){ |
| 150607 | return WRC_Abort; |
| 150608 | } |
| 150609 | } |
| 150610 | p->pWith->bView = 1; |
| @@ -151669,10 +151739,11 @@ | |
| 151739 | ** * The subquery is a UNION ALL of two or more terms |
| 151740 | ** * The subquery does not have a LIMIT clause |
| 151741 | ** * There is no WHERE or GROUP BY or HAVING clauses on the subqueries |
| 151742 | ** * The outer query is a simple count(*) with no WHERE clause or other |
| 151743 | ** extraneous syntax. |
| 151744 | ** * None of the subqueries are DISTINCT (forumpost/a860f5fb2e 2025-03-10) |
| 151745 | ** |
| 151746 | ** Return TRUE if the optimization is undertaken. |
| 151747 | */ |
| 151748 | static int countOfViewOptimization(Parse *pParse, Select *p){ |
| 151749 | Select *pSub, *pPrior; |
| @@ -151701,11 +151772,15 @@ | |
| 151772 | if( pSub->selFlags & SF_CopyCte ) return 0; /* Not a CTE */ |
| 151773 | do{ |
| 151774 | if( pSub->op!=TK_ALL && pSub->pPrior ) return 0; /* Must be UNION ALL */ |
| 151775 | if( pSub->pWhere ) return 0; /* No WHERE clause */ |
| 151776 | if( pSub->pLimit ) return 0; /* No LIMIT clause */ |
| 151777 | if( pSub->selFlags & (SF_Aggregate|SF_Distinct) ){ |
| 151778 | testcase( pSub->selFlags & SF_Aggregate ); |
| 151779 | testcase( pSub->selFlags & SF_Distinct ); |
| 151780 | return 0; /* Not an aggregate nor DISTINCT */ |
| 151781 | } |
| 151782 | assert( pSub->pHaving==0 ); /* Due to the previous */ |
| 151783 | pSub = pSub->pPrior; /* Repeat over compound */ |
| 151784 | }while( pSub ); |
| 151785 | |
| 151786 | /* If we reach this point then it is OK to perform the transformation */ |
| @@ -151713,11 +151788,11 @@ | |
| 151788 | db = pParse->db; |
| 151789 | pCount = pExpr; |
| 151790 | pExpr = 0; |
| 151791 | pSub = sqlite3SubqueryDetach(db, pFrom); |
| 151792 | sqlite3SrcListDelete(db, p->pSrc); |
| 151793 | p->pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1); |
| 151794 | while( pSub ){ |
| 151795 | Expr *pTerm; |
| 151796 | pPrior = pSub->pPrior; |
| 151797 | pSub->pPrior = 0; |
| 151798 | pSub->pNext = 0; |
| @@ -154504,11 +154579,12 @@ | |
| 154579 | Vdbe *v = pParse->pVdbe; |
| 154580 | sqlite3 *db = pParse->db; |
| 154581 | ExprList *pNew; |
| 154582 | Returning *pReturning; |
| 154583 | Select sSelect; |
| 154584 | SrcList *pFrom; |
| 154585 | u8 fromSpace[SZ_SRCLIST_1]; |
| 154586 | |
| 154587 | assert( v!=0 ); |
| 154588 | if( !pParse->bReturning ){ |
| 154589 | /* This RETURNING trigger must be for a different statement as |
| 154590 | ** this statement lacks a RETURNING clause. */ |
| @@ -154520,17 +154596,18 @@ | |
| 154596 | if( pTrigger != &(pReturning->retTrig) ){ |
| 154597 | /* This RETURNING trigger is for a different statement */ |
| 154598 | return; |
| 154599 | } |
| 154600 | memset(&sSelect, 0, sizeof(sSelect)); |
| 154601 | pFrom = (SrcList*)fromSpace; |
| 154602 | memset(pFrom, 0, SZ_SRCLIST_1); |
| 154603 | sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0); |
| 154604 | sSelect.pSrc = pFrom; |
| 154605 | pFrom->nSrc = 1; |
| 154606 | pFrom->a[0].pSTab = pTab; |
| 154607 | pFrom->a[0].zName = pTab->zName; /* tag-20240424-1 */ |
| 154608 | pFrom->a[0].iCursor = -1; |
| 154609 | sqlite3SelectPrep(pParse, &sSelect, 0); |
| 154610 | if( pParse->nErr==0 ){ |
| 154611 | assert( db->mallocFailed==0 ); |
| 154612 | sqlite3GenerateColumnNames(pParse, &sSelect); |
| 154613 | } |
| @@ -156927,11 +157004,11 @@ | |
| 157004 | saved_flags = db->flags; |
| 157005 | saved_mDbFlags = db->mDbFlags; |
| 157006 | saved_nChange = db->nChange; |
| 157007 | saved_nTotalChange = db->nTotalChange; |
| 157008 | saved_mTrace = db->mTrace; |
| 157009 | db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_Comments; |
| 157010 | db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum; |
| 157011 | db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder |
| 157012 | | SQLITE_Defensive | SQLITE_CountRows); |
| 157013 | db->mTrace = 0; |
| 157014 | |
| @@ -159056,13 +159133,18 @@ | |
| 159133 | WhereLoop *pLoops; /* List of all WhereLoop objects */ |
| 159134 | WhereMemBlock *pMemToFree;/* Memory to free when this object destroyed */ |
| 159135 | Bitmask revMask; /* Mask of ORDER BY terms that need reversing */ |
| 159136 | WhereClause sWC; /* Decomposition of the WHERE clause */ |
| 159137 | WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */ |
| 159138 | WhereLevel a[FLEXARRAY]; /* Information about each nest loop in WHERE */ |
| 159139 | }; |
| 159140 | |
| 159141 | /* |
| 159142 | ** The size (in bytes) of a WhereInfo object that holds N WhereLevels. |
| 159143 | */ |
| 159144 | #define SZ_WHEREINFO(N) ROUND8(offsetof(WhereInfo,a)+(N)*sizeof(WhereLevel)) |
| 159145 | |
| 159146 | /* |
| 159147 | ** Private interfaces - callable only by other where.c routines. |
| 159148 | ** |
| 159149 | ** where.c: |
| 159150 | */ |
| @@ -161509,12 +161591,11 @@ | |
| 161591 | */ |
| 161592 | if( pWInfo->nLevel>1 ){ |
| 161593 | int nNotReady; /* The number of notReady tables */ |
| 161594 | SrcItem *origSrc; /* Original list of tables */ |
| 161595 | nNotReady = pWInfo->nLevel - iLevel - 1; |
| 161596 | pOrTab = sqlite3DbMallocRawNN(db, SZ_SRCLIST(nNotReady+1)); |
| 161597 | if( pOrTab==0 ) return notReady; |
| 161598 | pOrTab->nAlloc = (u8)(nNotReady + 1); |
| 161599 | pOrTab->nSrc = pOrTab->nAlloc; |
| 161600 | memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem)); |
| 161601 | origSrc = pWInfo->pTabList->a; |
| @@ -162053,11 +162134,12 @@ | |
| 162134 | Expr *pSubWhere = 0; |
| 162135 | WhereClause *pWC = &pWInfo->sWC; |
| 162136 | WhereInfo *pSubWInfo; |
| 162137 | WhereLoop *pLoop = pLevel->pWLoop; |
| 162138 | SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; |
| 162139 | SrcList *pFrom; |
| 162140 | u8 fromSpace[SZ_SRCLIST_1]; |
| 162141 | Bitmask mAll = 0; |
| 162142 | int k; |
| 162143 | |
| 162144 | ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pSTab->zName)); |
| 162145 | sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn, |
| @@ -162097,17 +162179,18 @@ | |
| 162179 | if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue; |
| 162180 | pSubWhere = sqlite3ExprAnd(pParse, pSubWhere, |
| 162181 | sqlite3ExprDup(pParse->db, pTerm->pExpr, 0)); |
| 162182 | } |
| 162183 | } |
| 162184 | pFrom = (SrcList*)fromSpace; |
| 162185 | pFrom->nSrc = 1; |
| 162186 | pFrom->nAlloc = 1; |
| 162187 | memcpy(&pFrom->a[0], pTabItem, sizeof(SrcItem)); |
| 162188 | pFrom->a[0].fg.jointype = 0; |
| 162189 | assert( pParse->withinRJSubrtn < 100 ); |
| 162190 | pParse->withinRJSubrtn++; |
| 162191 | pSubWInfo = sqlite3WhereBegin(pParse, pFrom, pSubWhere, 0, 0, 0, |
| 162192 | WHERE_RIGHT_JOIN, 0); |
| 162193 | if( pSubWInfo ){ |
| 162194 | int iCur = pLevel->iTabCur; |
| 162195 | int r = ++pParse->nMem; |
| 162196 | int nPk; |
| @@ -164091,15 +164174,20 @@ | |
| 164174 | WhereClause *pWC; /* The Where clause being analyzed */ |
| 164175 | Parse *pParse; /* The parsing context */ |
| 164176 | int eDistinct; /* Value to return from sqlite3_vtab_distinct() */ |
| 164177 | u32 mIn; /* Mask of terms that are <col> IN (...) */ |
| 164178 | u32 mHandleIn; /* Terms that vtab will handle as <col> IN (...) */ |
| 164179 | sqlite3_value *aRhs[FLEXARRAY]; /* RHS values for constraints. MUST BE LAST |
| 164180 | ** Extra space is allocated to hold up |
| 164181 | ** to nTerm such values */ |
| 164182 | }; |
| 164183 | |
| 164184 | /* Size (in bytes) of a HiddenIndeInfo object sufficient to hold as |
| 164185 | ** many as N constraints */ |
| 164186 | #define SZ_HIDDENINDEXINFO(N) \ |
| 164187 | (offsetof(HiddenIndexInfo,aRhs) + (N)*sizeof(sqlite3_value*)) |
| 164188 | |
| 164189 | /* Forward declaration of methods */ |
| 164190 | static int whereLoopResize(sqlite3*, WhereLoop*, int); |
| 164191 | |
| 164192 | /* |
| 164193 | ** Return the estimated number of output rows from a WHERE clause |
| @@ -165573,12 +165661,12 @@ | |
| 165661 | |
| 165662 | /* Allocate the sqlite3_index_info structure |
| 165663 | */ |
| 165664 | pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo) |
| 165665 | + (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm |
| 165666 | + sizeof(*pIdxOrderBy)*nOrderBy |
| 165667 | + SZ_HIDDENINDEXINFO(nTerm) ); |
| 165668 | if( pIdxInfo==0 ){ |
| 165669 | sqlite3ErrorMsg(pParse, "out of memory"); |
| 165670 | return 0; |
| 165671 | } |
| 165672 | pHidden = (struct HiddenIndexInfo*)&pIdxInfo[1]; |
| @@ -170768,14 +170856,11 @@ | |
| 170856 | ** struct, the contents of WhereInfo.a[], the WhereClause structure |
| 170857 | ** and the WhereMaskSet structure. Since WhereClause contains an 8-byte |
| 170858 | ** field (type Bitmask) it must be aligned on an 8-byte boundary on |
| 170859 | ** some architectures. Hence the ROUND8() below. |
| 170860 | */ |
| 170861 | nByteWInfo = SZ_WHEREINFO(nTabList); |
| 170862 | pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop)); |
| 170863 | if( db->mallocFailed ){ |
| 170864 | sqlite3DbFree(db, pWInfo); |
| 170865 | pWInfo = 0; |
| 170866 | goto whereBeginError; |
| @@ -181607,11 +181692,15 @@ | |
| 181692 | tokenType = analyzeOverKeyword((const u8*)&zSql[4], lastTokenParsed); |
| 181693 | }else if( tokenType==TK_FILTER ){ |
| 181694 | assert( n==6 ); |
| 181695 | tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed); |
| 181696 | #endif /* SQLITE_OMIT_WINDOWFUNC */ |
| 181697 | }else if( tokenType==TK_COMMENT |
| 181698 | && (db->init.busy || (db->flags & SQLITE_Comments)!=0) |
| 181699 | ){ |
| 181700 | /* Ignore SQL comments if either (1) we are reparsing the schema or |
| 181701 | ** (2) SQLITE_DBCONFIG_ENABLE_COMMENTS is turned on (the default). */ |
| 181702 | zSql += n; |
| 181703 | continue; |
| 181704 | }else if( tokenType!=TK_QNUMBER ){ |
| 181705 | Token x; |
| 181706 | x.z = zSql; |
| @@ -182502,10 +182591,18 @@ | |
| 182591 | } |
| 182592 | #endif |
| 182593 | if( rc==SQLITE_OK ){ |
| 182594 | sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage, |
| 182595 | sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage); |
| 182596 | #ifdef SQLITE_EXTRA_INIT_MUTEXED |
| 182597 | { |
| 182598 | int SQLITE_EXTRA_INIT_MUTEXED(const char*); |
| 182599 | rc = SQLITE_EXTRA_INIT_MUTEXED(0); |
| 182600 | } |
| 182601 | #endif |
| 182602 | } |
| 182603 | if( rc==SQLITE_OK ){ |
| 182604 | sqlite3MemoryBarrier(); |
| 182605 | sqlite3GlobalConfig.isInit = 1; |
| 182606 | #ifdef SQLITE_EXTRA_INIT |
| 182607 | bRunExtraInit = 1; |
| 182608 | #endif |
| @@ -184063,10 +184160,14 @@ | |
| 184160 | sqlite3_file *fd = sqlite3PagerFile(sqlite3BtreePager(pBt)); |
| 184161 | sqlite3OsFileControlHint(fd, SQLITE_FCNTL_BLOCK_ON_CONNECT, (void*)&bBOC); |
| 184162 | } |
| 184163 | } |
| 184164 | sqlite3BtreeLeaveAll(db); |
| 184165 | #endif |
| 184166 | #if !defined(SQLITE_ENABLE_API_ARMOR) && !defined(SQLITE_ENABLE_SETLK_TIMEOUT) |
| 184167 | UNUSED_PARAMETER(db); |
| 184168 | UNUSED_PARAMETER(flags); |
| 184169 | #endif |
| 184170 | return SQLITE_OK; |
| 184171 | } |
| 184172 | |
| 184173 | /* |
| @@ -186032,11 +186133,11 @@ | |
| 186133 | }else if( pData==0 ){ |
| 186134 | sqlite3_mutex_leave(db->mutex); |
| 186135 | return SQLITE_OK; |
| 186136 | }else{ |
| 186137 | size_t n = strlen(zName); |
| 186138 | p = sqlite3_malloc64( SZ_DBCLIENTDATA(n+1) ); |
| 186139 | if( p==0 ){ |
| 186140 | if( xDestructor ) xDestructor(pData); |
| 186141 | sqlite3_mutex_leave(db->mutex); |
| 186142 | return SQLITE_NOMEM; |
| 186143 | } |
| @@ -186398,12 +186499,12 @@ | |
| 186499 | #endif |
| 186500 | |
| 186501 | /* sqlite3_test_control(SQLITE_TESTCTRL_FK_NO_ACTION, sqlite3 *db, int b); |
| 186502 | ** |
| 186503 | ** If b is true, then activate the SQLITE_FkNoAction setting. If b is |
| 186504 | ** false then clear that setting. If the SQLITE_FkNoAction setting is |
| 186505 | ** enabled, all foreign key ON DELETE and ON UPDATE actions behave as if |
| 186506 | ** they were NO ACTION, regardless of how they are defined. |
| 186507 | ** |
| 186508 | ** NB: One must usually run "PRAGMA writable_schema=RESET" after |
| 186509 | ** using this test-control, before it will take full effect. failing |
| 186510 | ** to reset the schema can result in some unexpected behavior. |
| @@ -187746,11 +187847,11 @@ | |
| 187847 | ** } |
| 187848 | ** |
| 187849 | ** Here, array { X } means zero or more occurrences of X, adjacent in |
| 187850 | ** memory. A "position" is an index of a token in the token stream |
| 187851 | ** generated by the tokenizer. Note that POS_END and POS_COLUMN occur |
| 187852 | ** in the same logical place as the position element, and act as sentinels |
| 187853 | ** ending a position list array. POS_END is 0. POS_COLUMN is 1. |
| 187854 | ** The positions numbers are not stored literally but rather as two more |
| 187855 | ** than the difference from the prior position, or the just the position plus |
| 187856 | ** 2 for the first position. Example: |
| 187857 | ** |
| @@ -188433,10 +188534,23 @@ | |
| 188534 | |
| 188535 | #define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) |
| 188536 | #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) |
| 188537 | |
| 188538 | #define deliberate_fall_through |
| 188539 | |
| 188540 | /* |
| 188541 | ** Macros needed to provide flexible arrays in a portable way |
| 188542 | */ |
| 188543 | #ifndef offsetof |
| 188544 | # define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) |
| 188545 | #endif |
| 188546 | #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) |
| 188547 | # define FLEXARRAY |
| 188548 | #else |
| 188549 | # define FLEXARRAY 1 |
| 188550 | #endif |
| 188551 | |
| 188552 | |
| 188553 | #endif /* SQLITE_AMALGAMATION */ |
| 188554 | |
| 188555 | #ifdef SQLITE_DEBUG |
| 188556 | SQLITE_PRIVATE int sqlite3Fts3Corrupt(void); |
| @@ -188538,11 +188652,11 @@ | |
| 188652 | int inTransaction; /* True after xBegin but before xCommit/xRollback */ |
| 188653 | int mxSavepoint; /* Largest valid xSavepoint integer */ |
| 188654 | #endif |
| 188655 | |
| 188656 | #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) |
| 188657 | /* True to disable the incremental doclist optimization. This is controlled |
| 188658 | ** by special insert command 'test-no-incr-doclist'. */ |
| 188659 | int bNoIncrDoclist; |
| 188660 | |
| 188661 | /* Number of segments in a level */ |
| 188662 | int nMergeCount; |
| @@ -188590,11 +188704,11 @@ | |
| 188704 | #define FTS3_EVAL_NEXT 1 |
| 188705 | #define FTS3_EVAL_MATCHINFO 2 |
| 188706 | |
| 188707 | /* |
| 188708 | ** The Fts3Cursor.eSearch member is always set to one of the following. |
| 188709 | ** Actually, Fts3Cursor.eSearch can be greater than or equal to |
| 188710 | ** FTS3_FULLTEXT_SEARCH. If so, then Fts3Cursor.eSearch - 2 is the index |
| 188711 | ** of the column to be searched. For example, in |
| 188712 | ** |
| 188713 | ** CREATE VIRTUAL TABLE ex1 USING fts3(a,b,c,d); |
| 188714 | ** SELECT docid FROM ex1 WHERE b MATCH 'one two three'; |
| @@ -188663,12 +188777,16 @@ | |
| 188777 | /* Variables below this point are populated by fts3_expr.c when parsing |
| 188778 | ** a MATCH expression. Everything above is part of the evaluation phase. |
| 188779 | */ |
| 188780 | int nToken; /* Number of tokens in the phrase */ |
| 188781 | int iColumn; /* Index of column this phrase must match */ |
| 188782 | Fts3PhraseToken aToken[FLEXARRAY]; /* One for each token in the phrase */ |
| 188783 | }; |
| 188784 | |
| 188785 | /* Size (in bytes) of an Fts3Phrase object large enough to hold N tokens */ |
| 188786 | #define SZ_FTS3PHRASE(N) \ |
| 188787 | (offsetof(Fts3Phrase,aToken)+(N)*sizeof(Fts3PhraseToken)) |
| 188788 | |
| 188789 | /* |
| 188790 | ** A tree of these objects forms the RHS of a MATCH operator. |
| 188791 | ** |
| 188792 | ** If Fts3Expr.eType is FTSQUERY_PHRASE and isLoaded is true, then aDoclist |
| @@ -191243,11 +191361,11 @@ | |
| 191361 | ** |
| 191362 | ** The space required to store the output is therefore the sum of the |
| 191363 | ** sizes of the two inputs, plus enough space for exactly one of the input |
| 191364 | ** docids to grow. |
| 191365 | ** |
| 191366 | ** A symmetric argument may be made if the doclists are in descending |
| 191367 | ** order. |
| 191368 | */ |
| 191369 | aOut = sqlite3_malloc64((i64)n1+n2+FTS3_VARINT_MAX-1+FTS3_BUFFER_PADDING); |
| 191370 | if( !aOut ) return SQLITE_NOMEM; |
| 191371 | |
| @@ -193341,11 +193459,11 @@ | |
| 193459 | ** |
| 193460 | ** * features at least one token that uses an incremental doclist, and |
| 193461 | ** |
| 193462 | ** * does not contain any deferred tokens. |
| 193463 | ** |
| 193464 | ** Advance it to the next matching document in the database and populate |
| 193465 | ** the Fts3Doclist.pList and nList fields. |
| 193466 | ** |
| 193467 | ** If there is no "next" entry and no error occurs, then *pbEof is set to |
| 193468 | ** 1 before returning. Otherwise, if no error occurs and the iterator is |
| 193469 | ** successfully advanced, *pbEof is set to 0. |
| @@ -194348,11 +194466,11 @@ | |
| 194466 | |
| 194467 | return rc; |
| 194468 | } |
| 194469 | |
| 194470 | /* |
| 194471 | ** Restart iteration for expression pExpr so that the next call to |
| 194472 | ** fts3EvalNext() visits the first row. Do not allow incremental |
| 194473 | ** loading or merging of phrase doclists for this iteration. |
| 194474 | ** |
| 194475 | ** If *pRc is other than SQLITE_OK when this function is called, it is |
| 194476 | ** a no-op. If an error occurs within this function, *pRc is set to an |
| @@ -195539,10 +195657,27 @@ | |
| 195657 | /* |
| 195658 | ** Function getNextNode(), which is called by fts3ExprParse(), may itself |
| 195659 | ** call fts3ExprParse(). So this forward declaration is required. |
| 195660 | */ |
| 195661 | static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *); |
| 195662 | |
| 195663 | /* |
| 195664 | ** Search buffer z[], size n, for a '"' character. Or, if enable_parenthesis |
| 195665 | ** is defined, search for '(' and ')' as well. Return the index of the first |
| 195666 | ** such character in the buffer. If there is no such character, return -1. |
| 195667 | */ |
| 195668 | static int findBarredChar(const char *z, int n){ |
| 195669 | int ii; |
| 195670 | for(ii=0; ii<n; ii++){ |
| 195671 | if( (z[ii]=='"') |
| 195672 | || (sqlite3_fts3_enable_parentheses && (z[ii]=='(' || z[ii]==')')) |
| 195673 | ){ |
| 195674 | return ii; |
| 195675 | } |
| 195676 | } |
| 195677 | return -1; |
| 195678 | } |
| 195679 | |
| 195680 | /* |
| 195681 | ** Extract the next token from buffer z (length n) using the tokenizer |
| 195682 | ** and other information (column names etc.) in pParse. Create an Fts3Expr |
| 195683 | ** structure of type FTSQUERY_PHRASE containing a phrase consisting of this |
| @@ -195564,38 +195699,42 @@ | |
| 195699 | sqlite3_tokenizer *pTokenizer = pParse->pTokenizer; |
| 195700 | sqlite3_tokenizer_module const *pModule = pTokenizer->pModule; |
| 195701 | int rc; |
| 195702 | sqlite3_tokenizer_cursor *pCursor; |
| 195703 | Fts3Expr *pRet = 0; |
| 195704 | |
| 195705 | *pnConsumed = n; |
| 195706 | rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, n, &pCursor); |
| 195707 | if( rc==SQLITE_OK ){ |
| 195708 | const char *zToken; |
| 195709 | int nToken = 0, iStart = 0, iEnd = 0, iPosition = 0; |
| 195710 | sqlite3_int64 nByte; /* total space to allocate */ |
| 195711 | |
| 195712 | rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition); |
| 195713 | if( rc==SQLITE_OK ){ |
| 195714 | /* Check that this tokenization did not gobble up any " characters. Or, |
| 195715 | ** if enable_parenthesis is true, that it did not gobble up any |
| 195716 | ** open or close parenthesis characters either. If it did, call |
| 195717 | ** getNextToken() again, but pass only that part of the input buffer |
| 195718 | ** up to the first such character. */ |
| 195719 | int iBarred = findBarredChar(z, iEnd); |
| 195720 | if( iBarred>=0 ){ |
| 195721 | pModule->xClose(pCursor); |
| 195722 | return getNextToken(pParse, iCol, z, iBarred, ppExpr, pnConsumed); |
| 195723 | } |
| 195724 | |
| 195725 | nByte = sizeof(Fts3Expr) + SZ_FTS3PHRASE(1) + nToken; |
| 195726 | pRet = (Fts3Expr *)sqlite3Fts3MallocZero(nByte); |
| 195727 | if( !pRet ){ |
| 195728 | rc = SQLITE_NOMEM; |
| 195729 | }else{ |
| 195730 | pRet->eType = FTSQUERY_PHRASE; |
| 195731 | pRet->pPhrase = (Fts3Phrase *)&pRet[1]; |
| 195732 | pRet->pPhrase->nToken = 1; |
| 195733 | pRet->pPhrase->iColumn = iCol; |
| 195734 | pRet->pPhrase->aToken[0].n = nToken; |
| 195735 | pRet->pPhrase->aToken[0].z = (char*)&pRet->pPhrase->aToken[1]; |
| 195736 | memcpy(pRet->pPhrase->aToken[0].z, zToken, nToken); |
| 195737 | |
| 195738 | if( iEnd<n && z[iEnd]=='*' ){ |
| 195739 | pRet->pPhrase->aToken[0].isPrefix = 1; |
| 195740 | iEnd++; |
| @@ -195615,11 +195754,15 @@ | |
| 195754 | } |
| 195755 | } |
| 195756 | |
| 195757 | } |
| 195758 | *pnConsumed = iEnd; |
| 195759 | }else if( n && rc==SQLITE_DONE ){ |
| 195760 | int iBarred = findBarredChar(z, n); |
| 195761 | if( iBarred>=0 ){ |
| 195762 | *pnConsumed = iBarred; |
| 195763 | } |
| 195764 | rc = SQLITE_OK; |
| 195765 | } |
| 195766 | |
| 195767 | pModule->xClose(pCursor); |
| 195768 | } |
| @@ -195664,11 +195807,11 @@ | |
| 195807 | Fts3Expr *p = 0; |
| 195808 | sqlite3_tokenizer_cursor *pCursor = 0; |
| 195809 | char *zTemp = 0; |
| 195810 | i64 nTemp = 0; |
| 195811 | |
| 195812 | const int nSpace = sizeof(Fts3Expr) + SZ_FTS3PHRASE(1); |
| 195813 | int nToken = 0; |
| 195814 | |
| 195815 | /* The final Fts3Expr data structure, including the Fts3Phrase, |
| 195816 | ** Fts3PhraseToken structures token buffers are all stored as a single |
| 195817 | ** allocation so that the expression can be freed with a single call to |
| @@ -196036,11 +196179,11 @@ | |
| 196179 | int eType = p->eType; |
| 196180 | isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft); |
| 196181 | |
| 196182 | /* The isRequirePhrase variable is set to true if a phrase or |
| 196183 | ** an expression contained in parenthesis is required. If a |
| 196184 | ** binary operator (AND, OR, NOT or NEAR) is encountered when |
| 196185 | ** isRequirePhrase is set, this is a syntax error. |
| 196186 | */ |
| 196187 | if( !isPhrase && isRequirePhrase ){ |
| 196188 | sqlite3Fts3ExprFree(p); |
| 196189 | rc = SQLITE_ERROR; |
| @@ -196618,11 +196761,10 @@ | |
| 196761 | pTokenizer, 0, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr |
| 196762 | ); |
| 196763 | } |
| 196764 | |
| 196765 | if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){ |
| 196766 | sqlite3_result_error(context, "Error parsing expression", -1); |
| 196767 | }else if( rc==SQLITE_NOMEM || !(zBuf = exprToString(pExpr, 0)) ){ |
| 196768 | sqlite3_result_error_nomem(context); |
| 196769 | }else{ |
| 196770 | sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); |
| @@ -196861,11 +197003,11 @@ | |
| 197003 | pEntry->count++; |
| 197004 | pEntry->chain = pNew; |
| 197005 | } |
| 197006 | |
| 197007 | |
| 197008 | /* Resize the hash table so that it contains "new_size" buckets. |
| 197009 | ** "new_size" must be a power of 2. The hash table might fail |
| 197010 | ** to resize if sqliteMalloc() fails. |
| 197011 | ** |
| 197012 | ** Return non-zero if a memory allocation error occurs. |
| 197013 | */ |
| @@ -197316,11 +197458,11 @@ | |
| 197458 | isConsonant(z+2); |
| 197459 | } |
| 197460 | |
| 197461 | /* |
| 197462 | ** If the word ends with zFrom and xCond() is true for the stem |
| 197463 | ** of the word that precedes the zFrom ending, then change the |
| 197464 | ** ending to zTo. |
| 197465 | ** |
| 197466 | ** The input word *pz and zFrom are both in reverse order. zTo |
| 197467 | ** is in normal order. |
| 197468 | ** |
| @@ -202899,11 +203041,11 @@ | |
| 203041 | ** previous term. Before this function returns, it is updated to contain a |
| 203042 | ** copy of zTerm/nTerm. |
| 203043 | ** |
| 203044 | ** It is assumed that the buffer associated with pNode is already large |
| 203045 | ** enough to accommodate the new entry. The buffer associated with pPrev |
| 203046 | ** is extended by this function if required. |
| 203047 | ** |
| 203048 | ** If an error (i.e. OOM condition) occurs, an SQLite error code is |
| 203049 | ** returned. Otherwise, SQLITE_OK. |
| 203050 | */ |
| 203051 | static int fts3AppendToNode( |
| @@ -204562,11 +204704,11 @@ | |
| 204704 | #endif |
| 204705 | |
| 204706 | /* |
| 204707 | ** SQLite value pRowid contains the rowid of a row that may or may not be |
| 204708 | ** present in the FTS3 table. If it is, delete it and adjust the contents |
| 204709 | ** of subsidiary data structures accordingly. |
| 204710 | */ |
| 204711 | static int fts3DeleteByRowid( |
| 204712 | Fts3Table *p, |
| 204713 | sqlite3_value *pRowid, |
| 204714 | int *pnChng, /* IN/OUT: Decrement if row is deleted */ |
| @@ -204888,12 +205030,16 @@ | |
| 205030 | struct MatchinfoBuffer { |
| 205031 | u8 aRef[3]; |
| 205032 | int nElem; |
| 205033 | int bGlobal; /* Set if global data is loaded */ |
| 205034 | char *zMatchinfo; |
| 205035 | u32 aMI[FLEXARRAY]; |
| 205036 | }; |
| 205037 | |
| 205038 | /* Size (in bytes) of a MatchinfoBuffer sufficient for N elements */ |
| 205039 | #define SZ_MATCHINFOBUFFER(N) \ |
| 205040 | (offsetof(MatchinfoBuffer,aMI)+(((N)+1)/2)*sizeof(u64)) |
| 205041 | |
| 205042 | |
| 205043 | /* |
| 205044 | ** The snippet() and offsets() functions both return text values. An instance |
| 205045 | ** of the following structure is used to accumulate those values while the |
| @@ -204915,17 +205061,17 @@ | |
| 205061 | ** Allocate a two-slot MatchinfoBuffer object. |
| 205062 | */ |
| 205063 | static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){ |
| 205064 | MatchinfoBuffer *pRet; |
| 205065 | sqlite3_int64 nByte = sizeof(u32) * (2*(sqlite3_int64)nElem + 1) |
| 205066 | + SZ_MATCHINFOBUFFER(1); |
| 205067 | sqlite3_int64 nStr = strlen(zMatchinfo); |
| 205068 | |
| 205069 | pRet = sqlite3Fts3MallocZero(nByte + nStr+1); |
| 205070 | if( pRet ){ |
| 205071 | pRet->aMI[0] = (u8*)(&pRet->aMI[1]) - (u8*)pRet; |
| 205072 | pRet->aMI[1+nElem] = pRet->aMI[0] |
| 205073 | + sizeof(u32)*((int)nElem+1); |
| 205074 | pRet->nElem = (int)nElem; |
| 205075 | pRet->zMatchinfo = ((char*)pRet) + nByte; |
| 205076 | memcpy(pRet->zMatchinfo, zMatchinfo, nStr+1); |
| 205077 | pRet->aRef[0] = 1; |
| @@ -204935,14 +205081,14 @@ | |
| 205081 | } |
| 205082 | |
| 205083 | static void fts3MIBufferFree(void *p){ |
| 205084 | MatchinfoBuffer *pBuf = (MatchinfoBuffer*)((u8*)p - ((u32*)p)[-1]); |
| 205085 | |
| 205086 | assert( (u32*)p==&pBuf->aMI[1] |
| 205087 | || (u32*)p==&pBuf->aMI[pBuf->nElem+2] |
| 205088 | ); |
| 205089 | if( (u32*)p==&pBuf->aMI[1] ){ |
| 205090 | pBuf->aRef[1] = 0; |
| 205091 | }else{ |
| 205092 | pBuf->aRef[2] = 0; |
| 205093 | } |
| 205094 | |
| @@ -204955,32 +205101,32 @@ | |
| 205101 | void (*xRet)(void*) = 0; |
| 205102 | u32 *aOut = 0; |
| 205103 | |
| 205104 | if( p->aRef[1]==0 ){ |
| 205105 | p->aRef[1] = 1; |
| 205106 | aOut = &p->aMI[1]; |
| 205107 | xRet = fts3MIBufferFree; |
| 205108 | } |
| 205109 | else if( p->aRef[2]==0 ){ |
| 205110 | p->aRef[2] = 1; |
| 205111 | aOut = &p->aMI[p->nElem+2]; |
| 205112 | xRet = fts3MIBufferFree; |
| 205113 | }else{ |
| 205114 | aOut = (u32*)sqlite3_malloc64(p->nElem * sizeof(u32)); |
| 205115 | if( aOut ){ |
| 205116 | xRet = sqlite3_free; |
| 205117 | if( p->bGlobal ) memcpy(aOut, &p->aMI[1], p->nElem*sizeof(u32)); |
| 205118 | } |
| 205119 | } |
| 205120 | |
| 205121 | *paOut = aOut; |
| 205122 | return xRet; |
| 205123 | } |
| 205124 | |
| 205125 | static void fts3MIBufferSetGlobal(MatchinfoBuffer *p){ |
| 205126 | p->bGlobal = 1; |
| 205127 | memcpy(&p->aMI[2+p->nElem], &p->aMI[1], p->nElem*sizeof(u32)); |
| 205128 | } |
| 205129 | |
| 205130 | /* |
| 205131 | ** Free a MatchinfoBuffer object allocated using fts3MIBufferNew() |
| 205132 | */ |
| @@ -205391,11 +205537,11 @@ | |
| 205537 | if( nAppend<0 ){ |
| 205538 | nAppend = (int)strlen(zAppend); |
| 205539 | } |
| 205540 | |
| 205541 | /* If there is insufficient space allocated at StrBuffer.z, use realloc() |
| 205542 | ** to grow the buffer until so that it is big enough to accommodate the |
| 205543 | ** appended data. |
| 205544 | */ |
| 205545 | if( pStr->n+nAppend+1>=pStr->nAlloc ){ |
| 205546 | sqlite3_int64 nAlloc = pStr->nAlloc+(sqlite3_int64)nAppend+100; |
| 205547 | char *zNew = sqlite3_realloc64(pStr->z, nAlloc); |
| @@ -207766,11 +207912,11 @@ | |
| 207912 | ** |
| 207913 | ** When a match if found, the matching entry is moved to become the |
| 207914 | ** most-recently used entry if it isn't so already. |
| 207915 | ** |
| 207916 | ** The JsonParse object returned still belongs to the Cache and might |
| 207917 | ** be deleted at any moment. If the caller wants the JsonParse to |
| 207918 | ** linger, it needs to increment the nPJRef reference counter. |
| 207919 | */ |
| 207920 | static JsonParse *jsonCacheSearch( |
| 207921 | sqlite3_context *ctx, /* The SQL statement context holding the cache */ |
| 207922 | sqlite3_value *pArg /* Function argument containing SQL text */ |
| @@ -210811,11 +210957,11 @@ | |
| 210957 | ** |
| 210958 | ** This goes against all historical documentation about how the SQLite |
| 210959 | ** JSON functions were suppose to work. From the beginning, blob was |
| 210960 | ** reserved for expansion and a blob value should have raised an error. |
| 210961 | ** But it did not, due to a bug. And many applications came to depend |
| 210962 | ** upon this buggy behavior, especially when using the CLI and reading |
| 210963 | ** JSON text using readfile(), which returns a blob. For this reason |
| 210964 | ** we will continue to support the bug moving forward. |
| 210965 | ** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d |
| 210966 | */ |
| 210967 | } |
| @@ -212911,10 +213057,18 @@ | |
| 213057 | # define NEVER(X) ((X)?(assert(0),1):0) |
| 213058 | #else |
| 213059 | # define ALWAYS(X) (X) |
| 213060 | # define NEVER(X) (X) |
| 213061 | #endif |
| 213062 | #ifndef offsetof |
| 213063 | #define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) |
| 213064 | #endif |
| 213065 | #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) |
| 213066 | # define FLEXARRAY |
| 213067 | #else |
| 213068 | # define FLEXARRAY 1 |
| 213069 | #endif |
| 213070 | #endif /* !defined(SQLITE_AMALGAMATION) */ |
| 213071 | |
| 213072 | /* Macro to check for 4-byte alignment. Only used inside of assert() */ |
| 213073 | #ifdef SQLITE_DEBUG |
| 213074 | # define FOUR_BYTE_ALIGNED(X) ((((char*)(X) - (char*)0) & 3)==0) |
| @@ -213231,13 +213385,17 @@ | |
| 213385 | struct RtreeMatchArg { |
| 213386 | u32 iSize; /* Size of this object */ |
| 213387 | RtreeGeomCallback cb; /* Info about the callback functions */ |
| 213388 | int nParam; /* Number of parameters to the SQL function */ |
| 213389 | sqlite3_value **apSqlParam; /* Original SQL parameter values */ |
| 213390 | RtreeDValue aParam[FLEXARRAY]; /* Values for parameters to the SQL function */ |
| 213391 | }; |
| 213392 | |
| 213393 | /* Size of an RtreeMatchArg object with N parameters */ |
| 213394 | #define SZ_RTREEMATCHARG(N) \ |
| 213395 | (offsetof(RtreeMatchArg,aParam)+(N)*sizeof(RtreeDValue)) |
| 213396 | |
| 213397 | #ifndef MAX |
| 213398 | # define MAX(x,y) ((x) < (y) ? (y) : (x)) |
| 213399 | #endif |
| 213400 | #ifndef MIN |
| 213401 | # define MIN(x,y) ((x) > (y) ? (y) : (x)) |
| @@ -214922,11 +215080,11 @@ | |
| 215080 | |
| 215081 | return rc; |
| 215082 | } |
| 215083 | |
| 215084 | /* |
| 215085 | ** Return the N-dimensional volume of the cell stored in *p. |
| 215086 | */ |
| 215087 | static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){ |
| 215088 | RtreeDValue area = (RtreeDValue)1; |
| 215089 | assert( pRtree->nDim>=1 && pRtree->nDim<=5 ); |
| 215090 | #ifndef SQLITE_RTREE_INT_ONLY |
| @@ -216688,11 +216846,11 @@ | |
| 216846 | } |
| 216847 | |
| 216848 | /* |
| 216849 | ** The second and subsequent arguments to this function are a printf() |
| 216850 | ** style format string and arguments. This function formats the string and |
| 216851 | ** appends it to the report being accumulated in pCheck. |
| 216852 | */ |
| 216853 | static void rtreeCheckAppendMsg(RtreeCheck *pCheck, const char *zFmt, ...){ |
| 216854 | va_list ap; |
| 216855 | va_start(ap, zFmt); |
| 216856 | if( pCheck->rc==SQLITE_OK && pCheck->nErr<RTREE_CHECK_MAX_ERROR ){ |
| @@ -217876,11 +218034,11 @@ | |
| 218034 | |
| 218035 | /* |
| 218036 | ** Determine if point (x0,y0) is beneath line segment (x1,y1)->(x2,y2). |
| 218037 | ** Returns: |
| 218038 | ** |
| 218039 | ** +2 x0,y0 is on the line segment |
| 218040 | ** |
| 218041 | ** +1 x0,y0 is beneath line segment |
| 218042 | ** |
| 218043 | ** 0 x0,y0 is not on or beneath the line segment or the line segment |
| 218044 | ** is vertical and x0,y0 is not on the line segment |
| @@ -217982,11 +218140,11 @@ | |
| 218140 | } |
| 218141 | sqlite3_free(p1); |
| 218142 | sqlite3_free(p2); |
| 218143 | } |
| 218144 | |
| 218145 | /* Objects used by the overlap algorithm. */ |
| 218146 | typedef struct GeoEvent GeoEvent; |
| 218147 | typedef struct GeoSegment GeoSegment; |
| 218148 | typedef struct GeoOverlap GeoOverlap; |
| 218149 | struct GeoEvent { |
| 218150 | double x; /* X coordinate at which event occurs */ |
| @@ -219029,12 +219187,11 @@ | |
| 219187 | RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx); |
| 219188 | RtreeMatchArg *pBlob; |
| 219189 | sqlite3_int64 nBlob; |
| 219190 | int memErr = 0; |
| 219191 | |
| 219192 | nBlob = SZ_RTREEMATCHARG(nArg) + nArg*sizeof(sqlite3_value*); |
| 219193 | pBlob = (RtreeMatchArg *)sqlite3_malloc64(nBlob); |
| 219194 | if( !pBlob ){ |
| 219195 | sqlite3_result_error_nomem(ctx); |
| 219196 | }else{ |
| 219197 | int i; |
| @@ -220125,11 +220282,11 @@ | |
| 220282 | ** to read from the original database snapshot. In other words, partially |
| 220283 | ** applied transactions are not visible to other clients. |
| 220284 | ** |
| 220285 | ** "RBU" stands for "Resumable Bulk Update". As in a large database update |
| 220286 | ** transmitted via a wireless network to a mobile device. A transaction |
| 220287 | ** applied using this extension is hence referred to as an "RBU update". |
| 220288 | ** |
| 220289 | ** |
| 220290 | ** LIMITATIONS |
| 220291 | ** |
| 220292 | ** An "RBU update" transaction is subject to the following limitations: |
| @@ -220422,11 +220579,11 @@ | |
| 220579 | ** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents |
| 220580 | ** of the state tables within the state database are zeroed. This way, |
| 220581 | ** the next call to sqlite3rbu_vacuum() opens a handle that starts a |
| 220582 | ** new RBU vacuum operation. |
| 220583 | ** |
| 220584 | ** As with sqlite3rbu_open(), Zipvfs users should refer to the comment |
| 220585 | ** describing the sqlite3rbu_create_vfs() API function below for |
| 220586 | ** a description of the complications associated with using RBU with |
| 220587 | ** zipvfs databases. |
| 220588 | */ |
| 220589 | SQLITE_API sqlite3rbu *sqlite3rbu_vacuum( |
| @@ -220518,11 +220675,11 @@ | |
| 220675 | /* |
| 220676 | ** Close an RBU handle. |
| 220677 | ** |
| 220678 | ** If the RBU update has been completely applied, mark the RBU database |
| 220679 | ** as fully applied. Otherwise, assuming no error has occurred, save the |
| 220680 | ** current state of the RBU update application to the RBU database. |
| 220681 | ** |
| 220682 | ** If an error has already occurred as part of an sqlite3rbu_step() |
| 220683 | ** or sqlite3rbu_open() call, or if one occurs within this function, an |
| 220684 | ** SQLite error code is returned. Additionally, if pzErrmsg is not NULL, |
| 220685 | ** *pzErrmsg may be set to point to a buffer containing a utf-8 formatted |
| @@ -225444,11 +225601,11 @@ | |
| 225601 | int rc; |
| 225602 | rc = p->pReal->pMethods->xFileSize(p->pReal, pSize); |
| 225603 | |
| 225604 | /* If this is an RBU vacuum operation and this is the target database, |
| 225605 | ** pretend that it has at least one page. Otherwise, SQLite will not |
| 225606 | ** check for the existence of a *-wal file. rbuVfsRead() contains |
| 225607 | ** similar logic. */ |
| 225608 | if( rc==SQLITE_OK && *pSize==0 |
| 225609 | && p->pRbu && rbuIsVacuum(p->pRbu) |
| 225610 | && (p->openFlags & SQLITE_OPEN_MAIN_DB) |
| 225611 | ){ |
| @@ -228674,11 +228831,11 @@ | |
| 228831 | } |
| 228832 | |
| 228833 | /* |
| 228834 | ** This function is called to initialize the SessionTable.nCol, azCol[] |
| 228835 | ** abPK[] and azDflt[] members of SessionTable object pTab. If these |
| 228836 | ** fields are already initialized, this function is a no-op. |
| 228837 | ** |
| 228838 | ** If an error occurs, an error code is stored in sqlite3_session.rc and |
| 228839 | ** non-zero returned. Or, if no error occurs but the table has no primary |
| 228840 | ** key, sqlite3_session.rc is left set to SQLITE_OK and non-zero returned to |
| 228841 | ** indicate that updates on this table should be ignored. SessionTable.abPK |
| @@ -230497,11 +230654,11 @@ | |
| 230654 | int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */ |
| 230655 | void **ppChangeset /* OUT: Buffer containing changeset */ |
| 230656 | ){ |
| 230657 | sqlite3 *db = pSession->db; /* Source database handle */ |
| 230658 | SessionTable *pTab; /* Used to iterate through attached tables */ |
| 230659 | SessionBuffer buf = {0,0,0}; /* Buffer in which to accumulate changeset */ |
| 230660 | int rc; /* Return code */ |
| 230661 | |
| 230662 | assert( xOutput==0 || (pnChangeset==0 && ppChangeset==0) ); |
| 230663 | assert( xOutput!=0 || (pnChangeset!=0 && ppChangeset!=0) ); |
| 230664 | |
| @@ -234931,10 +235088,22 @@ | |
| 235088 | # define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&3)==0) |
| 235089 | #else |
| 235090 | # define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0) |
| 235091 | #endif |
| 235092 | |
| 235093 | /* |
| 235094 | ** Macros needed to provide flexible arrays in a portable way |
| 235095 | */ |
| 235096 | #ifndef offsetof |
| 235097 | # define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) |
| 235098 | #endif |
| 235099 | #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) |
| 235100 | # define FLEXARRAY |
| 235101 | #else |
| 235102 | # define FLEXARRAY 1 |
| 235103 | #endif |
| 235104 | |
| 235105 | #endif |
| 235106 | |
| 235107 | /* Truncate very long tokens to this many bytes. Hard limit is |
| 235108 | ** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset |
| 235109 | ** field that occurs at the start of each leaf page (see fts5_index.c). */ |
| @@ -235003,14 +235172,15 @@ | |
| 235172 | ** |
| 235173 | ** This object is used by fts5_expr.c and fts5_index.c. |
| 235174 | */ |
| 235175 | struct Fts5Colset { |
| 235176 | int nCol; |
| 235177 | int aiCol[FLEXARRAY]; |
| 235178 | }; |
| 235179 | |
| 235180 | /* Size (int bytes) of a complete Fts5Colset object with N columns. */ |
| 235181 | #define SZ_FTS5COLSET(N) (sizeof(i64)*((N+2)/2)) |
| 235182 | |
| 235183 | /************************************************************************** |
| 235184 | ** Interface to code in fts5_config.c. fts5_config.c contains contains code |
| 235185 | ** to parse the arguments passed to the CREATE VIRTUAL TABLE statement. |
| 235186 | */ |
| @@ -235835,11 +236005,11 @@ | |
| 236005 | ************************************************************************* |
| 236006 | ** Driver template for the LEMON parser generator. |
| 236007 | ** |
| 236008 | ** The "lemon" program processes an LALR(1) input grammar file, then uses |
| 236009 | ** this template to construct a parser. The "lemon" program inserts text |
| 236010 | ** at each "%%" line. Also, any "P-a-r-s-e" identifier prefix (without the |
| 236011 | ** interstitial "-" characters) contained in this template is changed into |
| 236012 | ** the value of the %name directive from the grammar. Otherwise, the content |
| 236013 | ** of this template is copied straight through into the generate parser |
| 236014 | ** source file. |
| 236015 | ** |
| @@ -237989,11 +238159,11 @@ | |
| 238159 | ** where "N" is the total number of documents in the set and nHit |
| 238160 | ** is the number that contain at least one instance of the phrase |
| 238161 | ** under consideration. |
| 238162 | ** |
| 238163 | ** The problem with this is that if (N < 2*nHit), the IDF is |
| 238164 | ** negative. Which is undesirable. So the minimum allowable IDF is |
| 238165 | ** (1e-6) - roughly the same as a term that appears in just over |
| 238166 | ** half of set of 5,000,000 documents. */ |
| 238167 | double idf = log( (nRow - nHit + 0.5) / (nHit + 0.5) ); |
| 238168 | if( idf<=0.0 ) idf = 1e-6; |
| 238169 | p->aIDF[i] = idf; |
| @@ -238452,11 +238622,11 @@ | |
| 238622 | ** |
| 238623 | ** * All non-ASCII characters, |
| 238624 | ** * The 52 upper and lower case ASCII characters, and |
| 238625 | ** * The 10 integer ASCII characters. |
| 238626 | ** * The underscore character "_" (0x5F). |
| 238627 | ** * The unicode "substitute" character (0x1A). |
| 238628 | */ |
| 238629 | static int sqlite3Fts5IsBareword(char t){ |
| 238630 | u8 aBareword[128] = { |
| 238631 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 .. 0x0F */ |
| 238632 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* 0x10 .. 0x1F */ |
| @@ -239770,12 +239940,16 @@ | |
| 239940 | Fts5ExprNearset *pNear; /* For FTS5_STRING - cluster of phrases */ |
| 239941 | |
| 239942 | /* Child nodes. For a NOT node, this array always contains 2 entries. For |
| 239943 | ** AND or OR nodes, it contains 2 or more entries. */ |
| 239944 | int nChild; /* Number of child nodes */ |
| 239945 | Fts5ExprNode *apChild[FLEXARRAY]; /* Array of child nodes */ |
| 239946 | }; |
| 239947 | |
| 239948 | /* Size (in bytes) of an Fts5ExprNode object that holds up to N children */ |
| 239949 | #define SZ_FTS5EXPRNODE(N) \ |
| 239950 | (offsetof(Fts5ExprNode,apChild) + (N)*sizeof(Fts5ExprNode*)) |
| 239951 | |
| 239952 | #define Fts5NodeIsString(p) ((p)->eType==FTS5_TERM || (p)->eType==FTS5_STRING) |
| 239953 | |
| 239954 | /* |
| 239955 | ** Invoke the xNext method of an Fts5ExprNode object. This macro should be |
| @@ -239803,24 +239977,31 @@ | |
| 239977 | */ |
| 239978 | struct Fts5ExprPhrase { |
| 239979 | Fts5ExprNode *pNode; /* FTS5_STRING node this phrase is part of */ |
| 239980 | Fts5Buffer poslist; /* Current position list */ |
| 239981 | int nTerm; /* Number of entries in aTerm[] */ |
| 239982 | Fts5ExprTerm aTerm[FLEXARRAY]; /* Terms that make up this phrase */ |
| 239983 | }; |
| 239984 | |
| 239985 | /* Size (in bytes) of an Fts5ExprPhrase object that holds up to N terms */ |
| 239986 | #define SZ_FTS5EXPRPHRASE(N) \ |
| 239987 | (offsetof(Fts5ExprPhrase,aTerm) + (N)*sizeof(Fts5ExprTerm)) |
| 239988 | |
| 239989 | /* |
| 239990 | ** One or more phrases that must appear within a certain token distance of |
| 239991 | ** each other within each matching document. |
| 239992 | */ |
| 239993 | struct Fts5ExprNearset { |
| 239994 | int nNear; /* NEAR parameter */ |
| 239995 | Fts5Colset *pColset; /* Columns to search (NULL -> all columns) */ |
| 239996 | int nPhrase; /* Number of entries in aPhrase[] array */ |
| 239997 | Fts5ExprPhrase *apPhrase[FLEXARRAY]; /* Array of phrase pointers */ |
| 239998 | }; |
| 239999 | |
| 240000 | /* Size (in bytes) of an Fts5ExprNearset object covering up to N phrases */ |
| 240001 | #define SZ_FTS5EXPRNEARSET(N) \ |
| 240002 | (offsetof(Fts5ExprNearset,apPhrase)+(N)*sizeof(Fts5ExprPhrase*)) |
| 240003 | |
| 240004 | /* |
| 240005 | ** Parse context. |
| 240006 | */ |
| 240007 | struct Fts5Parse { |
| @@ -239976,11 +240157,11 @@ | |
| 240157 | assert_expr_depth_ok(sParse.rc, sParse.pExpr); |
| 240158 | |
| 240159 | /* If the LHS of the MATCH expression was a user column, apply the |
| 240160 | ** implicit column-filter. */ |
| 240161 | if( sParse.rc==SQLITE_OK && iCol<pConfig->nCol ){ |
| 240162 | int n = SZ_FTS5COLSET(1); |
| 240163 | Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&sParse.rc, n); |
| 240164 | if( pColset ){ |
| 240165 | pColset->nCol = 1; |
| 240166 | pColset->aiCol[0] = iCol; |
| 240167 | sqlite3Fts5ParseSetColset(&sParse, sParse.pExpr, pColset); |
| @@ -241334,11 +241515,11 @@ | |
| 241515 | Fts5ExprNearset *pRet = 0; |
| 241516 | |
| 241517 | if( pParse->rc==SQLITE_OK ){ |
| 241518 | if( pNear==0 ){ |
| 241519 | sqlite3_int64 nByte; |
| 241520 | nByte = SZ_FTS5EXPRNEARSET(SZALLOC+1); |
| 241521 | pRet = sqlite3_malloc64(nByte); |
| 241522 | if( pRet==0 ){ |
| 241523 | pParse->rc = SQLITE_NOMEM; |
| 241524 | }else{ |
| 241525 | memset(pRet, 0, (size_t)nByte); |
| @@ -241345,11 +241526,11 @@ | |
| 241526 | } |
| 241527 | }else if( (pNear->nPhrase % SZALLOC)==0 ){ |
| 241528 | int nNew = pNear->nPhrase + SZALLOC; |
| 241529 | sqlite3_int64 nByte; |
| 241530 | |
| 241531 | nByte = SZ_FTS5EXPRNEARSET(nNew+1); |
| 241532 | pRet = (Fts5ExprNearset*)sqlite3_realloc64(pNear, nByte); |
| 241533 | if( pRet==0 ){ |
| 241534 | pParse->rc = SQLITE_NOMEM; |
| 241535 | } |
| 241536 | }else{ |
| @@ -241436,16 +241617,16 @@ | |
| 241617 | if( pPhrase==0 || (pPhrase->nTerm % SZALLOC)==0 ){ |
| 241618 | Fts5ExprPhrase *pNew; |
| 241619 | int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0); |
| 241620 | |
| 241621 | pNew = (Fts5ExprPhrase*)sqlite3_realloc64(pPhrase, |
| 241622 | SZ_FTS5EXPRPHRASE(nNew+1) |
| 241623 | ); |
| 241624 | if( pNew==0 ){ |
| 241625 | rc = SQLITE_NOMEM; |
| 241626 | }else{ |
| 241627 | if( pPhrase==0 ) memset(pNew, 0, SZ_FTS5EXPRPHRASE(1)); |
| 241628 | pCtx->pPhrase = pPhrase = pNew; |
| 241629 | pNew->nTerm = nNew - SZALLOC; |
| 241630 | } |
| 241631 | } |
| 241632 | |
| @@ -241549,11 +241730,11 @@ | |
| 241730 | } |
| 241731 | |
| 241732 | if( sCtx.pPhrase==0 ){ |
| 241733 | /* This happens when parsing a token or quoted phrase that contains |
| 241734 | ** no token characters at all. (e.g ... MATCH '""'). */ |
| 241735 | sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, SZ_FTS5EXPRPHRASE(1)); |
| 241736 | }else if( sCtx.pPhrase->nTerm ){ |
| 241737 | sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = (u8)bPrefix; |
| 241738 | } |
| 241739 | assert( pParse->apPhrase!=0 ); |
| 241740 | pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase; |
| @@ -241584,23 +241765,22 @@ | |
| 241765 | if( rc==SQLITE_OK ){ |
| 241766 | pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc, |
| 241767 | sizeof(Fts5ExprPhrase*)); |
| 241768 | } |
| 241769 | if( rc==SQLITE_OK ){ |
| 241770 | pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRNODE(1)); |
| 241771 | } |
| 241772 | if( rc==SQLITE_OK ){ |
| 241773 | pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc, |
| 241774 | SZ_FTS5EXPRNEARSET(2)); |
| 241775 | } |
| 241776 | if( rc==SQLITE_OK && ALWAYS(pOrig!=0) ){ |
| 241777 | Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset; |
| 241778 | if( pColsetOrig ){ |
| 241779 | sqlite3_int64 nByte; |
| 241780 | Fts5Colset *pColset; |
| 241781 | nByte = SZ_FTS5COLSET(pColsetOrig->nCol); |
| 241782 | pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte); |
| 241783 | if( pColset ){ |
| 241784 | memcpy(pColset, pColsetOrig, (size_t)nByte); |
| 241785 | } |
| 241786 | pNew->pRoot->pNear->pColset = pColset; |
| @@ -241624,11 +241804,11 @@ | |
| 241804 | } |
| 241805 | } |
| 241806 | }else{ |
| 241807 | /* This happens when parsing a token or quoted phrase that contains |
| 241808 | ** no token characters at all. (e.g ... MATCH '""'). */ |
| 241809 | sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRPHRASE(1)); |
| 241810 | } |
| 241811 | } |
| 241812 | |
| 241813 | if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){ |
| 241814 | /* All the allocations succeeded. Put the expression object together. */ |
| @@ -241718,11 +241898,11 @@ | |
| 241898 | Fts5Colset *pNew; /* New colset object to return */ |
| 241899 | |
| 241900 | assert( pParse->rc==SQLITE_OK ); |
| 241901 | assert( iCol>=0 && iCol<pParse->pConfig->nCol ); |
| 241902 | |
| 241903 | pNew = sqlite3_realloc64(p, SZ_FTS5COLSET(nCol+1)); |
| 241904 | if( pNew==0 ){ |
| 241905 | pParse->rc = SQLITE_NOMEM; |
| 241906 | }else{ |
| 241907 | int *aiCol = pNew->aiCol; |
| 241908 | int i, j; |
| @@ -241753,11 +241933,11 @@ | |
| 241933 | static Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse *pParse, Fts5Colset *p){ |
| 241934 | Fts5Colset *pRet; |
| 241935 | int nCol = pParse->pConfig->nCol; |
| 241936 | |
| 241937 | pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc, |
| 241938 | SZ_FTS5COLSET(nCol+1) |
| 241939 | ); |
| 241940 | if( pRet ){ |
| 241941 | int i; |
| 241942 | int iOld = 0; |
| 241943 | for(i=0; i<nCol; i++){ |
| @@ -241814,11 +241994,11 @@ | |
| 241994 | ** fails, (*pRc) is set to SQLITE_NOMEM and NULL is returned. |
| 241995 | */ |
| 241996 | static Fts5Colset *fts5CloneColset(int *pRc, Fts5Colset *pOrig){ |
| 241997 | Fts5Colset *pRet; |
| 241998 | if( pOrig ){ |
| 241999 | sqlite3_int64 nByte = SZ_FTS5COLSET(pOrig->nCol); |
| 242000 | pRet = (Fts5Colset*)sqlite3Fts5MallocZero(pRc, nByte); |
| 242001 | if( pRet ){ |
| 242002 | memcpy(pRet, pOrig, (size_t)nByte); |
| 242003 | } |
| 242004 | }else{ |
| @@ -241982,21 +242162,21 @@ | |
| 242162 | Fts5ExprNode *pRet; |
| 242163 | |
| 242164 | assert( pNear->nPhrase==1 ); |
| 242165 | assert( pParse->bPhraseToAnd ); |
| 242166 | |
| 242167 | nByte = SZ_FTS5EXPRNODE(nTerm+1); |
| 242168 | pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); |
| 242169 | if( pRet ){ |
| 242170 | pRet->eType = FTS5_AND; |
| 242171 | pRet->nChild = nTerm; |
| 242172 | pRet->iHeight = 1; |
| 242173 | fts5ExprAssignXNext(pRet); |
| 242174 | pParse->nPhrase--; |
| 242175 | for(ii=0; ii<nTerm; ii++){ |
| 242176 | Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero( |
| 242177 | &pParse->rc, SZ_FTS5EXPRPHRASE(1) |
| 242178 | ); |
| 242179 | if( pPhrase ){ |
| 242180 | if( parseGrowPhraseArray(pParse) ){ |
| 242181 | fts5ExprPhraseFree(pPhrase); |
| 242182 | }else{ |
| @@ -242061,11 +242241,11 @@ | |
| 242241 | nChild = 2; |
| 242242 | if( pLeft->eType==eType ) nChild += pLeft->nChild-1; |
| 242243 | if( pRight->eType==eType ) nChild += pRight->nChild-1; |
| 242244 | } |
| 242245 | |
| 242246 | nByte = SZ_FTS5EXPRNODE(nChild); |
| 242247 | pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); |
| 242248 | |
| 242249 | if( pRet ){ |
| 242250 | pRet->eType = eType; |
| 242251 | pRet->pNear = pNear; |
| @@ -242936,11 +243116,11 @@ | |
| 243116 | } |
| 243117 | return rc; |
| 243118 | } |
| 243119 | |
| 243120 | /* |
| 243121 | ** Clear the token mappings for all Fts5IndexIter objects managed by |
| 243122 | ** the expression passed as the only argument. |
| 243123 | */ |
| 243124 | static void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){ |
| 243125 | int ii; |
| 243126 | for(ii=0; ii<pExpr->nPhrase; ii++){ |
| @@ -242971,11 +243151,11 @@ | |
| 243151 | |
| 243152 | typedef struct Fts5HashEntry Fts5HashEntry; |
| 243153 | |
| 243154 | /* |
| 243155 | ** This file contains the implementation of an in-memory hash table used |
| 243156 | ** to accumulate "term -> doclist" content before it is flushed to a level-0 |
| 243157 | ** segment. |
| 243158 | */ |
| 243159 | |
| 243160 | |
| 243161 | struct Fts5Hash { |
| @@ -243028,11 +243208,11 @@ | |
| 243208 | int iPos; /* Position of last value written */ |
| 243209 | i64 iRowid; /* Rowid of last value written */ |
| 243210 | }; |
| 243211 | |
| 243212 | /* |
| 243213 | ** Equivalent to: |
| 243214 | ** |
| 243215 | ** char *fts5EntryKey(Fts5HashEntry *pEntry){ return zKey; } |
| 243216 | */ |
| 243217 | #define fts5EntryKey(p) ( ((char *)(&(p)[1])) ) |
| 243218 | |
| @@ -243964,13 +244144,17 @@ | |
| 244144 | int nRef; /* Object reference count */ |
| 244145 | u64 nWriteCounter; /* Total leaves written to level 0 */ |
| 244146 | u64 nOriginCntr; /* Origin value for next top-level segment */ |
| 244147 | int nSegment; /* Total segments in this structure */ |
| 244148 | int nLevel; /* Number of levels in this index */ |
| 244149 | Fts5StructureLevel aLevel[FLEXARRAY]; /* Array of nLevel level objects */ |
| 244150 | }; |
| 244151 | |
| 244152 | /* Size (in bytes) of an Fts5Structure object holding up to N levels */ |
| 244153 | #define SZ_FTS5STRUCTURE(N) \ |
| 244154 | (offsetof(Fts5Structure,aLevel) + (N)*sizeof(Fts5StructureLevel)) |
| 244155 | |
| 244156 | /* |
| 244157 | ** An object of type Fts5SegWriter is used to write to segments. |
| 244158 | */ |
| 244159 | struct Fts5PageWriter { |
| 244160 | int pgno; /* Page number for this page */ |
| @@ -244096,14 +244280,18 @@ | |
| 244280 | |
| 244281 | /* |
| 244282 | ** Array of tombstone pages. Reference counted. |
| 244283 | */ |
| 244284 | struct Fts5TombstoneArray { |
| 244285 | int nRef; /* Number of pointers to this object */ |
| 244286 | int nTombstone; |
| 244287 | Fts5Data *apTombstone[FLEXARRAY]; /* Array of tombstone pages */ |
| 244288 | }; |
| 244289 | |
| 244290 | /* Size (in bytes) of an Fts5TombstoneArray holding up to N tombstones */ |
| 244291 | #define SZ_FTS5TOMBSTONEARRAY(N) \ |
| 244292 | (offsetof(Fts5TombstoneArray,apTombstone)+(N)*sizeof(Fts5Data*)) |
| 244293 | |
| 244294 | /* |
| 244295 | ** Argument is a pointer to an Fts5Data structure that contains a |
| 244296 | ** leaf page. |
| 244297 | */ |
| @@ -244169,12 +244357,15 @@ | |
| 244357 | int bRev; /* True to iterate in reverse order */ |
| 244358 | u8 bSkipEmpty; /* True to skip deleted entries */ |
| 244359 | |
| 244360 | i64 iSwitchRowid; /* Firstest rowid of other than aFirst[1] */ |
| 244361 | Fts5CResult *aFirst; /* Current merge state (see above) */ |
| 244362 | Fts5SegIter aSeg[FLEXARRAY]; /* Array of segment iterators */ |
| 244363 | }; |
| 244364 | |
| 244365 | /* Size (in bytes) of an Fts5Iter object holding up to N segment iterators */ |
| 244366 | #define SZ_FTS5ITER(N) (offsetof(Fts5Iter,aSeg)+(N)*sizeof(Fts5SegIter)) |
| 244367 | |
| 244368 | /* |
| 244369 | ** An instance of the following type is used to iterate through the contents |
| 244370 | ** of a doclist-index record. |
| 244371 | ** |
| @@ -244198,12 +244389,16 @@ | |
| 244389 | i64 iRowid; /* First rowid on leaf iLeafPgno */ |
| 244390 | }; |
| 244391 | struct Fts5DlidxIter { |
| 244392 | int nLvl; |
| 244393 | int iSegid; |
| 244394 | Fts5DlidxLvl aLvl[FLEXARRAY]; |
| 244395 | }; |
| 244396 | |
| 244397 | /* Size (in bytes) of an Fts5DlidxIter object with up to N levels */ |
| 244398 | #define SZ_FTS5DLIDXITER(N) \ |
| 244399 | (offsetof(Fts5DlidxIter,aLvl)+(N)*sizeof(Fts5DlidxLvl)) |
| 244400 | |
| 244401 | static void fts5PutU16(u8 *aOut, u16 iVal){ |
| 244402 | aOut[0] = (iVal>>8); |
| 244403 | aOut[1] = (iVal&0xFF); |
| 244404 | } |
| @@ -244568,11 +244763,11 @@ | |
| 244763 | ** an error occurs, (*pRc) is set to an SQLite error code before returning. |
| 244764 | */ |
| 244765 | static void fts5StructureMakeWritable(int *pRc, Fts5Structure **pp){ |
| 244766 | Fts5Structure *p = *pp; |
| 244767 | if( *pRc==SQLITE_OK && p->nRef>1 ){ |
| 244768 | i64 nByte = SZ_FTS5STRUCTURE(p->nLevel); |
| 244769 | Fts5Structure *pNew; |
| 244770 | pNew = (Fts5Structure*)sqlite3Fts5MallocZero(pRc, nByte); |
| 244771 | if( pNew ){ |
| 244772 | int i; |
| 244773 | memcpy(pNew, p, nByte); |
| @@ -244642,14 +244837,11 @@ | |
| 244837 | if( nLevel>FTS5_MAX_SEGMENT || nLevel<0 |
| 244838 | || nSegment>FTS5_MAX_SEGMENT || nSegment<0 |
| 244839 | ){ |
| 244840 | return FTS5_CORRUPT; |
| 244841 | } |
| 244842 | nByte = SZ_FTS5STRUCTURE(nLevel); |
| 244843 | pRet = (Fts5Structure*)sqlite3Fts5MallocZero(&rc, nByte); |
| 244844 | |
| 244845 | if( pRet ){ |
| 244846 | pRet->nRef = 1; |
| 244847 | pRet->nLevel = nLevel; |
| @@ -244725,14 +244917,11 @@ | |
| 244917 | fts5StructureMakeWritable(pRc, ppStruct); |
| 244918 | assert( (ppStruct!=0 && (*ppStruct)!=0) || (*pRc)!=SQLITE_OK ); |
| 244919 | if( *pRc==SQLITE_OK ){ |
| 244920 | Fts5Structure *pStruct = *ppStruct; |
| 244921 | int nLevel = pStruct->nLevel; |
| 244922 | sqlite3_int64 nByte = SZ_FTS5STRUCTURE(nLevel+2); |
| 244923 | |
| 244924 | pStruct = sqlite3_realloc64(pStruct, nByte); |
| 244925 | if( pStruct ){ |
| 244926 | memset(&pStruct->aLevel[nLevel], 0, sizeof(Fts5StructureLevel)); |
| 244927 | pStruct->nLevel++; |
| @@ -245267,11 +245456,11 @@ | |
| 245456 | Fts5DlidxIter *pIter = 0; |
| 245457 | int i; |
| 245458 | int bDone = 0; |
| 245459 | |
| 245460 | for(i=0; p->rc==SQLITE_OK && bDone==0; i++){ |
| 245461 | sqlite3_int64 nByte = SZ_FTS5DLIDXITER(i+1); |
| 245462 | Fts5DlidxIter *pNew; |
| 245463 | |
| 245464 | pNew = (Fts5DlidxIter*)sqlite3_realloc64(pIter, nByte); |
| 245465 | if( pNew==0 ){ |
| 245466 | p->rc = SQLITE_NOMEM; |
| @@ -245485,11 +245674,11 @@ | |
| 245674 | ** leave an error in the Fts5Index object. |
| 245675 | */ |
| 245676 | static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){ |
| 245677 | const int nTomb = pIter->pSeg->nPgTombstone; |
| 245678 | if( nTomb>0 ){ |
| 245679 | int nByte = SZ_FTS5TOMBSTONEARRAY(nTomb+1); |
| 245680 | Fts5TombstoneArray *pNew; |
| 245681 | pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte); |
| 245682 | if( pNew ){ |
| 245683 | pNew->nTombstone = nTomb; |
| 245684 | pNew->nRef = 1; |
| @@ -246946,12 +247135,11 @@ | |
| 247135 | Fts5Iter *pNew; |
| 247136 | i64 nSlot; /* Power of two >= nSeg */ |
| 247137 | |
| 247138 | for(nSlot=2; nSlot<nSeg; nSlot=nSlot*2); |
| 247139 | pNew = fts5IdxMalloc(p, |
| 247140 | SZ_FTS5ITER(nSlot) + /* pNew + pNew->aSeg[] */ |
| 247141 | sizeof(Fts5CResult) * nSlot /* pNew->aFirst[] */ |
| 247142 | ); |
| 247143 | if( pNew ){ |
| 247144 | pNew->nSeg = nSlot; |
| 247145 | pNew->aFirst = (Fts5CResult*)&pNew->aSeg[nSlot]; |
| @@ -249313,11 +249501,11 @@ | |
| 249501 | static Fts5Structure *fts5IndexOptimizeStruct( |
| 249502 | Fts5Index *p, |
| 249503 | Fts5Structure *pStruct |
| 249504 | ){ |
| 249505 | Fts5Structure *pNew = 0; |
| 249506 | sqlite3_int64 nByte = SZ_FTS5STRUCTURE(1); |
| 249507 | int nSeg = pStruct->nSegment; |
| 249508 | int i; |
| 249509 | |
| 249510 | /* Figure out if this structure requires optimization. A structure does |
| 249511 | ** not require optimization if either: |
| @@ -249343,10 +249531,11 @@ | |
| 249531 | } |
| 249532 | assert( pStruct->aLevel[i].nMerge<=nThis ); |
| 249533 | } |
| 249534 | |
| 249535 | nByte += (((i64)pStruct->nLevel)+1) * sizeof(Fts5StructureLevel); |
| 249536 | assert( nByte==SZ_FTS5STRUCTURE(pStruct->nLevel+2) ); |
| 249537 | pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte); |
| 249538 | |
| 249539 | if( pNew ){ |
| 249540 | Fts5StructureLevel *pLvl; |
| 249541 | nByte = nSeg * sizeof(Fts5StructureSegment); |
| @@ -249919,12 +250108,16 @@ | |
| 250108 | /* The following are used for other full-token tokendata queries only. */ |
| 250109 | int nIter; |
| 250110 | int nIterAlloc; |
| 250111 | Fts5PoslistReader *aPoslistReader; |
| 250112 | int *aPoslistToIter; |
| 250113 | Fts5Iter *apIter[FLEXARRAY]; |
| 250114 | }; |
| 250115 | |
| 250116 | /* Size in bytes of an Fts5TokenDataIter object holding up to N iterators */ |
| 250117 | #define SZ_FTS5TOKENDATAITER(N) \ |
| 250118 | (offsetof(Fts5TokenDataIter,apIter) + (N)*sizeof(Fts5Iter)) |
| 250119 | |
| 250120 | /* |
| 250121 | ** The two input arrays - a1[] and a2[] - are in sorted order. This function |
| 250122 | ** merges the two arrays together and writes the result to output array |
| 250123 | ** aOut[]. aOut[] is guaranteed to be large enough to hold the result. |
| @@ -249993,11 +250186,11 @@ | |
| 250186 | } |
| 250187 | |
| 250188 | /* |
| 250189 | ** Sort the contents of the pT->aMap[] array. |
| 250190 | ** |
| 250191 | ** The sorting algorithm requires a malloc(). If this fails, an error code |
| 250192 | ** is left in Fts5Index.rc before returning. |
| 250193 | */ |
| 250194 | static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){ |
| 250195 | Fts5TokenDataMap *aTmp = 0; |
| 250196 | int nByte = pT->nMap * sizeof(Fts5TokenDataMap); |
| @@ -250184,11 +250377,11 @@ | |
| 250377 | if( iIdx==0 |
| 250378 | && p->pConfig->eDetail==FTS5_DETAIL_FULL |
| 250379 | && p->pConfig->bPrefixInsttoken |
| 250380 | ){ |
| 250381 | s.pTokendata = &s2; |
| 250382 | s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, SZ_FTS5TOKENDATAITER(1)); |
| 250383 | } |
| 250384 | |
| 250385 | if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){ |
| 250386 | s.xMerge = fts5MergeRowidLists; |
| 250387 | s.xAppend = fts5AppendRowid; |
| @@ -250312,19 +250505,21 @@ | |
| 250505 | ** The %_data table is completely empty when this function is called. This |
| 250506 | ** function populates it with the initial structure objects for each index, |
| 250507 | ** and the initial version of the "averages" record (a zero-byte blob). |
| 250508 | */ |
| 250509 | static int sqlite3Fts5IndexReinit(Fts5Index *p){ |
| 250510 | Fts5Structure *pTmp; |
| 250511 | u8 tmpSpace[SZ_FTS5STRUCTURE(1)]; |
| 250512 | fts5StructureInvalidate(p); |
| 250513 | fts5IndexDiscardData(p); |
| 250514 | pTmp = (Fts5Structure*)tmpSpace; |
| 250515 | memset(pTmp, 0, SZ_FTS5STRUCTURE(1)); |
| 250516 | if( p->pConfig->bContentlessDelete ){ |
| 250517 | pTmp->nOriginCntr = 1; |
| 250518 | } |
| 250519 | fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0); |
| 250520 | fts5StructureWrite(p, pTmp); |
| 250521 | return fts5IndexReturn(p); |
| 250522 | } |
| 250523 | |
| 250524 | /* |
| 250525 | ** Open a new Fts5Index handle. If the bCreate argument is true, create |
| @@ -250528,11 +250723,11 @@ | |
| 250723 | Fts5TokenDataIter *pRet = pIn; |
| 250724 | |
| 250725 | if( p->rc==SQLITE_OK ){ |
| 250726 | if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){ |
| 250727 | int nAlloc = pIn ? pIn->nIterAlloc*2 : 16; |
| 250728 | int nByte = SZ_FTS5TOKENDATAITER(nAlloc+1); |
| 250729 | Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte); |
| 250730 | |
| 250731 | if( pNew==0 ){ |
| 250732 | p->rc = SQLITE_NOMEM; |
| 250733 | }else{ |
| @@ -251044,11 +251239,12 @@ | |
| 251239 | |
| 251240 | memset(&ctx, 0, sizeof(ctx)); |
| 251241 | |
| 251242 | fts5BufferGrow(&p->rc, &token, nToken+1); |
| 251243 | assert( token.p!=0 || p->rc!=SQLITE_OK ); |
| 251244 | ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, |
| 251245 | SZ_FTS5TOKENDATAITER(1)); |
| 251246 | |
| 251247 | if( p->rc==SQLITE_OK ){ |
| 251248 | |
| 251249 | /* Fill in the token prefix to search for */ |
| 251250 | token.p[0] = FTS5_MAIN_PREFIX; |
| @@ -251175,11 +251371,12 @@ | |
| 251371 | assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL ); |
| 251372 | assert( pIter->pTokenDataIter || pIter->nSeg>0 ); |
| 251373 | if( pIter->nSeg>0 ){ |
| 251374 | /* This is a prefix term iterator. */ |
| 251375 | if( pT==0 ){ |
| 251376 | pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, |
| 251377 | SZ_FTS5TOKENDATAITER(1)); |
| 251378 | pIter->pTokenDataIter = pT; |
| 251379 | } |
| 251380 | if( pT ){ |
| 251381 | fts5TokendataIterAppendMap(p, pT, pT->terms.n, nToken, iRowid, iPos); |
| 251382 | fts5BufferAppendBlob(&p->rc, &pT->terms, nToken, (const u8*)pToken); |
| @@ -252209,11 +252406,11 @@ | |
| 252406 | } |
| 252407 | #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ |
| 252408 | |
| 252409 | #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) |
| 252410 | static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){ |
| 252411 | int iSegid, iHeight, iPgno, bDlidx, bTomb; /* Rowid components */ |
| 252412 | fts5DecodeRowid(iKey, &bTomb, &iSegid, &bDlidx, &iHeight, &iPgno); |
| 252413 | |
| 252414 | if( iSegid==0 ){ |
| 252415 | if( iKey==FTS5_AVERAGES_ROWID ){ |
| 252416 | sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{averages} "); |
| @@ -253170,13 +253367,15 @@ | |
| 253367 | struct Fts5Sorter { |
| 253368 | sqlite3_stmt *pStmt; |
| 253369 | i64 iRowid; /* Current rowid */ |
| 253370 | const u8 *aPoslist; /* Position lists for current row */ |
| 253371 | int nIdx; /* Number of entries in aIdx[] */ |
| 253372 | int aIdx[FLEXARRAY]; /* Offsets into aPoslist for current row */ |
| 253373 | }; |
| 253374 | |
| 253375 | /* Size (int bytes) of an Fts5Sorter object with N indexes */ |
| 253376 | #define SZ_FTS5SORTER(N) (offsetof(Fts5Sorter,nIdx)+((N+2)/2)*sizeof(i64)) |
| 253377 | |
| 253378 | /* |
| 253379 | ** Virtual-table cursor object. |
| 253380 | ** |
| 253381 | ** iSpecial: |
| @@ -254050,11 +254249,11 @@ | |
| 254249 | int rc; |
| 254250 | const char *zRank = pCsr->zRank; |
| 254251 | const char *zRankArgs = pCsr->zRankArgs; |
| 254252 | |
| 254253 | nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); |
| 254254 | nByte = SZ_FTS5SORTER(nPhrase); |
| 254255 | pSorter = (Fts5Sorter*)sqlite3_malloc64(nByte); |
| 254256 | if( pSorter==0 ) return SQLITE_NOMEM; |
| 254257 | memset(pSorter, 0, (size_t)nByte); |
| 254258 | pSorter->nIdx = nPhrase; |
| 254259 | |
| @@ -256576,11 +256775,11 @@ | |
| 256775 | int nArg, /* Number of args */ |
| 256776 | sqlite3_value **apUnused /* Function arguments */ |
| 256777 | ){ |
| 256778 | assert( nArg==0 ); |
| 256779 | UNUSED_PARAM2(nArg, apUnused); |
| 256780 | sqlite3_result_text(pCtx, "fts5: 2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185", -1, SQLITE_TRANSIENT); |
| 256781 | } |
| 256782 | |
| 256783 | /* |
| 256784 | ** Implementation of fts5_locale(LOCALE, TEXT) function. |
| 256785 | ** |
| 256786 |
+4
-2
| --- extsrc/sqlite3.h | ||
| +++ extsrc/sqlite3.h | ||
| @@ -146,11 +146,11 @@ | ||
| 146 | 146 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 147 | 147 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 148 | 148 | */ |
| 149 | 149 | #define SQLITE_VERSION "3.50.0" |
| 150 | 150 | #define SQLITE_VERSION_NUMBER 3050000 |
| 151 | -#define SQLITE_SOURCE_ID "2025-02-25 18:10:47 e6784af6d50f715338ae3218fc8ba1b894883c27d797f0b7fd2625cac17d9cd7" | |
| 151 | +#define SQLITE_SOURCE_ID "2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185" | |
| 152 | 152 | |
| 153 | 153 | /* |
| 154 | 154 | ** CAPI3REF: Run-Time Library Version Numbers |
| 155 | 155 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 156 | 156 | ** |
| @@ -5173,11 +5173,11 @@ | ||
| 5173 | 5173 | ** For all versions of SQLite up to and including 3.6.23.1, a call to |
| 5174 | 5174 | ** [sqlite3_reset()] was required after sqlite3_step() returned anything |
| 5175 | 5175 | ** other than [SQLITE_ROW] before any subsequent invocation of |
| 5176 | 5176 | ** sqlite3_step(). Failure to reset the prepared statement using |
| 5177 | 5177 | ** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from |
| 5178 | -** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1], | |
| 5178 | +** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]), | |
| 5179 | 5179 | ** sqlite3_step() began |
| 5180 | 5180 | ** calling [sqlite3_reset()] automatically in this circumstance rather |
| 5181 | 5181 | ** than returning [SQLITE_MISUSE]. This is not considered a compatibility |
| 5182 | 5182 | ** break because any application that ever receives an SQLITE_MISUSE error |
| 5183 | 5183 | ** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option |
| @@ -7069,10 +7069,12 @@ | ||
| 7069 | 7069 | ** ^Any callback set by a previous call to this function |
| 7070 | 7070 | ** for the same database connection is overridden. |
| 7071 | 7071 | ** |
| 7072 | 7072 | ** ^The second argument is a pointer to the function to invoke when a |
| 7073 | 7073 | ** row is updated, inserted or deleted in a rowid table. |
| 7074 | +** ^The update hook is disabled by invoking sqlite3_update_hook() | |
| 7075 | +** with a NULL pointer as the second parameter. | |
| 7074 | 7076 | ** ^The first argument to the callback is a copy of the third argument |
| 7075 | 7077 | ** to sqlite3_update_hook(). |
| 7076 | 7078 | ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], |
| 7077 | 7079 | ** or [SQLITE_UPDATE], depending on the operation that caused the callback |
| 7078 | 7080 | ** to be invoked. |
| 7079 | 7081 |
| --- extsrc/sqlite3.h | |
| +++ extsrc/sqlite3.h | |
| @@ -146,11 +146,11 @@ | |
| 146 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 147 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 148 | */ |
| 149 | #define SQLITE_VERSION "3.50.0" |
| 150 | #define SQLITE_VERSION_NUMBER 3050000 |
| 151 | #define SQLITE_SOURCE_ID "2025-02-25 18:10:47 e6784af6d50f715338ae3218fc8ba1b894883c27d797f0b7fd2625cac17d9cd7" |
| 152 | |
| 153 | /* |
| 154 | ** CAPI3REF: Run-Time Library Version Numbers |
| 155 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 156 | ** |
| @@ -5173,11 +5173,11 @@ | |
| 5173 | ** For all versions of SQLite up to and including 3.6.23.1, a call to |
| 5174 | ** [sqlite3_reset()] was required after sqlite3_step() returned anything |
| 5175 | ** other than [SQLITE_ROW] before any subsequent invocation of |
| 5176 | ** sqlite3_step(). Failure to reset the prepared statement using |
| 5177 | ** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from |
| 5178 | ** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1], |
| 5179 | ** sqlite3_step() began |
| 5180 | ** calling [sqlite3_reset()] automatically in this circumstance rather |
| 5181 | ** than returning [SQLITE_MISUSE]. This is not considered a compatibility |
| 5182 | ** break because any application that ever receives an SQLITE_MISUSE error |
| 5183 | ** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option |
| @@ -7069,10 +7069,12 @@ | |
| 7069 | ** ^Any callback set by a previous call to this function |
| 7070 | ** for the same database connection is overridden. |
| 7071 | ** |
| 7072 | ** ^The second argument is a pointer to the function to invoke when a |
| 7073 | ** row is updated, inserted or deleted in a rowid table. |
| 7074 | ** ^The first argument to the callback is a copy of the third argument |
| 7075 | ** to sqlite3_update_hook(). |
| 7076 | ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], |
| 7077 | ** or [SQLITE_UPDATE], depending on the operation that caused the callback |
| 7078 | ** to be invoked. |
| 7079 |
| --- extsrc/sqlite3.h | |
| +++ extsrc/sqlite3.h | |
| @@ -146,11 +146,11 @@ | |
| 146 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 147 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 148 | */ |
| 149 | #define SQLITE_VERSION "3.50.0" |
| 150 | #define SQLITE_VERSION_NUMBER 3050000 |
| 151 | #define SQLITE_SOURCE_ID "2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185" |
| 152 | |
| 153 | /* |
| 154 | ** CAPI3REF: Run-Time Library Version Numbers |
| 155 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 156 | ** |
| @@ -5173,11 +5173,11 @@ | |
| 5173 | ** For all versions of SQLite up to and including 3.6.23.1, a call to |
| 5174 | ** [sqlite3_reset()] was required after sqlite3_step() returned anything |
| 5175 | ** other than [SQLITE_ROW] before any subsequent invocation of |
| 5176 | ** sqlite3_step(). Failure to reset the prepared statement using |
| 5177 | ** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from |
| 5178 | ** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]), |
| 5179 | ** sqlite3_step() began |
| 5180 | ** calling [sqlite3_reset()] automatically in this circumstance rather |
| 5181 | ** than returning [SQLITE_MISUSE]. This is not considered a compatibility |
| 5182 | ** break because any application that ever receives an SQLITE_MISUSE error |
| 5183 | ** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option |
| @@ -7069,10 +7069,12 @@ | |
| 7069 | ** ^Any callback set by a previous call to this function |
| 7070 | ** for the same database connection is overridden. |
| 7071 | ** |
| 7072 | ** ^The second argument is a pointer to the function to invoke when a |
| 7073 | ** row is updated, inserted or deleted in a rowid table. |
| 7074 | ** ^The update hook is disabled by invoking sqlite3_update_hook() |
| 7075 | ** with a NULL pointer as the second parameter. |
| 7076 | ** ^The first argument to the callback is a copy of the third argument |
| 7077 | ** to sqlite3_update_hook(). |
| 7078 | ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], |
| 7079 | ** or [SQLITE_UPDATE], depending on the operation that caused the callback |
| 7080 | ** to be invoked. |
| 7081 |
+1
-1
| --- src/add.c | ||
| +++ src/add.c | ||
| @@ -78,11 +78,11 @@ | ||
| 78 | 78 | static int numManifests; |
| 79 | 79 | |
| 80 | 80 | if( cachedManifest == -1 ){ |
| 81 | 81 | int i; |
| 82 | 82 | Blob repo; |
| 83 | - cachedManifest = db_get_manifest_setting(); | |
| 83 | + cachedManifest = db_get_manifest_setting(0); | |
| 84 | 84 | numManifests = 0; |
| 85 | 85 | for(i=0; i<count(aManifestflags); i++){ |
| 86 | 86 | if( cachedManifest&aManifestflags[i].flg ) { |
| 87 | 87 | azManifests[numManifests++] = aManifestflags[i].fname; |
| 88 | 88 | } |
| 89 | 89 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -78,11 +78,11 @@ | |
| 78 | static int numManifests; |
| 79 | |
| 80 | if( cachedManifest == -1 ){ |
| 81 | int i; |
| 82 | Blob repo; |
| 83 | cachedManifest = db_get_manifest_setting(); |
| 84 | numManifests = 0; |
| 85 | for(i=0; i<count(aManifestflags); i++){ |
| 86 | if( cachedManifest&aManifestflags[i].flg ) { |
| 87 | azManifests[numManifests++] = aManifestflags[i].fname; |
| 88 | } |
| 89 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -78,11 +78,11 @@ | |
| 78 | static int numManifests; |
| 79 | |
| 80 | if( cachedManifest == -1 ){ |
| 81 | int i; |
| 82 | Blob repo; |
| 83 | cachedManifest = db_get_manifest_setting(0); |
| 84 | numManifests = 0; |
| 85 | for(i=0; i<count(aManifestflags); i++){ |
| 86 | if( cachedManifest&aManifestflags[i].flg ) { |
| 87 | azManifests[numManifests++] = aManifestflags[i].fname; |
| 88 | } |
| 89 |
+27
-6
| --- src/alerts.c | ||
| +++ src/alerts.c | ||
| @@ -51,10 +51,11 @@ | ||
| 51 | 51 | @ -- f - Forum posts |
| 52 | 52 | @ -- k - ** Special: Unsubscribed using /oneclickunsub |
| 53 | 53 | @ -- n - New forum threads |
| 54 | 54 | @ -- r - Replies to my own forum posts |
| 55 | 55 | @ -- t - Ticket changes |
| 56 | +@ -- u - Elevation of users' permissions (admins only) | |
| 56 | 57 | @ -- w - Wiki changes |
| 57 | 58 | @ -- x - Edits to forum posts |
| 58 | 59 | @ -- Probably different codes will be added in the future. In the future |
| 59 | 60 | @ -- we might also add a separate table that allows subscribing to email |
| 60 | 61 | @ -- notifications for specific branches or tags or tickets. |
| @@ -1133,11 +1134,11 @@ | ||
| 1133 | 1134 | ** designated host and port and all times. |
| 1134 | 1135 | */ |
| 1135 | 1136 | |
| 1136 | 1137 | |
| 1137 | 1138 | /* |
| 1138 | -** COMMAND: alerts* abbreviated-subcommands | |
| 1139 | +** COMMAND: alerts* abbrv-subcom | |
| 1139 | 1140 | ** |
| 1140 | 1141 | ** Usage: %fossil alerts SUBCOMMAND ARGS... |
| 1141 | 1142 | ** |
| 1142 | 1143 | ** Subcommands: |
| 1143 | 1144 | ** |
| @@ -1258,11 +1259,11 @@ | ||
| 1258 | 1259 | g.argc = 3; |
| 1259 | 1260 | } |
| 1260 | 1261 | pSetting = setting_info(&nSetting); |
| 1261 | 1262 | for(; nSetting>0; nSetting--, pSetting++ ){ |
| 1262 | 1263 | if( strncmp(pSetting->name,"email-",6)!=0 ) continue; |
| 1263 | - print_setting(pSetting, 0); | |
| 1264 | + print_setting(pSetting, 0, 0); | |
| 1264 | 1265 | } |
| 1265 | 1266 | }else |
| 1266 | 1267 | if( strncmp(zCmd, "status", nCmd)==0 ){ |
| 1267 | 1268 | Stmt q; |
| 1268 | 1269 | int iCutoff; |
| @@ -1273,11 +1274,11 @@ | ||
| 1273 | 1274 | verify_all_options(); |
| 1274 | 1275 | if( g.argc!=3 ) usage("status"); |
| 1275 | 1276 | pSetting = setting_info(&nSetting); |
| 1276 | 1277 | for(; nSetting>0; nSetting--, pSetting++ ){ |
| 1277 | 1278 | if( strncmp(pSetting->name,"email-",6)!=0 ) continue; |
| 1278 | - print_setting(pSetting, 0); | |
| 1279 | + print_setting(pSetting, 0, 0); | |
| 1279 | 1280 | } |
| 1280 | 1281 | n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentSep"); |
| 1281 | 1282 | fossil_print(zFmt/*works-like:"%s%d"*/, "pending-alerts", n); |
| 1282 | 1283 | n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentDigest"); |
| 1283 | 1284 | fossil_print(zFmt/*works-like:"%s%d"*/, "pending-digest-alerts", n); |
| @@ -1563,10 +1564,11 @@ | ||
| 1563 | 1564 | if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c'; |
| 1564 | 1565 | if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f'; |
| 1565 | 1566 | if( g.perm.RdForum && PB("sn") ) ssub[nsub++] = 'n'; |
| 1566 | 1567 | if( g.perm.RdForum && PB("sr") ) ssub[nsub++] = 'r'; |
| 1567 | 1568 | if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't'; |
| 1569 | + if( g.perm.Admin && PB("su") ) ssub[nsub++] = 'u'; | |
| 1568 | 1570 | if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w'; |
| 1569 | 1571 | if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x'; |
| 1570 | 1572 | ssub[nsub] = 0; |
| 1571 | 1573 | zCode = db_text(0, |
| 1572 | 1574 | "INSERT INTO subscriber(semail,suname," |
| @@ -1627,10 +1629,11 @@ | ||
| 1627 | 1629 | if( g.perm.Read ) cgi_set_parameter_nocopy("sc","1",1); |
| 1628 | 1630 | if( g.perm.RdForum ) cgi_set_parameter_nocopy("sf","1",1); |
| 1629 | 1631 | if( g.perm.RdForum ) cgi_set_parameter_nocopy("sn","1",1); |
| 1630 | 1632 | if( g.perm.RdForum ) cgi_set_parameter_nocopy("sr","1",1); |
| 1631 | 1633 | if( g.perm.RdTkt ) cgi_set_parameter_nocopy("st","1",1); |
| 1634 | + if( g.perm.Admin ) cgi_set_parameter_nocopy("su","1",1); | |
| 1632 | 1635 | if( g.perm.RdWiki ) cgi_set_parameter_nocopy("sw","1",1); |
| 1633 | 1636 | } |
| 1634 | 1637 | @ <p>To receive email notifications for changes to this |
| 1635 | 1638 | @ repository, fill out the form below and press the "Submit" button.</p> |
| 1636 | 1639 | form_begin(0, "%R/subscribe"); |
| @@ -1699,10 +1702,14 @@ | ||
| 1699 | 1702 | } |
| 1700 | 1703 | if( g.perm.RdWiki ){ |
| 1701 | 1704 | @ <label><input type="checkbox" name="sw" %s(PCK("sw"))> \ |
| 1702 | 1705 | @ Wiki</label><br> |
| 1703 | 1706 | } |
| 1707 | + if( g.perm.Admin ){ | |
| 1708 | + @ <label><input type="checkbox" name="su" %s(PCK("su"))> \ | |
| 1709 | + @ User permission elevation</label> | |
| 1710 | + } | |
| 1704 | 1711 | di = PB("di"); |
| 1705 | 1712 | @ </td></tr> |
| 1706 | 1713 | @ <tr> |
| 1707 | 1714 | @ <td class="form_label">Delivery:</td> |
| 1708 | 1715 | @ <td><select size="1" name="di"> |
| @@ -1820,11 +1827,11 @@ | ||
| 1820 | 1827 | ** verifying the email address. |
| 1821 | 1828 | */ |
| 1822 | 1829 | void alert_page(void){ |
| 1823 | 1830 | const char *zName = 0; /* Value of the name= query parameter */ |
| 1824 | 1831 | Stmt q; /* For querying the database */ |
| 1825 | - int sa, sc, sf, st, sw, sx; /* Types of notifications requested */ | |
| 1832 | + int sa, sc, sf, st, su, sw, sx; /* Types of notifications requested */ | |
| 1826 | 1833 | int sn, sr; |
| 1827 | 1834 | int sdigest = 0, sdonotcall = 0, sverified = 0; /* Other fields */ |
| 1828 | 1835 | int isLogin; /* True if logged in as an individual */ |
| 1829 | 1836 | const char *ssub = 0; /* Subscription flags */ |
| 1830 | 1837 | const char *semail = 0; /* Email address */ |
| @@ -1881,10 +1888,11 @@ | ||
| 1881 | 1888 | if( g.perm.Read && PB("sc") ) newSsub[nsub++] = 'c'; |
| 1882 | 1889 | if( g.perm.RdForum && PB("sf") ) newSsub[nsub++] = 'f'; |
| 1883 | 1890 | if( g.perm.RdForum && PB("sn") ) newSsub[nsub++] = 'n'; |
| 1884 | 1891 | if( g.perm.RdForum && PB("sr") ) newSsub[nsub++] = 'r'; |
| 1885 | 1892 | if( g.perm.RdTkt && PB("st") ) newSsub[nsub++] = 't'; |
| 1893 | + if( g.perm.Admin && PB("su") ) newSsub[nsub++] = 'u'; | |
| 1886 | 1894 | if( g.perm.RdWiki && PB("sw") ) newSsub[nsub++] = 'w'; |
| 1887 | 1895 | if( g.perm.RdForum && PB("sx") ) newSsub[nsub++] = 'x'; |
| 1888 | 1896 | newSsub[nsub] = 0; |
| 1889 | 1897 | ssub = newSsub; |
| 1890 | 1898 | blob_init(&update, "UPDATE subscriber SET", -1); |
| @@ -1979,10 +1987,11 @@ | ||
| 1979 | 1987 | sc = strchr(ssub,'c')!=0; |
| 1980 | 1988 | sf = strchr(ssub,'f')!=0; |
| 1981 | 1989 | sn = strchr(ssub,'n')!=0; |
| 1982 | 1990 | sr = strchr(ssub,'r')!=0; |
| 1983 | 1991 | st = strchr(ssub,'t')!=0; |
| 1992 | + su = strchr(ssub,'u')!=0; | |
| 1984 | 1993 | sw = strchr(ssub,'w')!=0; |
| 1985 | 1994 | sx = strchr(ssub,'x')!=0; |
| 1986 | 1995 | smip = db_column_text(&q, 5); |
| 1987 | 1996 | mtime = db_column_text(&q, 7); |
| 1988 | 1997 | sctime = db_column_text(&q, 8); |
| @@ -2097,11 +2106,19 @@ | ||
| 2097 | 2106 | @ <label><input type="checkbox" name="st" %s(st?"checked":"")>\ |
| 2098 | 2107 | @ Ticket changes</label><br> |
| 2099 | 2108 | } |
| 2100 | 2109 | if( g.perm.RdWiki ){ |
| 2101 | 2110 | @ <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\ |
| 2102 | - @ Wiki</label> | |
| 2111 | + @ Wiki</label><br> | |
| 2112 | + } | |
| 2113 | + if( g.perm.Admin ){ | |
| 2114 | + /* Corner-case bug: if an admin assigns 'u' to a non-admin, that | |
| 2115 | + ** subscription will get removed if the user later edits their | |
| 2116 | + ** subscriptions, as non-admins are not permitted to add that | |
| 2117 | + ** subscription. */ | |
| 2118 | + @ <label><input type="checkbox" name="su" %s(su?"checked":"")>\ | |
| 2119 | + @ User permission elevation</label> | |
| 2103 | 2120 | } |
| 2104 | 2121 | @ </td></tr> |
| 2105 | 2122 | if( strchr(ssub,'k')!=0 ){ |
| 2106 | 2123 | @ <tr><td></td><td> ↑ |
| 2107 | 2124 | @ Note: User did a one-click unsubscribe</td></tr> |
| @@ -2529,15 +2546,16 @@ | ||
| 2529 | 2546 | ** f An original forum post |
| 2530 | 2547 | ** n New forum threads |
| 2531 | 2548 | ** r Replies to my forum posts |
| 2532 | 2549 | ** x An edit to a prior forum post |
| 2533 | 2550 | ** t A new ticket or a change to an existing ticket |
| 2551 | +** u A user was added or received new permissions | |
| 2534 | 2552 | ** w A change to a wiki page |
| 2535 | 2553 | ** x Edits to forum posts |
| 2536 | 2554 | */ |
| 2537 | 2555 | struct EmailEvent { |
| 2538 | - int type; /* 'c', 'f', 'n', 'r', 't', 'w', 'x' */ | |
| 2556 | + int type; /* 'c', 'f', 'n', 'r', 't', 'u', 'w', 'x' */ | |
| 2539 | 2557 | int needMod; /* Pending moderator approval */ |
| 2540 | 2558 | Blob hdr; /* Header content, for forum entries */ |
| 2541 | 2559 | Blob txt; /* Text description to appear in an alert */ |
| 2542 | 2560 | char *zFromName; /* Human name of the sender */ |
| 2543 | 2561 | char *zPriors; /* Upthread sender IDs for forum posts */ |
| @@ -2932,10 +2950,11 @@ | ||
| 2932 | 2950 | ); |
| 2933 | 2951 | if( strchr(zSub, 'a') ) blob_appendf(pBody, " * Announcements\n"); |
| 2934 | 2952 | if( strchr(zSub, 'c') ) blob_appendf(pBody, " * Check-ins\n"); |
| 2935 | 2953 | if( strchr(zSub, 'f') ) blob_appendf(pBody, " * Forum posts\n"); |
| 2936 | 2954 | if( strchr(zSub, 't') ) blob_appendf(pBody, " * Ticket changes\n"); |
| 2955 | + if( strchr(zSub, 'u') ) blob_appendf(pBody, " * User permission elevation\n"); | |
| 2937 | 2956 | if( strchr(zSub, 'w') ) blob_appendf(pBody, " * Wiki changes\n"); |
| 2938 | 2957 | blob_appendf(pBody, "\n" |
| 2939 | 2958 | "If you take no action, your subscription will expire and you will be\n" |
| 2940 | 2959 | "unsubscribed in about %d days. To make other changes or to unsubscribe\n" |
| 2941 | 2960 | "immediately, visit the following webpage:\n\n" |
| @@ -3144,10 +3163,11 @@ | ||
| 3144 | 3163 | switch( p->type ){ |
| 3145 | 3164 | case 'x': case 'f': |
| 3146 | 3165 | case 'n': case 'r': xType = '5'; break; |
| 3147 | 3166 | case 't': xType = 'q'; break; |
| 3148 | 3167 | case 'w': xType = 'l'; break; |
| 3168 | + /* Note: case 'u' is not handled here */ | |
| 3149 | 3169 | } |
| 3150 | 3170 | if( strchr(zCap,xType)==0 ) continue; |
| 3151 | 3171 | } |
| 3152 | 3172 | }else if( strchr(zCap,'s')!=0 || strchr(zCap,'a')!=0 ){ |
| 3153 | 3173 | /* Setup and admin users can get any notification that does not |
| @@ -3160,10 +3180,11 @@ | ||
| 3160 | 3180 | case 'c': xType = 'o'; break; |
| 3161 | 3181 | case 'x': case 'f': |
| 3162 | 3182 | case 'n': case 'r': xType = '2'; break; |
| 3163 | 3183 | case 't': xType = 'r'; break; |
| 3164 | 3184 | case 'w': xType = 'j'; break; |
| 3185 | + /* Note: case 'u' is not handled here */ | |
| 3165 | 3186 | } |
| 3166 | 3187 | if( strchr(zCap,xType)==0 ) continue; |
| 3167 | 3188 | } |
| 3168 | 3189 | if( blob_size(&p->hdr)>0 ){ |
| 3169 | 3190 | /* This alert should be sent as a separate email */ |
| 3170 | 3191 |
| --- src/alerts.c | |
| +++ src/alerts.c | |
| @@ -51,10 +51,11 @@ | |
| 51 | @ -- f - Forum posts |
| 52 | @ -- k - ** Special: Unsubscribed using /oneclickunsub |
| 53 | @ -- n - New forum threads |
| 54 | @ -- r - Replies to my own forum posts |
| 55 | @ -- t - Ticket changes |
| 56 | @ -- w - Wiki changes |
| 57 | @ -- x - Edits to forum posts |
| 58 | @ -- Probably different codes will be added in the future. In the future |
| 59 | @ -- we might also add a separate table that allows subscribing to email |
| 60 | @ -- notifications for specific branches or tags or tickets. |
| @@ -1133,11 +1134,11 @@ | |
| 1133 | ** designated host and port and all times. |
| 1134 | */ |
| 1135 | |
| 1136 | |
| 1137 | /* |
| 1138 | ** COMMAND: alerts* abbreviated-subcommands |
| 1139 | ** |
| 1140 | ** Usage: %fossil alerts SUBCOMMAND ARGS... |
| 1141 | ** |
| 1142 | ** Subcommands: |
| 1143 | ** |
| @@ -1258,11 +1259,11 @@ | |
| 1258 | g.argc = 3; |
| 1259 | } |
| 1260 | pSetting = setting_info(&nSetting); |
| 1261 | for(; nSetting>0; nSetting--, pSetting++ ){ |
| 1262 | if( strncmp(pSetting->name,"email-",6)!=0 ) continue; |
| 1263 | print_setting(pSetting, 0); |
| 1264 | } |
| 1265 | }else |
| 1266 | if( strncmp(zCmd, "status", nCmd)==0 ){ |
| 1267 | Stmt q; |
| 1268 | int iCutoff; |
| @@ -1273,11 +1274,11 @@ | |
| 1273 | verify_all_options(); |
| 1274 | if( g.argc!=3 ) usage("status"); |
| 1275 | pSetting = setting_info(&nSetting); |
| 1276 | for(; nSetting>0; nSetting--, pSetting++ ){ |
| 1277 | if( strncmp(pSetting->name,"email-",6)!=0 ) continue; |
| 1278 | print_setting(pSetting, 0); |
| 1279 | } |
| 1280 | n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentSep"); |
| 1281 | fossil_print(zFmt/*works-like:"%s%d"*/, "pending-alerts", n); |
| 1282 | n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentDigest"); |
| 1283 | fossil_print(zFmt/*works-like:"%s%d"*/, "pending-digest-alerts", n); |
| @@ -1563,10 +1564,11 @@ | |
| 1563 | if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c'; |
| 1564 | if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f'; |
| 1565 | if( g.perm.RdForum && PB("sn") ) ssub[nsub++] = 'n'; |
| 1566 | if( g.perm.RdForum && PB("sr") ) ssub[nsub++] = 'r'; |
| 1567 | if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't'; |
| 1568 | if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w'; |
| 1569 | if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x'; |
| 1570 | ssub[nsub] = 0; |
| 1571 | zCode = db_text(0, |
| 1572 | "INSERT INTO subscriber(semail,suname," |
| @@ -1627,10 +1629,11 @@ | |
| 1627 | if( g.perm.Read ) cgi_set_parameter_nocopy("sc","1",1); |
| 1628 | if( g.perm.RdForum ) cgi_set_parameter_nocopy("sf","1",1); |
| 1629 | if( g.perm.RdForum ) cgi_set_parameter_nocopy("sn","1",1); |
| 1630 | if( g.perm.RdForum ) cgi_set_parameter_nocopy("sr","1",1); |
| 1631 | if( g.perm.RdTkt ) cgi_set_parameter_nocopy("st","1",1); |
| 1632 | if( g.perm.RdWiki ) cgi_set_parameter_nocopy("sw","1",1); |
| 1633 | } |
| 1634 | @ <p>To receive email notifications for changes to this |
| 1635 | @ repository, fill out the form below and press the "Submit" button.</p> |
| 1636 | form_begin(0, "%R/subscribe"); |
| @@ -1699,10 +1702,14 @@ | |
| 1699 | } |
| 1700 | if( g.perm.RdWiki ){ |
| 1701 | @ <label><input type="checkbox" name="sw" %s(PCK("sw"))> \ |
| 1702 | @ Wiki</label><br> |
| 1703 | } |
| 1704 | di = PB("di"); |
| 1705 | @ </td></tr> |
| 1706 | @ <tr> |
| 1707 | @ <td class="form_label">Delivery:</td> |
| 1708 | @ <td><select size="1" name="di"> |
| @@ -1820,11 +1827,11 @@ | |
| 1820 | ** verifying the email address. |
| 1821 | */ |
| 1822 | void alert_page(void){ |
| 1823 | const char *zName = 0; /* Value of the name= query parameter */ |
| 1824 | Stmt q; /* For querying the database */ |
| 1825 | int sa, sc, sf, st, sw, sx; /* Types of notifications requested */ |
| 1826 | int sn, sr; |
| 1827 | int sdigest = 0, sdonotcall = 0, sverified = 0; /* Other fields */ |
| 1828 | int isLogin; /* True if logged in as an individual */ |
| 1829 | const char *ssub = 0; /* Subscription flags */ |
| 1830 | const char *semail = 0; /* Email address */ |
| @@ -1881,10 +1888,11 @@ | |
| 1881 | if( g.perm.Read && PB("sc") ) newSsub[nsub++] = 'c'; |
| 1882 | if( g.perm.RdForum && PB("sf") ) newSsub[nsub++] = 'f'; |
| 1883 | if( g.perm.RdForum && PB("sn") ) newSsub[nsub++] = 'n'; |
| 1884 | if( g.perm.RdForum && PB("sr") ) newSsub[nsub++] = 'r'; |
| 1885 | if( g.perm.RdTkt && PB("st") ) newSsub[nsub++] = 't'; |
| 1886 | if( g.perm.RdWiki && PB("sw") ) newSsub[nsub++] = 'w'; |
| 1887 | if( g.perm.RdForum && PB("sx") ) newSsub[nsub++] = 'x'; |
| 1888 | newSsub[nsub] = 0; |
| 1889 | ssub = newSsub; |
| 1890 | blob_init(&update, "UPDATE subscriber SET", -1); |
| @@ -1979,10 +1987,11 @@ | |
| 1979 | sc = strchr(ssub,'c')!=0; |
| 1980 | sf = strchr(ssub,'f')!=0; |
| 1981 | sn = strchr(ssub,'n')!=0; |
| 1982 | sr = strchr(ssub,'r')!=0; |
| 1983 | st = strchr(ssub,'t')!=0; |
| 1984 | sw = strchr(ssub,'w')!=0; |
| 1985 | sx = strchr(ssub,'x')!=0; |
| 1986 | smip = db_column_text(&q, 5); |
| 1987 | mtime = db_column_text(&q, 7); |
| 1988 | sctime = db_column_text(&q, 8); |
| @@ -2097,11 +2106,19 @@ | |
| 2097 | @ <label><input type="checkbox" name="st" %s(st?"checked":"")>\ |
| 2098 | @ Ticket changes</label><br> |
| 2099 | } |
| 2100 | if( g.perm.RdWiki ){ |
| 2101 | @ <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\ |
| 2102 | @ Wiki</label> |
| 2103 | } |
| 2104 | @ </td></tr> |
| 2105 | if( strchr(ssub,'k')!=0 ){ |
| 2106 | @ <tr><td></td><td> ↑ |
| 2107 | @ Note: User did a one-click unsubscribe</td></tr> |
| @@ -2529,15 +2546,16 @@ | |
| 2529 | ** f An original forum post |
| 2530 | ** n New forum threads |
| 2531 | ** r Replies to my forum posts |
| 2532 | ** x An edit to a prior forum post |
| 2533 | ** t A new ticket or a change to an existing ticket |
| 2534 | ** w A change to a wiki page |
| 2535 | ** x Edits to forum posts |
| 2536 | */ |
| 2537 | struct EmailEvent { |
| 2538 | int type; /* 'c', 'f', 'n', 'r', 't', 'w', 'x' */ |
| 2539 | int needMod; /* Pending moderator approval */ |
| 2540 | Blob hdr; /* Header content, for forum entries */ |
| 2541 | Blob txt; /* Text description to appear in an alert */ |
| 2542 | char *zFromName; /* Human name of the sender */ |
| 2543 | char *zPriors; /* Upthread sender IDs for forum posts */ |
| @@ -2932,10 +2950,11 @@ | |
| 2932 | ); |
| 2933 | if( strchr(zSub, 'a') ) blob_appendf(pBody, " * Announcements\n"); |
| 2934 | if( strchr(zSub, 'c') ) blob_appendf(pBody, " * Check-ins\n"); |
| 2935 | if( strchr(zSub, 'f') ) blob_appendf(pBody, " * Forum posts\n"); |
| 2936 | if( strchr(zSub, 't') ) blob_appendf(pBody, " * Ticket changes\n"); |
| 2937 | if( strchr(zSub, 'w') ) blob_appendf(pBody, " * Wiki changes\n"); |
| 2938 | blob_appendf(pBody, "\n" |
| 2939 | "If you take no action, your subscription will expire and you will be\n" |
| 2940 | "unsubscribed in about %d days. To make other changes or to unsubscribe\n" |
| 2941 | "immediately, visit the following webpage:\n\n" |
| @@ -3144,10 +3163,11 @@ | |
| 3144 | switch( p->type ){ |
| 3145 | case 'x': case 'f': |
| 3146 | case 'n': case 'r': xType = '5'; break; |
| 3147 | case 't': xType = 'q'; break; |
| 3148 | case 'w': xType = 'l'; break; |
| 3149 | } |
| 3150 | if( strchr(zCap,xType)==0 ) continue; |
| 3151 | } |
| 3152 | }else if( strchr(zCap,'s')!=0 || strchr(zCap,'a')!=0 ){ |
| 3153 | /* Setup and admin users can get any notification that does not |
| @@ -3160,10 +3180,11 @@ | |
| 3160 | case 'c': xType = 'o'; break; |
| 3161 | case 'x': case 'f': |
| 3162 | case 'n': case 'r': xType = '2'; break; |
| 3163 | case 't': xType = 'r'; break; |
| 3164 | case 'w': xType = 'j'; break; |
| 3165 | } |
| 3166 | if( strchr(zCap,xType)==0 ) continue; |
| 3167 | } |
| 3168 | if( blob_size(&p->hdr)>0 ){ |
| 3169 | /* This alert should be sent as a separate email */ |
| 3170 |
| --- src/alerts.c | |
| +++ src/alerts.c | |
| @@ -51,10 +51,11 @@ | |
| 51 | @ -- f - Forum posts |
| 52 | @ -- k - ** Special: Unsubscribed using /oneclickunsub |
| 53 | @ -- n - New forum threads |
| 54 | @ -- r - Replies to my own forum posts |
| 55 | @ -- t - Ticket changes |
| 56 | @ -- u - Elevation of users' permissions (admins only) |
| 57 | @ -- w - Wiki changes |
| 58 | @ -- x - Edits to forum posts |
| 59 | @ -- Probably different codes will be added in the future. In the future |
| 60 | @ -- we might also add a separate table that allows subscribing to email |
| 61 | @ -- notifications for specific branches or tags or tickets. |
| @@ -1133,11 +1134,11 @@ | |
| 1134 | ** designated host and port and all times. |
| 1135 | */ |
| 1136 | |
| 1137 | |
| 1138 | /* |
| 1139 | ** COMMAND: alerts* abbrv-subcom |
| 1140 | ** |
| 1141 | ** Usage: %fossil alerts SUBCOMMAND ARGS... |
| 1142 | ** |
| 1143 | ** Subcommands: |
| 1144 | ** |
| @@ -1258,11 +1259,11 @@ | |
| 1259 | g.argc = 3; |
| 1260 | } |
| 1261 | pSetting = setting_info(&nSetting); |
| 1262 | for(; nSetting>0; nSetting--, pSetting++ ){ |
| 1263 | if( strncmp(pSetting->name,"email-",6)!=0 ) continue; |
| 1264 | print_setting(pSetting, 0, 0); |
| 1265 | } |
| 1266 | }else |
| 1267 | if( strncmp(zCmd, "status", nCmd)==0 ){ |
| 1268 | Stmt q; |
| 1269 | int iCutoff; |
| @@ -1273,11 +1274,11 @@ | |
| 1274 | verify_all_options(); |
| 1275 | if( g.argc!=3 ) usage("status"); |
| 1276 | pSetting = setting_info(&nSetting); |
| 1277 | for(; nSetting>0; nSetting--, pSetting++ ){ |
| 1278 | if( strncmp(pSetting->name,"email-",6)!=0 ) continue; |
| 1279 | print_setting(pSetting, 0, 0); |
| 1280 | } |
| 1281 | n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentSep"); |
| 1282 | fossil_print(zFmt/*works-like:"%s%d"*/, "pending-alerts", n); |
| 1283 | n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentDigest"); |
| 1284 | fossil_print(zFmt/*works-like:"%s%d"*/, "pending-digest-alerts", n); |
| @@ -1563,10 +1564,11 @@ | |
| 1564 | if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c'; |
| 1565 | if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f'; |
| 1566 | if( g.perm.RdForum && PB("sn") ) ssub[nsub++] = 'n'; |
| 1567 | if( g.perm.RdForum && PB("sr") ) ssub[nsub++] = 'r'; |
| 1568 | if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't'; |
| 1569 | if( g.perm.Admin && PB("su") ) ssub[nsub++] = 'u'; |
| 1570 | if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w'; |
| 1571 | if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x'; |
| 1572 | ssub[nsub] = 0; |
| 1573 | zCode = db_text(0, |
| 1574 | "INSERT INTO subscriber(semail,suname," |
| @@ -1627,10 +1629,11 @@ | |
| 1629 | if( g.perm.Read ) cgi_set_parameter_nocopy("sc","1",1); |
| 1630 | if( g.perm.RdForum ) cgi_set_parameter_nocopy("sf","1",1); |
| 1631 | if( g.perm.RdForum ) cgi_set_parameter_nocopy("sn","1",1); |
| 1632 | if( g.perm.RdForum ) cgi_set_parameter_nocopy("sr","1",1); |
| 1633 | if( g.perm.RdTkt ) cgi_set_parameter_nocopy("st","1",1); |
| 1634 | if( g.perm.Admin ) cgi_set_parameter_nocopy("su","1",1); |
| 1635 | if( g.perm.RdWiki ) cgi_set_parameter_nocopy("sw","1",1); |
| 1636 | } |
| 1637 | @ <p>To receive email notifications for changes to this |
| 1638 | @ repository, fill out the form below and press the "Submit" button.</p> |
| 1639 | form_begin(0, "%R/subscribe"); |
| @@ -1699,10 +1702,14 @@ | |
| 1702 | } |
| 1703 | if( g.perm.RdWiki ){ |
| 1704 | @ <label><input type="checkbox" name="sw" %s(PCK("sw"))> \ |
| 1705 | @ Wiki</label><br> |
| 1706 | } |
| 1707 | if( g.perm.Admin ){ |
| 1708 | @ <label><input type="checkbox" name="su" %s(PCK("su"))> \ |
| 1709 | @ User permission elevation</label> |
| 1710 | } |
| 1711 | di = PB("di"); |
| 1712 | @ </td></tr> |
| 1713 | @ <tr> |
| 1714 | @ <td class="form_label">Delivery:</td> |
| 1715 | @ <td><select size="1" name="di"> |
| @@ -1820,11 +1827,11 @@ | |
| 1827 | ** verifying the email address. |
| 1828 | */ |
| 1829 | void alert_page(void){ |
| 1830 | const char *zName = 0; /* Value of the name= query parameter */ |
| 1831 | Stmt q; /* For querying the database */ |
| 1832 | int sa, sc, sf, st, su, sw, sx; /* Types of notifications requested */ |
| 1833 | int sn, sr; |
| 1834 | int sdigest = 0, sdonotcall = 0, sverified = 0; /* Other fields */ |
| 1835 | int isLogin; /* True if logged in as an individual */ |
| 1836 | const char *ssub = 0; /* Subscription flags */ |
| 1837 | const char *semail = 0; /* Email address */ |
| @@ -1881,10 +1888,11 @@ | |
| 1888 | if( g.perm.Read && PB("sc") ) newSsub[nsub++] = 'c'; |
| 1889 | if( g.perm.RdForum && PB("sf") ) newSsub[nsub++] = 'f'; |
| 1890 | if( g.perm.RdForum && PB("sn") ) newSsub[nsub++] = 'n'; |
| 1891 | if( g.perm.RdForum && PB("sr") ) newSsub[nsub++] = 'r'; |
| 1892 | if( g.perm.RdTkt && PB("st") ) newSsub[nsub++] = 't'; |
| 1893 | if( g.perm.Admin && PB("su") ) newSsub[nsub++] = 'u'; |
| 1894 | if( g.perm.RdWiki && PB("sw") ) newSsub[nsub++] = 'w'; |
| 1895 | if( g.perm.RdForum && PB("sx") ) newSsub[nsub++] = 'x'; |
| 1896 | newSsub[nsub] = 0; |
| 1897 | ssub = newSsub; |
| 1898 | blob_init(&update, "UPDATE subscriber SET", -1); |
| @@ -1979,10 +1987,11 @@ | |
| 1987 | sc = strchr(ssub,'c')!=0; |
| 1988 | sf = strchr(ssub,'f')!=0; |
| 1989 | sn = strchr(ssub,'n')!=0; |
| 1990 | sr = strchr(ssub,'r')!=0; |
| 1991 | st = strchr(ssub,'t')!=0; |
| 1992 | su = strchr(ssub,'u')!=0; |
| 1993 | sw = strchr(ssub,'w')!=0; |
| 1994 | sx = strchr(ssub,'x')!=0; |
| 1995 | smip = db_column_text(&q, 5); |
| 1996 | mtime = db_column_text(&q, 7); |
| 1997 | sctime = db_column_text(&q, 8); |
| @@ -2097,11 +2106,19 @@ | |
| 2106 | @ <label><input type="checkbox" name="st" %s(st?"checked":"")>\ |
| 2107 | @ Ticket changes</label><br> |
| 2108 | } |
| 2109 | if( g.perm.RdWiki ){ |
| 2110 | @ <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\ |
| 2111 | @ Wiki</label><br> |
| 2112 | } |
| 2113 | if( g.perm.Admin ){ |
| 2114 | /* Corner-case bug: if an admin assigns 'u' to a non-admin, that |
| 2115 | ** subscription will get removed if the user later edits their |
| 2116 | ** subscriptions, as non-admins are not permitted to add that |
| 2117 | ** subscription. */ |
| 2118 | @ <label><input type="checkbox" name="su" %s(su?"checked":"")>\ |
| 2119 | @ User permission elevation</label> |
| 2120 | } |
| 2121 | @ </td></tr> |
| 2122 | if( strchr(ssub,'k')!=0 ){ |
| 2123 | @ <tr><td></td><td> ↑ |
| 2124 | @ Note: User did a one-click unsubscribe</td></tr> |
| @@ -2529,15 +2546,16 @@ | |
| 2546 | ** f An original forum post |
| 2547 | ** n New forum threads |
| 2548 | ** r Replies to my forum posts |
| 2549 | ** x An edit to a prior forum post |
| 2550 | ** t A new ticket or a change to an existing ticket |
| 2551 | ** u A user was added or received new permissions |
| 2552 | ** w A change to a wiki page |
| 2553 | ** x Edits to forum posts |
| 2554 | */ |
| 2555 | struct EmailEvent { |
| 2556 | int type; /* 'c', 'f', 'n', 'r', 't', 'u', 'w', 'x' */ |
| 2557 | int needMod; /* Pending moderator approval */ |
| 2558 | Blob hdr; /* Header content, for forum entries */ |
| 2559 | Blob txt; /* Text description to appear in an alert */ |
| 2560 | char *zFromName; /* Human name of the sender */ |
| 2561 | char *zPriors; /* Upthread sender IDs for forum posts */ |
| @@ -2932,10 +2950,11 @@ | |
| 2950 | ); |
| 2951 | if( strchr(zSub, 'a') ) blob_appendf(pBody, " * Announcements\n"); |
| 2952 | if( strchr(zSub, 'c') ) blob_appendf(pBody, " * Check-ins\n"); |
| 2953 | if( strchr(zSub, 'f') ) blob_appendf(pBody, " * Forum posts\n"); |
| 2954 | if( strchr(zSub, 't') ) blob_appendf(pBody, " * Ticket changes\n"); |
| 2955 | if( strchr(zSub, 'u') ) blob_appendf(pBody, " * User permission elevation\n"); |
| 2956 | if( strchr(zSub, 'w') ) blob_appendf(pBody, " * Wiki changes\n"); |
| 2957 | blob_appendf(pBody, "\n" |
| 2958 | "If you take no action, your subscription will expire and you will be\n" |
| 2959 | "unsubscribed in about %d days. To make other changes or to unsubscribe\n" |
| 2960 | "immediately, visit the following webpage:\n\n" |
| @@ -3144,10 +3163,11 @@ | |
| 3163 | switch( p->type ){ |
| 3164 | case 'x': case 'f': |
| 3165 | case 'n': case 'r': xType = '5'; break; |
| 3166 | case 't': xType = 'q'; break; |
| 3167 | case 'w': xType = 'l'; break; |
| 3168 | /* Note: case 'u' is not handled here */ |
| 3169 | } |
| 3170 | if( strchr(zCap,xType)==0 ) continue; |
| 3171 | } |
| 3172 | }else if( strchr(zCap,'s')!=0 || strchr(zCap,'a')!=0 ){ |
| 3173 | /* Setup and admin users can get any notification that does not |
| @@ -3160,10 +3180,11 @@ | |
| 3180 | case 'c': xType = 'o'; break; |
| 3181 | case 'x': case 'f': |
| 3182 | case 'n': case 'r': xType = '2'; break; |
| 3183 | case 't': xType = 'r'; break; |
| 3184 | case 'w': xType = 'j'; break; |
| 3185 | /* Note: case 'u' is not handled here */ |
| 3186 | } |
| 3187 | if( strchr(zCap,xType)==0 ) continue; |
| 3188 | } |
| 3189 | if( blob_size(&p->hdr)>0 ){ |
| 3190 | /* This alert should be sent as a separate email */ |
| 3191 |
+2
-1
| --- src/allrepo.c | ||
| +++ src/allrepo.c | ||
| @@ -51,11 +51,11 @@ | ||
| 51 | 51 | blob_appendf(pExtra, " %s", g.argv[i]); |
| 52 | 52 | } |
| 53 | 53 | } |
| 54 | 54 | |
| 55 | 55 | /* |
| 56 | -** COMMAND: all abbreviated-subcommands | |
| 56 | +** COMMAND: all abbrv-subcom | |
| 57 | 57 | ** |
| 58 | 58 | ** Usage: %fossil all SUBCOMMAND ... |
| 59 | 59 | ** |
| 60 | 60 | ** The ~/.fossil file records the location of all repositories for a |
| 61 | 61 | ** user. This command performs certain operations on all repositories |
| @@ -316,10 +316,11 @@ | ||
| 316 | 316 | zCmd = "repack"; |
| 317 | 317 | }else if( fossil_strcmp(zCmd, "set")==0 |
| 318 | 318 | || fossil_strcmp(zCmd, "setting")==0 |
| 319 | 319 | || fossil_strcmp(zCmd, "settings")==0 ){ |
| 320 | 320 | zCmd = "settings -R"; |
| 321 | + collect_argument(&extra, "changed", 0); | |
| 321 | 322 | collect_argv(&extra, 3); |
| 322 | 323 | }else if( fossil_strcmp(zCmd, "unset")==0 ){ |
| 323 | 324 | zCmd = "unset -R"; |
| 324 | 325 | collect_argv(&extra, 3); |
| 325 | 326 | }else if( fossil_strcmp(zCmd, "fts-config")==0 ){ |
| 326 | 327 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -51,11 +51,11 @@ | |
| 51 | blob_appendf(pExtra, " %s", g.argv[i]); |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | /* |
| 56 | ** COMMAND: all abbreviated-subcommands |
| 57 | ** |
| 58 | ** Usage: %fossil all SUBCOMMAND ... |
| 59 | ** |
| 60 | ** The ~/.fossil file records the location of all repositories for a |
| 61 | ** user. This command performs certain operations on all repositories |
| @@ -316,10 +316,11 @@ | |
| 316 | zCmd = "repack"; |
| 317 | }else if( fossil_strcmp(zCmd, "set")==0 |
| 318 | || fossil_strcmp(zCmd, "setting")==0 |
| 319 | || fossil_strcmp(zCmd, "settings")==0 ){ |
| 320 | zCmd = "settings -R"; |
| 321 | collect_argv(&extra, 3); |
| 322 | }else if( fossil_strcmp(zCmd, "unset")==0 ){ |
| 323 | zCmd = "unset -R"; |
| 324 | collect_argv(&extra, 3); |
| 325 | }else if( fossil_strcmp(zCmd, "fts-config")==0 ){ |
| 326 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -51,11 +51,11 @@ | |
| 51 | blob_appendf(pExtra, " %s", g.argv[i]); |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | /* |
| 56 | ** COMMAND: all abbrv-subcom |
| 57 | ** |
| 58 | ** Usage: %fossil all SUBCOMMAND ... |
| 59 | ** |
| 60 | ** The ~/.fossil file records the location of all repositories for a |
| 61 | ** user. This command performs certain operations on all repositories |
| @@ -316,10 +316,11 @@ | |
| 316 | zCmd = "repack"; |
| 317 | }else if( fossil_strcmp(zCmd, "set")==0 |
| 318 | || fossil_strcmp(zCmd, "setting")==0 |
| 319 | || fossil_strcmp(zCmd, "settings")==0 ){ |
| 320 | zCmd = "settings -R"; |
| 321 | collect_argument(&extra, "changed", 0); |
| 322 | collect_argv(&extra, 3); |
| 323 | }else if( fossil_strcmp(zCmd, "unset")==0 ){ |
| 324 | zCmd = "unset -R"; |
| 325 | collect_argv(&extra, 3); |
| 326 | }else if( fossil_strcmp(zCmd, "fts-config")==0 ){ |
| 327 |
+1
-1
| --- src/cache.c | ||
| +++ src/cache.c | ||
| @@ -255,11 +255,11 @@ | ||
| 255 | 255 | void cache_initialize(void){ |
| 256 | 256 | sqlite3_close(cacheOpen(1)); |
| 257 | 257 | } |
| 258 | 258 | |
| 259 | 259 | /* |
| 260 | -** COMMAND: cache* abbreviated-subcommands | |
| 260 | +** COMMAND: cache* abbrv-subcom | |
| 261 | 261 | ** |
| 262 | 262 | ** Usage: %fossil cache SUBCOMMAND |
| 263 | 263 | ** |
| 264 | 264 | ** Manage the cache used for potentially expensive web pages such as |
| 265 | 265 | ** /zip and /tarball. SUBCOMMAND can be: |
| 266 | 266 |
| --- src/cache.c | |
| +++ src/cache.c | |
| @@ -255,11 +255,11 @@ | |
| 255 | void cache_initialize(void){ |
| 256 | sqlite3_close(cacheOpen(1)); |
| 257 | } |
| 258 | |
| 259 | /* |
| 260 | ** COMMAND: cache* abbreviated-subcommands |
| 261 | ** |
| 262 | ** Usage: %fossil cache SUBCOMMAND |
| 263 | ** |
| 264 | ** Manage the cache used for potentially expensive web pages such as |
| 265 | ** /zip and /tarball. SUBCOMMAND can be: |
| 266 |
| --- src/cache.c | |
| +++ src/cache.c | |
| @@ -255,11 +255,11 @@ | |
| 255 | void cache_initialize(void){ |
| 256 | sqlite3_close(cacheOpen(1)); |
| 257 | } |
| 258 | |
| 259 | /* |
| 260 | ** COMMAND: cache* abbrv-subcom |
| 261 | ** |
| 262 | ** Usage: %fossil cache SUBCOMMAND |
| 263 | ** |
| 264 | ** Manage the cache used for potentially expensive web pages such as |
| 265 | ** /zip and /tarball. SUBCOMMAND can be: |
| 266 |
+25
-5
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -688,21 +688,41 @@ | ||
| 688 | 688 | } |
| 689 | 689 | |
| 690 | 690 | |
| 691 | 691 | /* |
| 692 | 692 | ** Return true if the current request is coming from the same origin. |
| 693 | +** | |
| 694 | +** If the request comes from a different origin and bErrorLog is true, then | |
| 695 | +** put a warning message on the error log as this was a possible hack | |
| 696 | +** attempt. | |
| 693 | 697 | */ |
| 694 | -int cgi_same_origin(void){ | |
| 698 | +int cgi_same_origin(int bErrorLog){ | |
| 695 | 699 | const char *zRef; |
| 700 | + char *zToFree = 0; | |
| 696 | 701 | int nBase; |
| 702 | + int rc; | |
| 697 | 703 | if( g.zBaseURL==0 ) return 0; |
| 698 | 704 | zRef = P("HTTP_REFERER"); |
| 699 | 705 | if( zRef==0 ) return 0; |
| 706 | + if( strchr(zRef,'%')!=0 ){ | |
| 707 | + zToFree = strdup(zRef); | |
| 708 | + dehttpize(zToFree); | |
| 709 | + zRef = zToFree; | |
| 710 | + } | |
| 700 | 711 | nBase = (int)strlen(g.zBaseURL); |
| 701 | - if( fossil_strncmp(g.zBaseURL,zRef,nBase)!=0 ) return 0; | |
| 702 | - if( zRef[nBase]!=0 && zRef[nBase]!='/' ) return 0; | |
| 703 | - return 1; | |
| 712 | + if( fossil_strncmp(g.zBaseURL,zRef,nBase)!=0 ){ | |
| 713 | + rc = 0; | |
| 714 | + }else if( zRef[nBase]!=0 && zRef[nBase]!='/' ){ | |
| 715 | + rc = 0; | |
| 716 | + }else{ | |
| 717 | + rc = 1; | |
| 718 | + } | |
| 719 | + if( rc==0 && bErrorLog && fossil_strcmp(P("REQUST_METHOD"),"POST")==0 ){ | |
| 720 | + fossil_errorlog("warning: POST from different origin"); | |
| 721 | + } | |
| 722 | + fossil_free(zToFree); | |
| 723 | + return rc; | |
| 704 | 724 | } |
| 705 | 725 | |
| 706 | 726 | /* |
| 707 | 727 | ** Return true if the current CGI request is a POST request |
| 708 | 728 | */ |
| @@ -733,11 +753,11 @@ | ||
| 733 | 753 | ** 3: (2) plus there is a valid "csrf" token in the request |
| 734 | 754 | */ |
| 735 | 755 | int cgi_csrf_safe(int securityLevel){ |
| 736 | 756 | if( g.okCsrf<0 ) return 0; |
| 737 | 757 | if( g.okCsrf==0 ){ |
| 738 | - if( !cgi_same_origin() ){ | |
| 758 | + if( !cgi_same_origin(1) ){ | |
| 739 | 759 | g.okCsrf = -1; |
| 740 | 760 | }else{ |
| 741 | 761 | g.okCsrf = 1; |
| 742 | 762 | if( cgi_is_post_request() ){ |
| 743 | 763 | g.okCsrf = 2; |
| 744 | 764 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -688,21 +688,41 @@ | |
| 688 | } |
| 689 | |
| 690 | |
| 691 | /* |
| 692 | ** Return true if the current request is coming from the same origin. |
| 693 | */ |
| 694 | int cgi_same_origin(void){ |
| 695 | const char *zRef; |
| 696 | int nBase; |
| 697 | if( g.zBaseURL==0 ) return 0; |
| 698 | zRef = P("HTTP_REFERER"); |
| 699 | if( zRef==0 ) return 0; |
| 700 | nBase = (int)strlen(g.zBaseURL); |
| 701 | if( fossil_strncmp(g.zBaseURL,zRef,nBase)!=0 ) return 0; |
| 702 | if( zRef[nBase]!=0 && zRef[nBase]!='/' ) return 0; |
| 703 | return 1; |
| 704 | } |
| 705 | |
| 706 | /* |
| 707 | ** Return true if the current CGI request is a POST request |
| 708 | */ |
| @@ -733,11 +753,11 @@ | |
| 733 | ** 3: (2) plus there is a valid "csrf" token in the request |
| 734 | */ |
| 735 | int cgi_csrf_safe(int securityLevel){ |
| 736 | if( g.okCsrf<0 ) return 0; |
| 737 | if( g.okCsrf==0 ){ |
| 738 | if( !cgi_same_origin() ){ |
| 739 | g.okCsrf = -1; |
| 740 | }else{ |
| 741 | g.okCsrf = 1; |
| 742 | if( cgi_is_post_request() ){ |
| 743 | g.okCsrf = 2; |
| 744 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -688,21 +688,41 @@ | |
| 688 | } |
| 689 | |
| 690 | |
| 691 | /* |
| 692 | ** Return true if the current request is coming from the same origin. |
| 693 | ** |
| 694 | ** If the request comes from a different origin and bErrorLog is true, then |
| 695 | ** put a warning message on the error log as this was a possible hack |
| 696 | ** attempt. |
| 697 | */ |
| 698 | int cgi_same_origin(int bErrorLog){ |
| 699 | const char *zRef; |
| 700 | char *zToFree = 0; |
| 701 | int nBase; |
| 702 | int rc; |
| 703 | if( g.zBaseURL==0 ) return 0; |
| 704 | zRef = P("HTTP_REFERER"); |
| 705 | if( zRef==0 ) return 0; |
| 706 | if( strchr(zRef,'%')!=0 ){ |
| 707 | zToFree = strdup(zRef); |
| 708 | dehttpize(zToFree); |
| 709 | zRef = zToFree; |
| 710 | } |
| 711 | nBase = (int)strlen(g.zBaseURL); |
| 712 | if( fossil_strncmp(g.zBaseURL,zRef,nBase)!=0 ){ |
| 713 | rc = 0; |
| 714 | }else if( zRef[nBase]!=0 && zRef[nBase]!='/' ){ |
| 715 | rc = 0; |
| 716 | }else{ |
| 717 | rc = 1; |
| 718 | } |
| 719 | if( rc==0 && bErrorLog && fossil_strcmp(P("REQUST_METHOD"),"POST")==0 ){ |
| 720 | fossil_errorlog("warning: POST from different origin"); |
| 721 | } |
| 722 | fossil_free(zToFree); |
| 723 | return rc; |
| 724 | } |
| 725 | |
| 726 | /* |
| 727 | ** Return true if the current CGI request is a POST request |
| 728 | */ |
| @@ -733,11 +753,11 @@ | |
| 753 | ** 3: (2) plus there is a valid "csrf" token in the request |
| 754 | */ |
| 755 | int cgi_csrf_safe(int securityLevel){ |
| 756 | if( g.okCsrf<0 ) return 0; |
| 757 | if( g.okCsrf==0 ){ |
| 758 | if( !cgi_same_origin(1) ){ |
| 759 | g.okCsrf = -1; |
| 760 | }else{ |
| 761 | g.okCsrf = 1; |
| 762 | if( cgi_is_post_request() ){ |
| 763 | g.okCsrf = 2; |
| 764 |
+252
-70
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -2281,42 +2281,165 @@ | ||
| 2281 | 2281 | static int tagCmp(const void *a, const void *b){ |
| 2282 | 2282 | char **pA = (char**)a; |
| 2283 | 2283 | char **pB = (char**)b; |
| 2284 | 2284 | return fossil_strcmp(pA[0], pB[0]); |
| 2285 | 2285 | } |
| 2286 | + | |
| 2287 | +/* | |
| 2288 | +** SETTING: verify-comments width=8 default=on | |
| 2289 | +** | |
| 2290 | +** This setting determines how much sanity checking, if any, the | |
| 2291 | +** "fossil commit" and "fossil amend" commands do against check-in | |
| 2292 | +** comments. Recognized values: | |
| 2293 | +** | |
| 2294 | +** on (Default) Check for bad syntax in check-in comments | |
| 2295 | +** and offer the user a chance to continue editing for | |
| 2296 | +** interactive sessions, or simply abort the commit if | |
| 2297 | +** commit was entered using -m or -M | |
| 2298 | +** | |
| 2299 | +** off Do not do syntax checking of any kind | |
| 2300 | +** | |
| 2301 | +** links Similar to "on", except only check for bad hyperlinks | |
| 2302 | +** | |
| 2303 | +** preview Do all the same checks as "on" but also preview the | |
| 2304 | +** check-in comment to the user during interactive sessions | |
| 2305 | +** and provide an opportunity to accept or re-edit | |
| 2306 | +*/ | |
| 2307 | + | |
| 2308 | +#if INTERFACE | |
| 2309 | +#define COMCK_LINKS 0x01 /* Check for back hyperlinks */ | |
| 2310 | +#define COMCK_MARKUP 0x02 /* Check markup */ | |
| 2311 | +#define COMCK_PREVIEW 0x04 /* Always preview, even if no issues found */ | |
| 2312 | +#define COMCK_NOPREVIEW 0x08 /* Never preview, even for other errors */ | |
| 2313 | +#endif /* INTERFACE */ | |
| 2314 | + | |
| 2315 | +/* | |
| 2316 | +** Check for possible formatting errors in the comment string pComment. | |
| 2317 | +** | |
| 2318 | +** If concerns are found, write a description of the problem(s) to | |
| 2319 | +** stdout and return non-zero. The return value is some combination | |
| 2320 | +** of the COMCK_* flags, depending on what went wrong. | |
| 2321 | +** | |
| 2322 | +** If no issues are seen, do not output anything and return zero. | |
| 2323 | +*/ | |
| 2324 | +int suspicious_comment(Blob *pComment, int mFlags){ | |
| 2325 | + char *zStart = blob_str(pComment); | |
| 2326 | + char *z; | |
| 2327 | + char *zEnd, *zEnd2; | |
| 2328 | + char *zSep; | |
| 2329 | + char cSave1; | |
| 2330 | + int nIssue = 0; | |
| 2331 | + int rc = mFlags & COMCK_PREVIEW; | |
| 2332 | + Blob out; | |
| 2333 | + static const char zSpecial[] = "\\&<*_`["; | |
| 2334 | + | |
| 2335 | + if( mFlags==0 ) return 0; | |
| 2336 | + z = zStart; | |
| 2337 | + blob_init(&out, 0, 0); | |
| 2338 | + if( mFlags & COMCK_LINKS ){ | |
| 2339 | + while( (z = strchr(z,'['))!=0 ){ | |
| 2340 | + zEnd = strchr(z,']'); | |
| 2341 | + if( zEnd==0 ){ | |
| 2342 | + blob_appendf(&out,"\n (%d) ", ++nIssue); | |
| 2343 | + blob_appendf(&out, "Unterminated hyperlink \"%.12s...\"", z); | |
| 2344 | + break; | |
| 2345 | + } | |
| 2346 | + if( zEnd[1]=='(' && (zEnd2 = strchr(zEnd,')'))!=0 ){ | |
| 2347 | + blob_appendf(&out,"\n (%d) ", ++nIssue); | |
| 2348 | + blob_appendf(&out, "Markdown hyperlink syntax: %.*s", | |
| 2349 | + (int)(zEnd2+1-z), z); | |
| 2350 | + z = zEnd2; | |
| 2351 | + continue; | |
| 2352 | + } | |
| 2353 | + zSep = strchr(z+1,'|'); | |
| 2354 | + if( zSep==0 || zSep>zEnd ) zSep = zEnd; | |
| 2355 | + while( zSep>z && fossil_isspace(zSep[-1]) ) zSep--; | |
| 2356 | + cSave1 = zSep[0]; | |
| 2357 | + zSep[0] = 0; | |
| 2358 | + if( !wiki_valid_link_target(z+1) ){ | |
| 2359 | + blob_appendf(&out,"\n (%d) ", ++nIssue); | |
| 2360 | + blob_appendf(&out, "Broken hyperlink: [%s]", z+1); | |
| 2361 | + } | |
| 2362 | + zSep[0] = cSave1; | |
| 2363 | + z = zEnd; | |
| 2364 | + } | |
| 2365 | + } | |
| 2366 | + | |
| 2367 | + if( nIssue>0 | |
| 2368 | + || (mFlags & COMCK_PREVIEW)!=0 | |
| 2369 | + || ((mFlags & COMCK_MARKUP)!=0 && strcspn(zStart,zSpecial)<strlen(zStart)) | |
| 2370 | + ){ | |
| 2371 | + char zGot[16]; | |
| 2372 | + int nGot = 0; | |
| 2373 | + int i; | |
| 2374 | + if( (mFlags & COMCK_MARKUP)!=0 ){ | |
| 2375 | + for(i=0; zSpecial[i]; i++){ | |
| 2376 | + if( strchr(zStart,zSpecial[i]) ) zGot[nGot++] = zSpecial[i]; | |
| 2377 | + } | |
| 2378 | + } | |
| 2379 | + zGot[nGot] = 0; | |
| 2380 | + if( nGot>0 ) rc |= COMCK_MARKUP; | |
| 2381 | + if( nGot>0 && nIssue>0 ){ | |
| 2382 | + blob_appendf(&out,"\n (%d) Comment uses special character%s \"%s\"", | |
| 2383 | + ++nIssue, (nGot>1 ? "s" : ""), zGot); | |
| 2384 | + nGot = 0; | |
| 2385 | + } | |
| 2386 | + if( nIssue ){ | |
| 2387 | + rc |= COMCK_LINKS; | |
| 2388 | + fossil_print( | |
| 2389 | + "Possible comment formatting error%s:%b\n", | |
| 2390 | + nIssue>1 ? "s" : "", &out | |
| 2391 | + ); | |
| 2392 | + } | |
| 2393 | + if( (mFlags & COMCK_NOPREVIEW)==0 ){ | |
| 2394 | + Blob in, html, txt; | |
| 2395 | + blob_init(&in, blob_str(pComment), -1); | |
| 2396 | + blob_init(&html, 0, 0); | |
| 2397 | + blob_init(&txt, 0, 0); | |
| 2398 | + wiki_convert(&in, &html, WIKI_INLINE); | |
| 2399 | + html_to_plaintext(blob_str(&html), &txt); | |
| 2400 | + if( nGot>0 ){ | |
| 2401 | + fossil_print( | |
| 2402 | + "The comment uses special character%s \"%s\". " | |
| 2403 | + "Does it render as you expect?\n\n ", | |
| 2404 | + (nGot>1 ? "s" : ""), zGot | |
| 2405 | + ); | |
| 2406 | + }else{ | |
| 2407 | + fossil_print("Preview of the check-in comment:\n\n "); | |
| 2408 | + } | |
| 2409 | + comment_print(blob_str(&txt), 0, 3, -1, get_comment_format()); | |
| 2410 | + blob_reset(&in); | |
| 2411 | + blob_reset(&html); | |
| 2412 | + blob_reset(&txt); | |
| 2413 | + } | |
| 2414 | + } | |
| 2415 | + blob_reset(&out); | |
| 2416 | + return rc; | |
| 2417 | +} | |
| 2286 | 2418 | |
| 2287 | 2419 | /* |
| 2288 | 2420 | ** COMMAND: ci# |
| 2289 | 2421 | ** COMMAND: commit |
| 2290 | 2422 | ** |
| 2291 | 2423 | ** Usage: %fossil commit ?OPTIONS? ?FILE...? |
| 2292 | 2424 | ** or: %fossil ci ?OPTIONS? ?FILE...? |
| 2293 | 2425 | ** |
| 2294 | -** Create a new version containing all of the changes in the current | |
| 2295 | -** check-out. You will be prompted to enter a check-in comment unless | |
| 2296 | -** the comment has been specified on the command-line using "-m" or a | |
| 2297 | -** file containing the comment using -M. The editor defined in the | |
| 2298 | -** "editor" fossil option (see %fossil help set) will be used, or from | |
| 2299 | -** the "VISUAL" or "EDITOR" environment variables (in that order) if | |
| 2300 | -** no editor is set. | |
| 2426 | +** Create a new check-in containing all of the changes in the current | |
| 2427 | +** check-out. All changes are committed unless some subset of files | |
| 2428 | +** is specified on the command line, in which case only the named files | |
| 2429 | +** become part of the new check-in. | |
| 2301 | 2430 | ** |
| 2302 | -** All files that have changed will be committed unless some subset of | |
| 2303 | -** files is specified on the command line. | |
| 2431 | +** You will be prompted to enter a check-in comment unless the comment | |
| 2432 | +** has been specified on the command-line using "-m" or "-M". The | |
| 2433 | +** text editor used is determined by the "editor" setting, or by the | |
| 2434 | +** "VISUAL" or "EDITOR" environment variables. Commit message text is | |
| 2435 | +** interpreted as fossil-wiki format. Potentially misformatted check-in | |
| 2436 | +** comment text is detected and reported unless the --no-verify-comment | |
| 2437 | +** option is used. | |
| 2304 | 2438 | ** |
| 2305 | 2439 | ** The --branch option followed by a branch name causes the new |
| 2306 | -** check-in to be placed in a newly-created branch with the name | |
| 2307 | -** passed to the --branch option. | |
| 2308 | -** | |
| 2309 | -** Use the --branchcolor option followed by a color name (ex: | |
| 2310 | -** '#ffc0c0') to specify the background color of entries in the new | |
| 2311 | -** branch when shown in the web timeline interface. The use of | |
| 2312 | -** the --branchcolor option is not recommended. Instead, let Fossil | |
| 2313 | -** choose the branch color automatically. | |
| 2314 | -** | |
| 2315 | -** The --bgcolor option works like --branchcolor but only sets the | |
| 2316 | -** background color for a single check-in. Subsequent check-ins revert | |
| 2317 | -** to the default color. | |
| 2440 | +** check-in to be placed in a newly-created branch with name specified. | |
| 2318 | 2441 | ** |
| 2319 | 2442 | ** A check-in is not permitted to fork unless the --allow-fork option |
| 2320 | 2443 | ** appears. An empty check-in (i.e. with nothing changed) is not |
| 2321 | 2444 | ** allowed unless the --allow-empty option appears. A check-in may not |
| 2322 | 2445 | ** be older than its ancestor unless the --allow-older option appears. |
| @@ -2328,68 +2451,60 @@ | ||
| 2328 | 2451 | ** unless the interactive user chooses to proceed. If there is no |
| 2329 | 2452 | ** interactive user or these warnings should be skipped for some other |
| 2330 | 2453 | ** reason, the --no-warnings option may be used. A check-in is not |
| 2331 | 2454 | ** allowed against a closed leaf. |
| 2332 | 2455 | ** |
| 2333 | -** If a commit message is blank, you will be prompted: | |
| 2334 | -** ("continue (y/N)?") to confirm you really want to commit with a | |
| 2335 | -** blank commit message. The default value is "N", do not commit. | |
| 2336 | -** | |
| 2337 | 2456 | ** The --private option creates a private check-in that is never synced. |
| 2338 | 2457 | ** Children of private check-ins are automatically private. |
| 2339 | 2458 | ** |
| 2340 | 2459 | ** The --tag option applies the symbolic tag name to the check-in. |
| 2341 | -** | |
| 2342 | -** The --hash option detects edited files by computing each file's | |
| 2343 | -** artifact hash rather than just checking for changes to its size or mtime. | |
| 2460 | +** The --tag option can be repeated to assign multiple tags to a check-in. | |
| 2461 | +** For example: "... --tag release --tag version-1.2.3 ..." | |
| 2344 | 2462 | ** |
| 2345 | 2463 | ** Options: |
| 2346 | 2464 | ** --allow-conflict Allow unresolved merge conflicts |
| 2347 | 2465 | ** --allow-empty Allow a commit with no changes |
| 2348 | 2466 | ** --allow-fork Allow the commit to fork |
| 2349 | 2467 | ** --allow-older Allow a commit older than its ancestor |
| 2350 | 2468 | ** --baseline Use a baseline manifest in the commit process |
| 2351 | -** --bgcolor COLOR Apply COLOR to this one check-in only | |
| 2352 | 2469 | ** --branch NEW-BRANCH-NAME Check in to this new branch |
| 2353 | -** --branchcolor COLOR Apply given COLOR to the branch | |
| 2354 | 2470 | ** --close Close the branch being committed |
| 2355 | -** --date-override DATETIME DATE to use instead of 'now' | |
| 2471 | +** --date-override DATETIME Make DATETIME the time of the check-in. | |
| 2472 | +** Useful when importing historical check-ins | |
| 2473 | +** from another version control system. | |
| 2356 | 2474 | ** --delta Use a delta manifest in the commit process |
| 2357 | 2475 | ** --hash Verify file status using hashing rather |
| 2358 | -** than relying on file mtimes | |
| 2476 | +** than relying on filesystem mtimes | |
| 2359 | 2477 | ** --if-changes Make this command a silent no-op if there |
| 2360 | 2478 | ** are no changes |
| 2361 | 2479 | ** --ignore-clock-skew If a clock skew is detected, ignore it and |
| 2362 | 2480 | ** behave as if the user had entered 'yes' to |
| 2363 | 2481 | ** the question of whether to proceed despite |
| 2364 | 2482 | ** the skew. |
| 2365 | 2483 | ** --ignore-oversize Do not warn the user about oversized files |
| 2366 | 2484 | ** --integrate Close all merged-in branches |
| 2367 | -** -m|--comment COMMENT-TEXT Use COMMENT-TEXT as commit comment | |
| 2368 | -** -M|--message-file FILE Read the commit comment from given file | |
| 2369 | -** --mimetype MIMETYPE Mimetype of check-in comment | |
| 2370 | -** -n|--dry-run If given, display instead of run actions | |
| 2485 | +** -m|--comment COMMENT-TEXT Use COMMENT-TEXT as the check-in comment | |
| 2486 | +** -M|--message-file FILE Read the check-in comment from FILE | |
| 2487 | +** -n|--dry-run Do not actually create a new check-in. Just | |
| 2488 | +** show what would have happened. For debugging. | |
| 2371 | 2489 | ** -v|--verbose Show a diff in the commit message prompt |
| 2372 | 2490 | ** --no-prompt This option disables prompting the user for |
| 2373 | 2491 | ** input and assumes an answer of 'No' for every |
| 2374 | 2492 | ** question. |
| 2375 | 2493 | ** --no-warnings Omit all warnings about file contents |
| 2376 | 2494 | ** --no-verify Do not run before-commit hooks |
| 2495 | +** --no-verify-comment Do not validate the check-in comment | |
| 2377 | 2496 | ** --nosign Do not attempt to sign this commit with gpg |
| 2378 | 2497 | ** --nosync Do not auto-sync prior to committing |
| 2379 | 2498 | ** --override-lock Allow a check-in even though parent is locked |
| 2380 | -** --private Do not sync changes and their descendants | |
| 2499 | +** --private Never sync the resulting check-in and make | |
| 2500 | +** all descendants private too. | |
| 2381 | 2501 | ** --proxy PROXY Use PROXY as http proxy during sync operation |
| 2382 | -** --tag TAG-NAME Assign given tag TAG-NAME to the check-in | |
| 2502 | +** --tag TAG-NAME Add TAG-NAME to the check-in. May be repeated. | |
| 2383 | 2503 | ** --trace Debug tracing |
| 2384 | -** --user-override USER USER to use instead of the current default | |
| 2385 | -** | |
| 2386 | -** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in | |
| 2387 | -** year-month-day form, it may be truncated, the "T" may be replaced by | |
| 2388 | -** a space, and it may also name a timezone offset from UTC as "-HH:MM" | |
| 2389 | -** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" | |
| 2390 | -** means UTC. | |
| 2504 | +** --user-override USER Record USER as the login that created the | |
| 2505 | +** new check-in, rather that the current user. | |
| 2391 | 2506 | ** |
| 2392 | 2507 | ** See also: [[branch]], [[changes]], [[update]], [[extras]], [[sync]] |
| 2393 | 2508 | */ |
| 2394 | 2509 | void commit_cmd(void){ |
| 2395 | 2510 | int hasChanges; /* True if unsaved changes exist */ |
| @@ -2415,10 +2530,11 @@ | ||
| 2415 | 2530 | int allowConflict = 0; /* Allow unresolve merge conflicts */ |
| 2416 | 2531 | int allowEmpty = 0; /* Allow a commit with no changes */ |
| 2417 | 2532 | int onlyIfChanges = 0; /* No-op if there are no changes */ |
| 2418 | 2533 | int allowFork = 0; /* Allow the commit to fork */ |
| 2419 | 2534 | int allowOlder = 0; /* Allow a commit older than its ancestor */ |
| 2535 | + int noVerifyCom = 0; /* Allow suspicious check-in comments */ | |
| 2420 | 2536 | char *zManifestFile; /* Name of the manifest file */ |
| 2421 | 2537 | int useCksum; /* True if checksums should be computed and verified */ |
| 2422 | 2538 | int outputManifest; /* True to output "manifest" and "manifest.uuid" */ |
| 2423 | 2539 | int dryRunFlag; /* True for a test run. Debugging only */ |
| 2424 | 2540 | CheckinInfo sCiInfo; /* Information about this check-in */ |
| @@ -2440,10 +2556,11 @@ | ||
| 2440 | 2556 | int bRecheck = 0; /* Repeat fork and closed-branch checks*/ |
| 2441 | 2557 | int bIgnoreSkew = 0; /* --ignore-clock-skew flag */ |
| 2442 | 2558 | int mxSize; |
| 2443 | 2559 | char *zCurBranch = 0; /* The current branch name of checkout */ |
| 2444 | 2560 | char *zNewBranch = 0; /* The branch name after update */ |
| 2561 | + int ckComFlgs; /* Flags passed to suspicious_comment() */ | |
| 2445 | 2562 | |
| 2446 | 2563 | memset(&sCiInfo, 0, sizeof(sCiInfo)); |
| 2447 | 2564 | url_proxy_options(); |
| 2448 | 2565 | /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */ |
| 2449 | 2566 | useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0; |
| @@ -2470,24 +2587,30 @@ | ||
| 2470 | 2587 | } |
| 2471 | 2588 | zComment = find_option("comment","m",1); |
| 2472 | 2589 | forceFlag = find_option("force", "f", 0)!=0; |
| 2473 | 2590 | allowConflict = find_option("allow-conflict",0,0)!=0; |
| 2474 | 2591 | allowEmpty = find_option("allow-empty",0,0)!=0; |
| 2592 | + noVerifyCom = find_option("no-verify-comment",0,0)!=0; | |
| 2475 | 2593 | onlyIfChanges = find_option("if-changes",0,0)!=0; |
| 2476 | 2594 | allowFork = find_option("allow-fork",0,0)!=0; |
| 2477 | 2595 | if( find_option("override-lock",0,0)!=0 ) allowFork = 1; |
| 2478 | 2596 | allowOlder = find_option("allow-older",0,0)!=0; |
| 2479 | 2597 | noPrompt = find_option("no-prompt", 0, 0)!=0; |
| 2480 | 2598 | noWarningFlag = find_option("no-warnings", 0, 0)!=0; |
| 2481 | 2599 | noVerify = find_option("no-verify",0,0)!=0; |
| 2482 | 2600 | bTrace = find_option("trace",0,0)!=0; |
| 2483 | 2601 | sCiInfo.zBranch = find_option("branch","b",1); |
| 2484 | - sCiInfo.zColor = find_option("bgcolor",0,1); | |
| 2485 | - sCiInfo.zBrClr = find_option("branchcolor",0,1); | |
| 2602 | + | |
| 2603 | + /* NB: the --bgcolor and --branchcolor flags still work, but are | |
| 2604 | + ** now undocumented, to discourage their use. --mimetype has never | |
| 2605 | + ** been used for anything, so also leave it undocumented */ | |
| 2606 | + sCiInfo.zColor = find_option("bgcolor",0,1); /* Deprecated, undocumented*/ | |
| 2607 | + sCiInfo.zBrClr = find_option("branchcolor",0,1); /* Deprecated, undocumented*/ | |
| 2608 | + sCiInfo.zMimetype = find_option("mimetype",0,1); /* Deprecated, undocumented*/ | |
| 2609 | + | |
| 2486 | 2610 | sCiInfo.closeFlag = find_option("close",0,0)!=0; |
| 2487 | 2611 | sCiInfo.integrateFlag = find_option("integrate",0,0)!=0; |
| 2488 | - sCiInfo.zMimetype = find_option("mimetype",0,1); | |
| 2489 | 2612 | sCiInfo.verboseFlag = find_option("verbose", "v", 0)!=0; |
| 2490 | 2613 | while( (zTag = find_option("tag",0,1))!=0 ){ |
| 2491 | 2614 | if( zTag[0]==0 ) continue; |
| 2492 | 2615 | sCiInfo.azTag = fossil_realloc((void*)sCiInfo.azTag, |
| 2493 | 2616 | sizeof(char*)*(nTag+2)); |
| @@ -2499,14 +2622,20 @@ | ||
| 2499 | 2622 | sCiInfo.zUserOvrd = find_option("user-override",0,1); |
| 2500 | 2623 | noSign = db_get_boolean("omitsign", 0)|noSign; |
| 2501 | 2624 | if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } |
| 2502 | 2625 | useCksum = db_get_boolean("repo-cksum", 1); |
| 2503 | 2626 | bIgnoreSkew = find_option("ignore-clock-skew",0,0)!=0; |
| 2504 | - outputManifest = db_get_manifest_setting(); | |
| 2627 | + outputManifest = db_get_manifest_setting(0); | |
| 2505 | 2628 | mxSize = db_large_file_size(); |
| 2506 | 2629 | if( find_option("ignore-oversize",0,0)!=0 ) mxSize = 0; |
| 2507 | 2630 | verify_all_options(); |
| 2631 | + | |
| 2632 | + /* The --no-warnings flag and the --force flag each imply | |
| 2633 | + ** the --no-verify-comment flag */ | |
| 2634 | + if( noWarningFlag || forceFlag ){ | |
| 2635 | + noVerifyCom = 1; | |
| 2636 | + } | |
| 2508 | 2637 | |
| 2509 | 2638 | /* Get the ID of the parent manifest artifact */ |
| 2510 | 2639 | vid = db_lget_int("checkout", 0); |
| 2511 | 2640 | if( vid==0 ){ |
| 2512 | 2641 | useCksum = 1; |
| @@ -2607,33 +2736,35 @@ | ||
| 2607 | 2736 | fossil_exit(1); |
| 2608 | 2737 | } |
| 2609 | 2738 | } |
| 2610 | 2739 | |
| 2611 | 2740 | /* So that older versions of Fossil (that do not understand delta- |
| 2612 | - ** manifest) can continue to use this repository, do not create a new | |
| 2741 | + ** manifest) can continue to use this repository, and because | |
| 2742 | + ** delta manifests are usually a bad idea unless the repository | |
| 2743 | + ** has a really large number of files, do not create a new | |
| 2613 | 2744 | ** delta-manifest unless this repository already contains one or more |
| 2614 | 2745 | ** delta-manifests, or unless the delta-manifest is explicitly requested |
| 2615 | 2746 | ** by the --delta option. |
| 2616 | 2747 | ** |
| 2617 | - ** The forbid-delta-manifests setting prevents new delta manifests. | |
| 2748 | + ** The forbid-delta-manifests setting prevents new delta manifests, | |
| 2749 | + ** even if the --delta option is used. | |
| 2618 | 2750 | ** |
| 2619 | 2751 | ** If the remote repository sent an avoid-delta-manifests pragma on |
| 2620 | - ** the autosync above, then also try to avoid deltas, unless the | |
| 2752 | + ** the autosync above, then also forbid delta manifests, even if the | |
| 2621 | 2753 | ** --delta option is specified. The remote repo will send the |
| 2622 | - ** avoid-delta-manifests pragma if it has its "forbid-delta-manifests" | |
| 2623 | - ** setting enabled. | |
| 2754 | + ** avoid-delta-manifests pragma if its "forbid-delta-manifests" | |
| 2755 | + ** setting is enabled. | |
| 2624 | 2756 | */ |
| 2625 | - if( !db_get_boolean("seen-delta-manifest",0) | |
| 2757 | + if( !(forceDelta || db_get_boolean("seen-delta-manifest",0)) | |
| 2626 | 2758 | || db_get_boolean("forbid-delta-manifests",0) |
| 2627 | 2759 | || g.bAvoidDeltaManifests |
| 2628 | 2760 | ){ |
| 2629 | - if( !forceDelta ) forceBaseline = 1; | |
| 2761 | + forceBaseline = 1; | |
| 2630 | 2762 | } |
| 2631 | - | |
| 2632 | 2763 | |
| 2633 | 2764 | /* Require confirmation to continue with the check-in if there is |
| 2634 | - ** clock skew | |
| 2765 | + ** clock skew. This helps to prevent timewarps. | |
| 2635 | 2766 | */ |
| 2636 | 2767 | if( g.clockSkewSeen ){ |
| 2637 | 2768 | if( bIgnoreSkew!=0 ){ |
| 2638 | 2769 | cReply = 'y'; |
| 2639 | 2770 | fossil_warning("Clock skew ignored due to --ignore-clock-skew."); |
| @@ -2788,35 +2919,86 @@ | ||
| 2788 | 2919 | fossil_free(zNewBranch); |
| 2789 | 2920 | |
| 2790 | 2921 | /* Always exit the loop on the second pass */ |
| 2791 | 2922 | if( bRecheck ) break; |
| 2792 | 2923 | |
| 2924 | + | |
| 2925 | + /* Figure out how much comment verification is requested */ | |
| 2926 | + if( noVerifyCom ){ | |
| 2927 | + ckComFlgs = 0; | |
| 2928 | + }else{ | |
| 2929 | + const char *zVerComs = db_get("verify-comments","on"); | |
| 2930 | + if( is_false(zVerComs) ){ | |
| 2931 | + ckComFlgs = 0; | |
| 2932 | + }else if( strcmp(zVerComs,"preview")==0 ){ | |
| 2933 | + ckComFlgs = COMCK_PREVIEW | COMCK_LINKS | COMCK_MARKUP; | |
| 2934 | + }else if( strcmp(zVerComs,"links")==0 ){ | |
| 2935 | + ckComFlgs = COMCK_LINKS; | |
| 2936 | + }else{ | |
| 2937 | + ckComFlgs = COMCK_LINKS | COMCK_MARKUP; | |
| 2938 | + } | |
| 2939 | + } | |
| 2793 | 2940 | |
| 2794 | 2941 | /* Get the check-in comment. This might involve prompting the |
| 2795 | 2942 | ** user for the check-in comment, in which case we should resync |
| 2796 | 2943 | ** to renew the check-in lock and repeat the checks for conflicts. |
| 2797 | 2944 | */ |
| 2798 | 2945 | if( zComment ){ |
| 2799 | 2946 | blob_zero(&comment); |
| 2800 | 2947 | blob_append(&comment, zComment, -1); |
| 2948 | + ckComFlgs &= ~(COMCK_PREVIEW|COMCK_MARKUP); | |
| 2949 | + ckComFlgs |= COMCK_NOPREVIEW; | |
| 2950 | + if( suspicious_comment(&comment, ckComFlgs) ){ | |
| 2951 | + fossil_fatal("Commit aborted; " | |
| 2952 | + "use --no-verify-comment to override"); | |
| 2953 | + } | |
| 2801 | 2954 | }else if( zComFile ){ |
| 2802 | 2955 | blob_zero(&comment); |
| 2803 | 2956 | blob_read_from_file(&comment, zComFile, ExtFILE); |
| 2804 | 2957 | blob_to_utf8_no_bom(&comment, 1); |
| 2958 | + ckComFlgs &= ~(COMCK_PREVIEW|COMCK_MARKUP); | |
| 2959 | + ckComFlgs |= COMCK_NOPREVIEW; | |
| 2960 | + if( suspicious_comment(&comment, ckComFlgs) ){ | |
| 2961 | + fossil_fatal("Commit aborted; " | |
| 2962 | + "use --no-verify-comment to override"); | |
| 2963 | + } | |
| 2805 | 2964 | }else if( !noPrompt ){ |
| 2806 | - char *zInit = db_text(0,"SELECT value FROM vvar WHERE name='ci-comment'"); | |
| 2807 | - prepare_commit_comment(&comment, zInit, &sCiInfo, vid, dryRunFlag); | |
| 2808 | - if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){ | |
| 2809 | - prompt_user("unchanged check-in comment. continue (y/N)? ", &ans); | |
| 2810 | - cReply = blob_str(&ans)[0]; | |
| 2811 | - blob_reset(&ans); | |
| 2812 | - if( cReply!='y' && cReply!='Y' ){ | |
| 2813 | - fossil_fatal("Commit aborted."); | |
| 2814 | - } | |
| 2815 | - } | |
| 2816 | - free(zInit); | |
| 2817 | - db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment); | |
| 2965 | + while( 1/*exit-by-break*/ ){ | |
| 2966 | + int rc; | |
| 2967 | + char *zInit; | |
| 2968 | + zInit = db_text(0,"SELECT value FROM vvar WHERE name='ci-comment'"); | |
| 2969 | + prepare_commit_comment(&comment, zInit, &sCiInfo, vid, dryRunFlag); | |
| 2970 | + db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment); | |
| 2971 | + if( (rc = suspicious_comment(&comment, ckComFlgs))!=0 ){ | |
| 2972 | + if( rc==COMCK_PREVIEW ){ | |
| 2973 | + prompt_user("\nContinue (Y/n/e=edit)? ", &ans); | |
| 2974 | + }else{ | |
| 2975 | + prompt_user("\nContinue (y/n/E=edit)? ", &ans); | |
| 2976 | + } | |
| 2977 | + cReply = blob_str(&ans)[0]; | |
| 2978 | + cReply = fossil_tolower(cReply); | |
| 2979 | + blob_reset(&ans); | |
| 2980 | + if( cReply=='n' ){ | |
| 2981 | + fossil_fatal("Commit aborted."); | |
| 2982 | + } | |
| 2983 | + if( cReply=='e' || (cReply!='y' && rc!=COMCK_PREVIEW) ){ | |
| 2984 | + fossil_free(zInit); | |
| 2985 | + continue; | |
| 2986 | + } | |
| 2987 | + } | |
| 2988 | + if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){ | |
| 2989 | + prompt_user("unchanged check-in comment. continue (y/N)? ", &ans); | |
| 2990 | + cReply = blob_str(&ans)[0]; | |
| 2991 | + blob_reset(&ans); | |
| 2992 | + if( cReply!='y' && cReply!='Y' ){ | |
| 2993 | + fossil_fatal("Commit aborted."); | |
| 2994 | + } | |
| 2995 | + } | |
| 2996 | + fossil_free(zInit); | |
| 2997 | + break; | |
| 2998 | + } | |
| 2999 | + | |
| 2818 | 3000 | db_end_transaction(0); |
| 2819 | 3001 | db_begin_transaction(); |
| 2820 | 3002 | if( !g.markPrivate && vid!=0 && !allowFork && !forceFlag ){ |
| 2821 | 3003 | /* Do another auto-pull, renewing the check-in lock. Then set |
| 2822 | 3004 | ** bRecheck so that we loop back above to verify that the check-in |
| 2823 | 3005 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -2281,42 +2281,165 @@ | |
| 2281 | static int tagCmp(const void *a, const void *b){ |
| 2282 | char **pA = (char**)a; |
| 2283 | char **pB = (char**)b; |
| 2284 | return fossil_strcmp(pA[0], pB[0]); |
| 2285 | } |
| 2286 | |
| 2287 | /* |
| 2288 | ** COMMAND: ci# |
| 2289 | ** COMMAND: commit |
| 2290 | ** |
| 2291 | ** Usage: %fossil commit ?OPTIONS? ?FILE...? |
| 2292 | ** or: %fossil ci ?OPTIONS? ?FILE...? |
| 2293 | ** |
| 2294 | ** Create a new version containing all of the changes in the current |
| 2295 | ** check-out. You will be prompted to enter a check-in comment unless |
| 2296 | ** the comment has been specified on the command-line using "-m" or a |
| 2297 | ** file containing the comment using -M. The editor defined in the |
| 2298 | ** "editor" fossil option (see %fossil help set) will be used, or from |
| 2299 | ** the "VISUAL" or "EDITOR" environment variables (in that order) if |
| 2300 | ** no editor is set. |
| 2301 | ** |
| 2302 | ** All files that have changed will be committed unless some subset of |
| 2303 | ** files is specified on the command line. |
| 2304 | ** |
| 2305 | ** The --branch option followed by a branch name causes the new |
| 2306 | ** check-in to be placed in a newly-created branch with the name |
| 2307 | ** passed to the --branch option. |
| 2308 | ** |
| 2309 | ** Use the --branchcolor option followed by a color name (ex: |
| 2310 | ** '#ffc0c0') to specify the background color of entries in the new |
| 2311 | ** branch when shown in the web timeline interface. The use of |
| 2312 | ** the --branchcolor option is not recommended. Instead, let Fossil |
| 2313 | ** choose the branch color automatically. |
| 2314 | ** |
| 2315 | ** The --bgcolor option works like --branchcolor but only sets the |
| 2316 | ** background color for a single check-in. Subsequent check-ins revert |
| 2317 | ** to the default color. |
| 2318 | ** |
| 2319 | ** A check-in is not permitted to fork unless the --allow-fork option |
| 2320 | ** appears. An empty check-in (i.e. with nothing changed) is not |
| 2321 | ** allowed unless the --allow-empty option appears. A check-in may not |
| 2322 | ** be older than its ancestor unless the --allow-older option appears. |
| @@ -2328,68 +2451,60 @@ | |
| 2328 | ** unless the interactive user chooses to proceed. If there is no |
| 2329 | ** interactive user or these warnings should be skipped for some other |
| 2330 | ** reason, the --no-warnings option may be used. A check-in is not |
| 2331 | ** allowed against a closed leaf. |
| 2332 | ** |
| 2333 | ** If a commit message is blank, you will be prompted: |
| 2334 | ** ("continue (y/N)?") to confirm you really want to commit with a |
| 2335 | ** blank commit message. The default value is "N", do not commit. |
| 2336 | ** |
| 2337 | ** The --private option creates a private check-in that is never synced. |
| 2338 | ** Children of private check-ins are automatically private. |
| 2339 | ** |
| 2340 | ** The --tag option applies the symbolic tag name to the check-in. |
| 2341 | ** |
| 2342 | ** The --hash option detects edited files by computing each file's |
| 2343 | ** artifact hash rather than just checking for changes to its size or mtime. |
| 2344 | ** |
| 2345 | ** Options: |
| 2346 | ** --allow-conflict Allow unresolved merge conflicts |
| 2347 | ** --allow-empty Allow a commit with no changes |
| 2348 | ** --allow-fork Allow the commit to fork |
| 2349 | ** --allow-older Allow a commit older than its ancestor |
| 2350 | ** --baseline Use a baseline manifest in the commit process |
| 2351 | ** --bgcolor COLOR Apply COLOR to this one check-in only |
| 2352 | ** --branch NEW-BRANCH-NAME Check in to this new branch |
| 2353 | ** --branchcolor COLOR Apply given COLOR to the branch |
| 2354 | ** --close Close the branch being committed |
| 2355 | ** --date-override DATETIME DATE to use instead of 'now' |
| 2356 | ** --delta Use a delta manifest in the commit process |
| 2357 | ** --hash Verify file status using hashing rather |
| 2358 | ** than relying on file mtimes |
| 2359 | ** --if-changes Make this command a silent no-op if there |
| 2360 | ** are no changes |
| 2361 | ** --ignore-clock-skew If a clock skew is detected, ignore it and |
| 2362 | ** behave as if the user had entered 'yes' to |
| 2363 | ** the question of whether to proceed despite |
| 2364 | ** the skew. |
| 2365 | ** --ignore-oversize Do not warn the user about oversized files |
| 2366 | ** --integrate Close all merged-in branches |
| 2367 | ** -m|--comment COMMENT-TEXT Use COMMENT-TEXT as commit comment |
| 2368 | ** -M|--message-file FILE Read the commit comment from given file |
| 2369 | ** --mimetype MIMETYPE Mimetype of check-in comment |
| 2370 | ** -n|--dry-run If given, display instead of run actions |
| 2371 | ** -v|--verbose Show a diff in the commit message prompt |
| 2372 | ** --no-prompt This option disables prompting the user for |
| 2373 | ** input and assumes an answer of 'No' for every |
| 2374 | ** question. |
| 2375 | ** --no-warnings Omit all warnings about file contents |
| 2376 | ** --no-verify Do not run before-commit hooks |
| 2377 | ** --nosign Do not attempt to sign this commit with gpg |
| 2378 | ** --nosync Do not auto-sync prior to committing |
| 2379 | ** --override-lock Allow a check-in even though parent is locked |
| 2380 | ** --private Do not sync changes and their descendants |
| 2381 | ** --proxy PROXY Use PROXY as http proxy during sync operation |
| 2382 | ** --tag TAG-NAME Assign given tag TAG-NAME to the check-in |
| 2383 | ** --trace Debug tracing |
| 2384 | ** --user-override USER USER to use instead of the current default |
| 2385 | ** |
| 2386 | ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in |
| 2387 | ** year-month-day form, it may be truncated, the "T" may be replaced by |
| 2388 | ** a space, and it may also name a timezone offset from UTC as "-HH:MM" |
| 2389 | ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" |
| 2390 | ** means UTC. |
| 2391 | ** |
| 2392 | ** See also: [[branch]], [[changes]], [[update]], [[extras]], [[sync]] |
| 2393 | */ |
| 2394 | void commit_cmd(void){ |
| 2395 | int hasChanges; /* True if unsaved changes exist */ |
| @@ -2415,10 +2530,11 @@ | |
| 2415 | int allowConflict = 0; /* Allow unresolve merge conflicts */ |
| 2416 | int allowEmpty = 0; /* Allow a commit with no changes */ |
| 2417 | int onlyIfChanges = 0; /* No-op if there are no changes */ |
| 2418 | int allowFork = 0; /* Allow the commit to fork */ |
| 2419 | int allowOlder = 0; /* Allow a commit older than its ancestor */ |
| 2420 | char *zManifestFile; /* Name of the manifest file */ |
| 2421 | int useCksum; /* True if checksums should be computed and verified */ |
| 2422 | int outputManifest; /* True to output "manifest" and "manifest.uuid" */ |
| 2423 | int dryRunFlag; /* True for a test run. Debugging only */ |
| 2424 | CheckinInfo sCiInfo; /* Information about this check-in */ |
| @@ -2440,10 +2556,11 @@ | |
| 2440 | int bRecheck = 0; /* Repeat fork and closed-branch checks*/ |
| 2441 | int bIgnoreSkew = 0; /* --ignore-clock-skew flag */ |
| 2442 | int mxSize; |
| 2443 | char *zCurBranch = 0; /* The current branch name of checkout */ |
| 2444 | char *zNewBranch = 0; /* The branch name after update */ |
| 2445 | |
| 2446 | memset(&sCiInfo, 0, sizeof(sCiInfo)); |
| 2447 | url_proxy_options(); |
| 2448 | /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */ |
| 2449 | useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0; |
| @@ -2470,24 +2587,30 @@ | |
| 2470 | } |
| 2471 | zComment = find_option("comment","m",1); |
| 2472 | forceFlag = find_option("force", "f", 0)!=0; |
| 2473 | allowConflict = find_option("allow-conflict",0,0)!=0; |
| 2474 | allowEmpty = find_option("allow-empty",0,0)!=0; |
| 2475 | onlyIfChanges = find_option("if-changes",0,0)!=0; |
| 2476 | allowFork = find_option("allow-fork",0,0)!=0; |
| 2477 | if( find_option("override-lock",0,0)!=0 ) allowFork = 1; |
| 2478 | allowOlder = find_option("allow-older",0,0)!=0; |
| 2479 | noPrompt = find_option("no-prompt", 0, 0)!=0; |
| 2480 | noWarningFlag = find_option("no-warnings", 0, 0)!=0; |
| 2481 | noVerify = find_option("no-verify",0,0)!=0; |
| 2482 | bTrace = find_option("trace",0,0)!=0; |
| 2483 | sCiInfo.zBranch = find_option("branch","b",1); |
| 2484 | sCiInfo.zColor = find_option("bgcolor",0,1); |
| 2485 | sCiInfo.zBrClr = find_option("branchcolor",0,1); |
| 2486 | sCiInfo.closeFlag = find_option("close",0,0)!=0; |
| 2487 | sCiInfo.integrateFlag = find_option("integrate",0,0)!=0; |
| 2488 | sCiInfo.zMimetype = find_option("mimetype",0,1); |
| 2489 | sCiInfo.verboseFlag = find_option("verbose", "v", 0)!=0; |
| 2490 | while( (zTag = find_option("tag",0,1))!=0 ){ |
| 2491 | if( zTag[0]==0 ) continue; |
| 2492 | sCiInfo.azTag = fossil_realloc((void*)sCiInfo.azTag, |
| 2493 | sizeof(char*)*(nTag+2)); |
| @@ -2499,14 +2622,20 @@ | |
| 2499 | sCiInfo.zUserOvrd = find_option("user-override",0,1); |
| 2500 | noSign = db_get_boolean("omitsign", 0)|noSign; |
| 2501 | if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } |
| 2502 | useCksum = db_get_boolean("repo-cksum", 1); |
| 2503 | bIgnoreSkew = find_option("ignore-clock-skew",0,0)!=0; |
| 2504 | outputManifest = db_get_manifest_setting(); |
| 2505 | mxSize = db_large_file_size(); |
| 2506 | if( find_option("ignore-oversize",0,0)!=0 ) mxSize = 0; |
| 2507 | verify_all_options(); |
| 2508 | |
| 2509 | /* Get the ID of the parent manifest artifact */ |
| 2510 | vid = db_lget_int("checkout", 0); |
| 2511 | if( vid==0 ){ |
| 2512 | useCksum = 1; |
| @@ -2607,33 +2736,35 @@ | |
| 2607 | fossil_exit(1); |
| 2608 | } |
| 2609 | } |
| 2610 | |
| 2611 | /* So that older versions of Fossil (that do not understand delta- |
| 2612 | ** manifest) can continue to use this repository, do not create a new |
| 2613 | ** delta-manifest unless this repository already contains one or more |
| 2614 | ** delta-manifests, or unless the delta-manifest is explicitly requested |
| 2615 | ** by the --delta option. |
| 2616 | ** |
| 2617 | ** The forbid-delta-manifests setting prevents new delta manifests. |
| 2618 | ** |
| 2619 | ** If the remote repository sent an avoid-delta-manifests pragma on |
| 2620 | ** the autosync above, then also try to avoid deltas, unless the |
| 2621 | ** --delta option is specified. The remote repo will send the |
| 2622 | ** avoid-delta-manifests pragma if it has its "forbid-delta-manifests" |
| 2623 | ** setting enabled. |
| 2624 | */ |
| 2625 | if( !db_get_boolean("seen-delta-manifest",0) |
| 2626 | || db_get_boolean("forbid-delta-manifests",0) |
| 2627 | || g.bAvoidDeltaManifests |
| 2628 | ){ |
| 2629 | if( !forceDelta ) forceBaseline = 1; |
| 2630 | } |
| 2631 | |
| 2632 | |
| 2633 | /* Require confirmation to continue with the check-in if there is |
| 2634 | ** clock skew |
| 2635 | */ |
| 2636 | if( g.clockSkewSeen ){ |
| 2637 | if( bIgnoreSkew!=0 ){ |
| 2638 | cReply = 'y'; |
| 2639 | fossil_warning("Clock skew ignored due to --ignore-clock-skew."); |
| @@ -2788,35 +2919,86 @@ | |
| 2788 | fossil_free(zNewBranch); |
| 2789 | |
| 2790 | /* Always exit the loop on the second pass */ |
| 2791 | if( bRecheck ) break; |
| 2792 | |
| 2793 | |
| 2794 | /* Get the check-in comment. This might involve prompting the |
| 2795 | ** user for the check-in comment, in which case we should resync |
| 2796 | ** to renew the check-in lock and repeat the checks for conflicts. |
| 2797 | */ |
| 2798 | if( zComment ){ |
| 2799 | blob_zero(&comment); |
| 2800 | blob_append(&comment, zComment, -1); |
| 2801 | }else if( zComFile ){ |
| 2802 | blob_zero(&comment); |
| 2803 | blob_read_from_file(&comment, zComFile, ExtFILE); |
| 2804 | blob_to_utf8_no_bom(&comment, 1); |
| 2805 | }else if( !noPrompt ){ |
| 2806 | char *zInit = db_text(0,"SELECT value FROM vvar WHERE name='ci-comment'"); |
| 2807 | prepare_commit_comment(&comment, zInit, &sCiInfo, vid, dryRunFlag); |
| 2808 | if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){ |
| 2809 | prompt_user("unchanged check-in comment. continue (y/N)? ", &ans); |
| 2810 | cReply = blob_str(&ans)[0]; |
| 2811 | blob_reset(&ans); |
| 2812 | if( cReply!='y' && cReply!='Y' ){ |
| 2813 | fossil_fatal("Commit aborted."); |
| 2814 | } |
| 2815 | } |
| 2816 | free(zInit); |
| 2817 | db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment); |
| 2818 | db_end_transaction(0); |
| 2819 | db_begin_transaction(); |
| 2820 | if( !g.markPrivate && vid!=0 && !allowFork && !forceFlag ){ |
| 2821 | /* Do another auto-pull, renewing the check-in lock. Then set |
| 2822 | ** bRecheck so that we loop back above to verify that the check-in |
| 2823 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -2281,42 +2281,165 @@ | |
| 2281 | static int tagCmp(const void *a, const void *b){ |
| 2282 | char **pA = (char**)a; |
| 2283 | char **pB = (char**)b; |
| 2284 | return fossil_strcmp(pA[0], pB[0]); |
| 2285 | } |
| 2286 | |
| 2287 | /* |
| 2288 | ** SETTING: verify-comments width=8 default=on |
| 2289 | ** |
| 2290 | ** This setting determines how much sanity checking, if any, the |
| 2291 | ** "fossil commit" and "fossil amend" commands do against check-in |
| 2292 | ** comments. Recognized values: |
| 2293 | ** |
| 2294 | ** on (Default) Check for bad syntax in check-in comments |
| 2295 | ** and offer the user a chance to continue editing for |
| 2296 | ** interactive sessions, or simply abort the commit if |
| 2297 | ** commit was entered using -m or -M |
| 2298 | ** |
| 2299 | ** off Do not do syntax checking of any kind |
| 2300 | ** |
| 2301 | ** links Similar to "on", except only check for bad hyperlinks |
| 2302 | ** |
| 2303 | ** preview Do all the same checks as "on" but also preview the |
| 2304 | ** check-in comment to the user during interactive sessions |
| 2305 | ** and provide an opportunity to accept or re-edit |
| 2306 | */ |
| 2307 | |
| 2308 | #if INTERFACE |
| 2309 | #define COMCK_LINKS 0x01 /* Check for back hyperlinks */ |
| 2310 | #define COMCK_MARKUP 0x02 /* Check markup */ |
| 2311 | #define COMCK_PREVIEW 0x04 /* Always preview, even if no issues found */ |
| 2312 | #define COMCK_NOPREVIEW 0x08 /* Never preview, even for other errors */ |
| 2313 | #endif /* INTERFACE */ |
| 2314 | |
| 2315 | /* |
| 2316 | ** Check for possible formatting errors in the comment string pComment. |
| 2317 | ** |
| 2318 | ** If concerns are found, write a description of the problem(s) to |
| 2319 | ** stdout and return non-zero. The return value is some combination |
| 2320 | ** of the COMCK_* flags, depending on what went wrong. |
| 2321 | ** |
| 2322 | ** If no issues are seen, do not output anything and return zero. |
| 2323 | */ |
| 2324 | int suspicious_comment(Blob *pComment, int mFlags){ |
| 2325 | char *zStart = blob_str(pComment); |
| 2326 | char *z; |
| 2327 | char *zEnd, *zEnd2; |
| 2328 | char *zSep; |
| 2329 | char cSave1; |
| 2330 | int nIssue = 0; |
| 2331 | int rc = mFlags & COMCK_PREVIEW; |
| 2332 | Blob out; |
| 2333 | static const char zSpecial[] = "\\&<*_`["; |
| 2334 | |
| 2335 | if( mFlags==0 ) return 0; |
| 2336 | z = zStart; |
| 2337 | blob_init(&out, 0, 0); |
| 2338 | if( mFlags & COMCK_LINKS ){ |
| 2339 | while( (z = strchr(z,'['))!=0 ){ |
| 2340 | zEnd = strchr(z,']'); |
| 2341 | if( zEnd==0 ){ |
| 2342 | blob_appendf(&out,"\n (%d) ", ++nIssue); |
| 2343 | blob_appendf(&out, "Unterminated hyperlink \"%.12s...\"", z); |
| 2344 | break; |
| 2345 | } |
| 2346 | if( zEnd[1]=='(' && (zEnd2 = strchr(zEnd,')'))!=0 ){ |
| 2347 | blob_appendf(&out,"\n (%d) ", ++nIssue); |
| 2348 | blob_appendf(&out, "Markdown hyperlink syntax: %.*s", |
| 2349 | (int)(zEnd2+1-z), z); |
| 2350 | z = zEnd2; |
| 2351 | continue; |
| 2352 | } |
| 2353 | zSep = strchr(z+1,'|'); |
| 2354 | if( zSep==0 || zSep>zEnd ) zSep = zEnd; |
| 2355 | while( zSep>z && fossil_isspace(zSep[-1]) ) zSep--; |
| 2356 | cSave1 = zSep[0]; |
| 2357 | zSep[0] = 0; |
| 2358 | if( !wiki_valid_link_target(z+1) ){ |
| 2359 | blob_appendf(&out,"\n (%d) ", ++nIssue); |
| 2360 | blob_appendf(&out, "Broken hyperlink: [%s]", z+1); |
| 2361 | } |
| 2362 | zSep[0] = cSave1; |
| 2363 | z = zEnd; |
| 2364 | } |
| 2365 | } |
| 2366 | |
| 2367 | if( nIssue>0 |
| 2368 | || (mFlags & COMCK_PREVIEW)!=0 |
| 2369 | || ((mFlags & COMCK_MARKUP)!=0 && strcspn(zStart,zSpecial)<strlen(zStart)) |
| 2370 | ){ |
| 2371 | char zGot[16]; |
| 2372 | int nGot = 0; |
| 2373 | int i; |
| 2374 | if( (mFlags & COMCK_MARKUP)!=0 ){ |
| 2375 | for(i=0; zSpecial[i]; i++){ |
| 2376 | if( strchr(zStart,zSpecial[i]) ) zGot[nGot++] = zSpecial[i]; |
| 2377 | } |
| 2378 | } |
| 2379 | zGot[nGot] = 0; |
| 2380 | if( nGot>0 ) rc |= COMCK_MARKUP; |
| 2381 | if( nGot>0 && nIssue>0 ){ |
| 2382 | blob_appendf(&out,"\n (%d) Comment uses special character%s \"%s\"", |
| 2383 | ++nIssue, (nGot>1 ? "s" : ""), zGot); |
| 2384 | nGot = 0; |
| 2385 | } |
| 2386 | if( nIssue ){ |
| 2387 | rc |= COMCK_LINKS; |
| 2388 | fossil_print( |
| 2389 | "Possible comment formatting error%s:%b\n", |
| 2390 | nIssue>1 ? "s" : "", &out |
| 2391 | ); |
| 2392 | } |
| 2393 | if( (mFlags & COMCK_NOPREVIEW)==0 ){ |
| 2394 | Blob in, html, txt; |
| 2395 | blob_init(&in, blob_str(pComment), -1); |
| 2396 | blob_init(&html, 0, 0); |
| 2397 | blob_init(&txt, 0, 0); |
| 2398 | wiki_convert(&in, &html, WIKI_INLINE); |
| 2399 | html_to_plaintext(blob_str(&html), &txt); |
| 2400 | if( nGot>0 ){ |
| 2401 | fossil_print( |
| 2402 | "The comment uses special character%s \"%s\". " |
| 2403 | "Does it render as you expect?\n\n ", |
| 2404 | (nGot>1 ? "s" : ""), zGot |
| 2405 | ); |
| 2406 | }else{ |
| 2407 | fossil_print("Preview of the check-in comment:\n\n "); |
| 2408 | } |
| 2409 | comment_print(blob_str(&txt), 0, 3, -1, get_comment_format()); |
| 2410 | blob_reset(&in); |
| 2411 | blob_reset(&html); |
| 2412 | blob_reset(&txt); |
| 2413 | } |
| 2414 | } |
| 2415 | blob_reset(&out); |
| 2416 | return rc; |
| 2417 | } |
| 2418 | |
| 2419 | /* |
| 2420 | ** COMMAND: ci# |
| 2421 | ** COMMAND: commit |
| 2422 | ** |
| 2423 | ** Usage: %fossil commit ?OPTIONS? ?FILE...? |
| 2424 | ** or: %fossil ci ?OPTIONS? ?FILE...? |
| 2425 | ** |
| 2426 | ** Create a new check-in containing all of the changes in the current |
| 2427 | ** check-out. All changes are committed unless some subset of files |
| 2428 | ** is specified on the command line, in which case only the named files |
| 2429 | ** become part of the new check-in. |
| 2430 | ** |
| 2431 | ** You will be prompted to enter a check-in comment unless the comment |
| 2432 | ** has been specified on the command-line using "-m" or "-M". The |
| 2433 | ** text editor used is determined by the "editor" setting, or by the |
| 2434 | ** "VISUAL" or "EDITOR" environment variables. Commit message text is |
| 2435 | ** interpreted as fossil-wiki format. Potentially misformatted check-in |
| 2436 | ** comment text is detected and reported unless the --no-verify-comment |
| 2437 | ** option is used. |
| 2438 | ** |
| 2439 | ** The --branch option followed by a branch name causes the new |
| 2440 | ** check-in to be placed in a newly-created branch with name specified. |
| 2441 | ** |
| 2442 | ** A check-in is not permitted to fork unless the --allow-fork option |
| 2443 | ** appears. An empty check-in (i.e. with nothing changed) is not |
| 2444 | ** allowed unless the --allow-empty option appears. A check-in may not |
| 2445 | ** be older than its ancestor unless the --allow-older option appears. |
| @@ -2328,68 +2451,60 @@ | |
| 2451 | ** unless the interactive user chooses to proceed. If there is no |
| 2452 | ** interactive user or these warnings should be skipped for some other |
| 2453 | ** reason, the --no-warnings option may be used. A check-in is not |
| 2454 | ** allowed against a closed leaf. |
| 2455 | ** |
| 2456 | ** The --private option creates a private check-in that is never synced. |
| 2457 | ** Children of private check-ins are automatically private. |
| 2458 | ** |
| 2459 | ** The --tag option applies the symbolic tag name to the check-in. |
| 2460 | ** The --tag option can be repeated to assign multiple tags to a check-in. |
| 2461 | ** For example: "... --tag release --tag version-1.2.3 ..." |
| 2462 | ** |
| 2463 | ** Options: |
| 2464 | ** --allow-conflict Allow unresolved merge conflicts |
| 2465 | ** --allow-empty Allow a commit with no changes |
| 2466 | ** --allow-fork Allow the commit to fork |
| 2467 | ** --allow-older Allow a commit older than its ancestor |
| 2468 | ** --baseline Use a baseline manifest in the commit process |
| 2469 | ** --branch NEW-BRANCH-NAME Check in to this new branch |
| 2470 | ** --close Close the branch being committed |
| 2471 | ** --date-override DATETIME Make DATETIME the time of the check-in. |
| 2472 | ** Useful when importing historical check-ins |
| 2473 | ** from another version control system. |
| 2474 | ** --delta Use a delta manifest in the commit process |
| 2475 | ** --hash Verify file status using hashing rather |
| 2476 | ** than relying on filesystem mtimes |
| 2477 | ** --if-changes Make this command a silent no-op if there |
| 2478 | ** are no changes |
| 2479 | ** --ignore-clock-skew If a clock skew is detected, ignore it and |
| 2480 | ** behave as if the user had entered 'yes' to |
| 2481 | ** the question of whether to proceed despite |
| 2482 | ** the skew. |
| 2483 | ** --ignore-oversize Do not warn the user about oversized files |
| 2484 | ** --integrate Close all merged-in branches |
| 2485 | ** -m|--comment COMMENT-TEXT Use COMMENT-TEXT as the check-in comment |
| 2486 | ** -M|--message-file FILE Read the check-in comment from FILE |
| 2487 | ** -n|--dry-run Do not actually create a new check-in. Just |
| 2488 | ** show what would have happened. For debugging. |
| 2489 | ** -v|--verbose Show a diff in the commit message prompt |
| 2490 | ** --no-prompt This option disables prompting the user for |
| 2491 | ** input and assumes an answer of 'No' for every |
| 2492 | ** question. |
| 2493 | ** --no-warnings Omit all warnings about file contents |
| 2494 | ** --no-verify Do not run before-commit hooks |
| 2495 | ** --no-verify-comment Do not validate the check-in comment |
| 2496 | ** --nosign Do not attempt to sign this commit with gpg |
| 2497 | ** --nosync Do not auto-sync prior to committing |
| 2498 | ** --override-lock Allow a check-in even though parent is locked |
| 2499 | ** --private Never sync the resulting check-in and make |
| 2500 | ** all descendants private too. |
| 2501 | ** --proxy PROXY Use PROXY as http proxy during sync operation |
| 2502 | ** --tag TAG-NAME Add TAG-NAME to the check-in. May be repeated. |
| 2503 | ** --trace Debug tracing |
| 2504 | ** --user-override USER Record USER as the login that created the |
| 2505 | ** new check-in, rather that the current user. |
| 2506 | ** |
| 2507 | ** See also: [[branch]], [[changes]], [[update]], [[extras]], [[sync]] |
| 2508 | */ |
| 2509 | void commit_cmd(void){ |
| 2510 | int hasChanges; /* True if unsaved changes exist */ |
| @@ -2415,10 +2530,11 @@ | |
| 2530 | int allowConflict = 0; /* Allow unresolve merge conflicts */ |
| 2531 | int allowEmpty = 0; /* Allow a commit with no changes */ |
| 2532 | int onlyIfChanges = 0; /* No-op if there are no changes */ |
| 2533 | int allowFork = 0; /* Allow the commit to fork */ |
| 2534 | int allowOlder = 0; /* Allow a commit older than its ancestor */ |
| 2535 | int noVerifyCom = 0; /* Allow suspicious check-in comments */ |
| 2536 | char *zManifestFile; /* Name of the manifest file */ |
| 2537 | int useCksum; /* True if checksums should be computed and verified */ |
| 2538 | int outputManifest; /* True to output "manifest" and "manifest.uuid" */ |
| 2539 | int dryRunFlag; /* True for a test run. Debugging only */ |
| 2540 | CheckinInfo sCiInfo; /* Information about this check-in */ |
| @@ -2440,10 +2556,11 @@ | |
| 2556 | int bRecheck = 0; /* Repeat fork and closed-branch checks*/ |
| 2557 | int bIgnoreSkew = 0; /* --ignore-clock-skew flag */ |
| 2558 | int mxSize; |
| 2559 | char *zCurBranch = 0; /* The current branch name of checkout */ |
| 2560 | char *zNewBranch = 0; /* The branch name after update */ |
| 2561 | int ckComFlgs; /* Flags passed to suspicious_comment() */ |
| 2562 | |
| 2563 | memset(&sCiInfo, 0, sizeof(sCiInfo)); |
| 2564 | url_proxy_options(); |
| 2565 | /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */ |
| 2566 | useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0; |
| @@ -2470,24 +2587,30 @@ | |
| 2587 | } |
| 2588 | zComment = find_option("comment","m",1); |
| 2589 | forceFlag = find_option("force", "f", 0)!=0; |
| 2590 | allowConflict = find_option("allow-conflict",0,0)!=0; |
| 2591 | allowEmpty = find_option("allow-empty",0,0)!=0; |
| 2592 | noVerifyCom = find_option("no-verify-comment",0,0)!=0; |
| 2593 | onlyIfChanges = find_option("if-changes",0,0)!=0; |
| 2594 | allowFork = find_option("allow-fork",0,0)!=0; |
| 2595 | if( find_option("override-lock",0,0)!=0 ) allowFork = 1; |
| 2596 | allowOlder = find_option("allow-older",0,0)!=0; |
| 2597 | noPrompt = find_option("no-prompt", 0, 0)!=0; |
| 2598 | noWarningFlag = find_option("no-warnings", 0, 0)!=0; |
| 2599 | noVerify = find_option("no-verify",0,0)!=0; |
| 2600 | bTrace = find_option("trace",0,0)!=0; |
| 2601 | sCiInfo.zBranch = find_option("branch","b",1); |
| 2602 | |
| 2603 | /* NB: the --bgcolor and --branchcolor flags still work, but are |
| 2604 | ** now undocumented, to discourage their use. --mimetype has never |
| 2605 | ** been used for anything, so also leave it undocumented */ |
| 2606 | sCiInfo.zColor = find_option("bgcolor",0,1); /* Deprecated, undocumented*/ |
| 2607 | sCiInfo.zBrClr = find_option("branchcolor",0,1); /* Deprecated, undocumented*/ |
| 2608 | sCiInfo.zMimetype = find_option("mimetype",0,1); /* Deprecated, undocumented*/ |
| 2609 | |
| 2610 | sCiInfo.closeFlag = find_option("close",0,0)!=0; |
| 2611 | sCiInfo.integrateFlag = find_option("integrate",0,0)!=0; |
| 2612 | sCiInfo.verboseFlag = find_option("verbose", "v", 0)!=0; |
| 2613 | while( (zTag = find_option("tag",0,1))!=0 ){ |
| 2614 | if( zTag[0]==0 ) continue; |
| 2615 | sCiInfo.azTag = fossil_realloc((void*)sCiInfo.azTag, |
| 2616 | sizeof(char*)*(nTag+2)); |
| @@ -2499,14 +2622,20 @@ | |
| 2622 | sCiInfo.zUserOvrd = find_option("user-override",0,1); |
| 2623 | noSign = db_get_boolean("omitsign", 0)|noSign; |
| 2624 | if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } |
| 2625 | useCksum = db_get_boolean("repo-cksum", 1); |
| 2626 | bIgnoreSkew = find_option("ignore-clock-skew",0,0)!=0; |
| 2627 | outputManifest = db_get_manifest_setting(0); |
| 2628 | mxSize = db_large_file_size(); |
| 2629 | if( find_option("ignore-oversize",0,0)!=0 ) mxSize = 0; |
| 2630 | verify_all_options(); |
| 2631 | |
| 2632 | /* The --no-warnings flag and the --force flag each imply |
| 2633 | ** the --no-verify-comment flag */ |
| 2634 | if( noWarningFlag || forceFlag ){ |
| 2635 | noVerifyCom = 1; |
| 2636 | } |
| 2637 | |
| 2638 | /* Get the ID of the parent manifest artifact */ |
| 2639 | vid = db_lget_int("checkout", 0); |
| 2640 | if( vid==0 ){ |
| 2641 | useCksum = 1; |
| @@ -2607,33 +2736,35 @@ | |
| 2736 | fossil_exit(1); |
| 2737 | } |
| 2738 | } |
| 2739 | |
| 2740 | /* So that older versions of Fossil (that do not understand delta- |
| 2741 | ** manifest) can continue to use this repository, and because |
| 2742 | ** delta manifests are usually a bad idea unless the repository |
| 2743 | ** has a really large number of files, do not create a new |
| 2744 | ** delta-manifest unless this repository already contains one or more |
| 2745 | ** delta-manifests, or unless the delta-manifest is explicitly requested |
| 2746 | ** by the --delta option. |
| 2747 | ** |
| 2748 | ** The forbid-delta-manifests setting prevents new delta manifests, |
| 2749 | ** even if the --delta option is used. |
| 2750 | ** |
| 2751 | ** If the remote repository sent an avoid-delta-manifests pragma on |
| 2752 | ** the autosync above, then also forbid delta manifests, even if the |
| 2753 | ** --delta option is specified. The remote repo will send the |
| 2754 | ** avoid-delta-manifests pragma if its "forbid-delta-manifests" |
| 2755 | ** setting is enabled. |
| 2756 | */ |
| 2757 | if( !(forceDelta || db_get_boolean("seen-delta-manifest",0)) |
| 2758 | || db_get_boolean("forbid-delta-manifests",0) |
| 2759 | || g.bAvoidDeltaManifests |
| 2760 | ){ |
| 2761 | forceBaseline = 1; |
| 2762 | } |
| 2763 | |
| 2764 | /* Require confirmation to continue with the check-in if there is |
| 2765 | ** clock skew. This helps to prevent timewarps. |
| 2766 | */ |
| 2767 | if( g.clockSkewSeen ){ |
| 2768 | if( bIgnoreSkew!=0 ){ |
| 2769 | cReply = 'y'; |
| 2770 | fossil_warning("Clock skew ignored due to --ignore-clock-skew."); |
| @@ -2788,35 +2919,86 @@ | |
| 2919 | fossil_free(zNewBranch); |
| 2920 | |
| 2921 | /* Always exit the loop on the second pass */ |
| 2922 | if( bRecheck ) break; |
| 2923 | |
| 2924 | |
| 2925 | /* Figure out how much comment verification is requested */ |
| 2926 | if( noVerifyCom ){ |
| 2927 | ckComFlgs = 0; |
| 2928 | }else{ |
| 2929 | const char *zVerComs = db_get("verify-comments","on"); |
| 2930 | if( is_false(zVerComs) ){ |
| 2931 | ckComFlgs = 0; |
| 2932 | }else if( strcmp(zVerComs,"preview")==0 ){ |
| 2933 | ckComFlgs = COMCK_PREVIEW | COMCK_LINKS | COMCK_MARKUP; |
| 2934 | }else if( strcmp(zVerComs,"links")==0 ){ |
| 2935 | ckComFlgs = COMCK_LINKS; |
| 2936 | }else{ |
| 2937 | ckComFlgs = COMCK_LINKS | COMCK_MARKUP; |
| 2938 | } |
| 2939 | } |
| 2940 | |
| 2941 | /* Get the check-in comment. This might involve prompting the |
| 2942 | ** user for the check-in comment, in which case we should resync |
| 2943 | ** to renew the check-in lock and repeat the checks for conflicts. |
| 2944 | */ |
| 2945 | if( zComment ){ |
| 2946 | blob_zero(&comment); |
| 2947 | blob_append(&comment, zComment, -1); |
| 2948 | ckComFlgs &= ~(COMCK_PREVIEW|COMCK_MARKUP); |
| 2949 | ckComFlgs |= COMCK_NOPREVIEW; |
| 2950 | if( suspicious_comment(&comment, ckComFlgs) ){ |
| 2951 | fossil_fatal("Commit aborted; " |
| 2952 | "use --no-verify-comment to override"); |
| 2953 | } |
| 2954 | }else if( zComFile ){ |
| 2955 | blob_zero(&comment); |
| 2956 | blob_read_from_file(&comment, zComFile, ExtFILE); |
| 2957 | blob_to_utf8_no_bom(&comment, 1); |
| 2958 | ckComFlgs &= ~(COMCK_PREVIEW|COMCK_MARKUP); |
| 2959 | ckComFlgs |= COMCK_NOPREVIEW; |
| 2960 | if( suspicious_comment(&comment, ckComFlgs) ){ |
| 2961 | fossil_fatal("Commit aborted; " |
| 2962 | "use --no-verify-comment to override"); |
| 2963 | } |
| 2964 | }else if( !noPrompt ){ |
| 2965 | while( 1/*exit-by-break*/ ){ |
| 2966 | int rc; |
| 2967 | char *zInit; |
| 2968 | zInit = db_text(0,"SELECT value FROM vvar WHERE name='ci-comment'"); |
| 2969 | prepare_commit_comment(&comment, zInit, &sCiInfo, vid, dryRunFlag); |
| 2970 | db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment); |
| 2971 | if( (rc = suspicious_comment(&comment, ckComFlgs))!=0 ){ |
| 2972 | if( rc==COMCK_PREVIEW ){ |
| 2973 | prompt_user("\nContinue (Y/n/e=edit)? ", &ans); |
| 2974 | }else{ |
| 2975 | prompt_user("\nContinue (y/n/E=edit)? ", &ans); |
| 2976 | } |
| 2977 | cReply = blob_str(&ans)[0]; |
| 2978 | cReply = fossil_tolower(cReply); |
| 2979 | blob_reset(&ans); |
| 2980 | if( cReply=='n' ){ |
| 2981 | fossil_fatal("Commit aborted."); |
| 2982 | } |
| 2983 | if( cReply=='e' || (cReply!='y' && rc!=COMCK_PREVIEW) ){ |
| 2984 | fossil_free(zInit); |
| 2985 | continue; |
| 2986 | } |
| 2987 | } |
| 2988 | if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){ |
| 2989 | prompt_user("unchanged check-in comment. continue (y/N)? ", &ans); |
| 2990 | cReply = blob_str(&ans)[0]; |
| 2991 | blob_reset(&ans); |
| 2992 | if( cReply!='y' && cReply!='Y' ){ |
| 2993 | fossil_fatal("Commit aborted."); |
| 2994 | } |
| 2995 | } |
| 2996 | fossil_free(zInit); |
| 2997 | break; |
| 2998 | } |
| 2999 | |
| 3000 | db_end_transaction(0); |
| 3001 | db_begin_transaction(); |
| 3002 | if( !g.markPrivate && vid!=0 && !allowFork && !forceFlag ){ |
| 3003 | /* Do another auto-pull, renewing the check-in lock. Then set |
| 3004 | ** bRecheck so that we loop back above to verify that the check-in |
| 3005 |
+1
-1
| --- src/checkout.c | ||
| +++ src/checkout.c | ||
| @@ -173,11 +173,11 @@ | ||
| 173 | 173 | */ |
| 174 | 174 | void manifest_to_disk(int vid){ |
| 175 | 175 | char *zManFile; |
| 176 | 176 | int flg; |
| 177 | 177 | |
| 178 | - flg = db_get_manifest_setting(); | |
| 178 | + flg = db_get_manifest_setting(0); | |
| 179 | 179 | |
| 180 | 180 | if( flg & MFESTFLG_RAW ){ |
| 181 | 181 | Blob manifest = BLOB_INITIALIZER; |
| 182 | 182 | content_get(vid, &manifest); |
| 183 | 183 | sterilize_manifest(&manifest, CFTYPE_MANIFEST); |
| 184 | 184 |
| --- src/checkout.c | |
| +++ src/checkout.c | |
| @@ -173,11 +173,11 @@ | |
| 173 | */ |
| 174 | void manifest_to_disk(int vid){ |
| 175 | char *zManFile; |
| 176 | int flg; |
| 177 | |
| 178 | flg = db_get_manifest_setting(); |
| 179 | |
| 180 | if( flg & MFESTFLG_RAW ){ |
| 181 | Blob manifest = BLOB_INITIALIZER; |
| 182 | content_get(vid, &manifest); |
| 183 | sterilize_manifest(&manifest, CFTYPE_MANIFEST); |
| 184 |
| --- src/checkout.c | |
| +++ src/checkout.c | |
| @@ -173,11 +173,11 @@ | |
| 173 | */ |
| 174 | void manifest_to_disk(int vid){ |
| 175 | char *zManFile; |
| 176 | int flg; |
| 177 | |
| 178 | flg = db_get_manifest_setting(0); |
| 179 | |
| 180 | if( flg & MFESTFLG_RAW ){ |
| 181 | Blob manifest = BLOB_INITIALIZER; |
| 182 | content_get(vid, &manifest); |
| 183 | sterilize_manifest(&manifest, CFTYPE_MANIFEST); |
| 184 |
+1
| --- src/configure.c | ||
| +++ src/configure.c | ||
| @@ -891,10 +891,11 @@ | ||
| 891 | 891 | } |
| 892 | 892 | url_parse(zServer, URL_PROMPT_PW|URL_USE_CONFIG); |
| 893 | 893 | if( g.url.protocol==0 ) fossil_fatal("no server URL specified"); |
| 894 | 894 | user_select(); |
| 895 | 895 | url_enable_proxy("via proxy: "); |
| 896 | + g.zHttpAuth = get_httpauth(); | |
| 896 | 897 | if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE; |
| 897 | 898 | if( strncmp(zMethod, "push", n)==0 ){ |
| 898 | 899 | client_sync(0,0,(unsigned)mask,0,0); |
| 899 | 900 | }else if( strncmp(zMethod, "pull", n)==0 ){ |
| 900 | 901 | if( overwriteFlag ) db_unprotect(PROTECT_USER); |
| 901 | 902 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -891,10 +891,11 @@ | |
| 891 | } |
| 892 | url_parse(zServer, URL_PROMPT_PW|URL_USE_CONFIG); |
| 893 | if( g.url.protocol==0 ) fossil_fatal("no server URL specified"); |
| 894 | user_select(); |
| 895 | url_enable_proxy("via proxy: "); |
| 896 | if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE; |
| 897 | if( strncmp(zMethod, "push", n)==0 ){ |
| 898 | client_sync(0,0,(unsigned)mask,0,0); |
| 899 | }else if( strncmp(zMethod, "pull", n)==0 ){ |
| 900 | if( overwriteFlag ) db_unprotect(PROTECT_USER); |
| 901 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -891,10 +891,11 @@ | |
| 891 | } |
| 892 | url_parse(zServer, URL_PROMPT_PW|URL_USE_CONFIG); |
| 893 | if( g.url.protocol==0 ) fossil_fatal("no server URL specified"); |
| 894 | user_select(); |
| 895 | url_enable_proxy("via proxy: "); |
| 896 | g.zHttpAuth = get_httpauth(); |
| 897 | if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE; |
| 898 | if( strncmp(zMethod, "push", n)==0 ){ |
| 899 | client_sync(0,0,(unsigned)mask,0,0); |
| 900 | }else if( strncmp(zMethod, "pull", n)==0 ){ |
| 901 | if( overwriteFlag ) db_unprotect(PROTECT_USER); |
| 902 |
M
src/db.c
+194
-73
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -3204,23 +3204,16 @@ | ||
| 3204 | 3204 | |
| 3205 | 3205 | db_unprotect(PROTECT_ALL); |
| 3206 | 3206 | db_set("content-schema", CONTENT_SCHEMA, 0); |
| 3207 | 3207 | db_set("aux-schema", AUX_SCHEMA_MAX, 0); |
| 3208 | 3208 | db_set("rebuilt", get_version(), 0); |
| 3209 | - db_set("admin-log", "1", 0); | |
| 3210 | - db_set("access-log", "1", 0); | |
| 3211 | 3209 | db_multi_exec( |
| 3212 | 3210 | "INSERT INTO config(name,value,mtime)" |
| 3213 | 3211 | " VALUES('server-code', lower(hex(randomblob(20))),now());" |
| 3214 | 3212 | "INSERT INTO config(name,value,mtime)" |
| 3215 | 3213 | " VALUES('project-code', lower(hex(randomblob(20))),now());" |
| 3216 | 3214 | ); |
| 3217 | - if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0); | |
| 3218 | - if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0); | |
| 3219 | - if( !db_is_global("timeline-plaintext") ){ | |
| 3220 | - db_set_int("timeline-plaintext", 1, 0); | |
| 3221 | - } | |
| 3222 | 3215 | db_create_default_users(0, zDefaultUser); |
| 3223 | 3216 | if( zDefaultUser ) g.zLogin = zDefaultUser; |
| 3224 | 3217 | user_select(); |
| 3225 | 3218 | |
| 3226 | 3219 | if( zTemplate ){ |
| @@ -3647,66 +3640,79 @@ | ||
| 3647 | 3640 | ** |
| 3648 | 3641 | ** If the zNonVersionedSetting parameter is not NULL then it holds the |
| 3649 | 3642 | ** non-versioned value for this setting. If both a versioned and a |
| 3650 | 3643 | ** non-versioned value exist and are not equal, then a warning message |
| 3651 | 3644 | ** might be generated. |
| 3645 | +** | |
| 3646 | +** zCkin is normally NULL. In that case, the versioned setting is | |
| 3647 | +** take from the local check-out, if a local checkout exists, or from | |
| 3648 | +** checkin named by the g.zOpenRevision global variable. If zCkin is | |
| 3649 | +** not NULL, then zCkin is the name of the specific checkin from which | |
| 3650 | +** versioned setting value is taken. When zCkin is not NULL, the cache | |
| 3651 | +** is bypassed. | |
| 3652 | 3652 | */ |
| 3653 | -char *db_get_versioned(const char *zName, char *zNonVersionedSetting){ | |
| 3653 | +char *db_get_versioned( | |
| 3654 | + const char *zName, | |
| 3655 | + char *zNonVersionedSetting, | |
| 3656 | + const char *zCkin | |
| 3657 | +){ | |
| 3654 | 3658 | char *zVersionedSetting = 0; |
| 3655 | 3659 | int noWarn = 0; |
| 3656 | 3660 | int found = 0; |
| 3657 | 3661 | struct _cacheEntry { |
| 3658 | 3662 | struct _cacheEntry *next; |
| 3659 | 3663 | const char *zName, *zValue; |
| 3660 | 3664 | } *cacheEntry = 0; |
| 3661 | 3665 | static struct _cacheEntry *cache = 0; |
| 3662 | 3666 | |
| 3663 | - if( !g.localOpen && g.zOpenRevision==0 ) return zNonVersionedSetting; | |
| 3667 | + if( !g.localOpen && g.zOpenRevision==0 && zCkin==0 ){ | |
| 3668 | + return zNonVersionedSetting; | |
| 3669 | + } | |
| 3670 | + | |
| 3664 | 3671 | /* Look up name in cache */ |
| 3665 | - cacheEntry = cache; | |
| 3666 | - while( cacheEntry!=0 ){ | |
| 3667 | - if( fossil_strcmp(cacheEntry->zName, zName)==0 ){ | |
| 3668 | - zVersionedSetting = fossil_strdup(cacheEntry->zValue); | |
| 3669 | - break; | |
| 3670 | - } | |
| 3671 | - cacheEntry = cacheEntry->next; | |
| 3672 | - } | |
| 3672 | + if( zCkin==0 ){ | |
| 3673 | + cacheEntry = cache; | |
| 3674 | + while( cacheEntry!=0 ){ | |
| 3675 | + if( fossil_strcmp(cacheEntry->zName, zName)==0 ){ | |
| 3676 | + zVersionedSetting = fossil_strdup(cacheEntry->zValue); | |
| 3677 | + break; | |
| 3678 | + } | |
| 3679 | + cacheEntry = cacheEntry->next; | |
| 3680 | + } | |
| 3681 | + } | |
| 3682 | + | |
| 3673 | 3683 | /* Attempt to read value from file in check-out if there wasn't a cache hit.*/ |
| 3674 | 3684 | if( cacheEntry==0 ){ |
| 3675 | 3685 | Blob versionedPathname; |
| 3676 | 3686 | Blob setting; |
| 3677 | - blob_zero(&versionedPathname); | |
| 3678 | - blob_zero(&setting); | |
| 3679 | - blob_appendf(&versionedPathname, "%s.fossil-settings/%s", | |
| 3680 | - g.zLocalRoot, zName); | |
| 3681 | - if( !g.localOpen ){ | |
| 3682 | - /* Repository is in the process of being opened, but files have not been | |
| 3683 | - * written to disk. Load from the database. */ | |
| 3684 | - Blob noWarnFile; | |
| 3685 | - if( historical_blob(g.zOpenRevision, blob_str(&versionedPathname), | |
| 3686 | - &setting, 0) ){ | |
| 3687 | - found = 1; | |
| 3688 | - } | |
| 3689 | - /* See if there's a no-warn flag */ | |
| 3690 | - blob_append(&versionedPathname, ".no-warn", -1); | |
| 3691 | - blob_zero(&noWarnFile); | |
| 3692 | - if( historical_blob(g.zOpenRevision, blob_str(&versionedPathname), | |
| 3693 | - &noWarnFile, 0) ){ | |
| 3694 | - noWarn = 1; | |
| 3695 | - } | |
| 3696 | - blob_reset(&noWarnFile); | |
| 3697 | - }else if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){ | |
| 3698 | - /* File exists, and contains the value for this setting. Load from | |
| 3699 | - ** the file. */ | |
| 3700 | - const char *zFile = blob_str(&versionedPathname); | |
| 3701 | - if( blob_read_from_file(&setting, zFile, ExtFILE)>=0 ){ | |
| 3702 | - found = 1; | |
| 3703 | - } | |
| 3704 | - /* See if there's a no-warn flag */ | |
| 3705 | - blob_append(&versionedPathname, ".no-warn", -1); | |
| 3706 | - if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){ | |
| 3707 | - noWarn = 1; | |
| 3687 | + blob_init(&versionedPathname, 0, 0); | |
| 3688 | + blob_init(&setting, 0, 0); | |
| 3689 | + if( !g.localOpen || zCkin!=0 ){ | |
| 3690 | + /* Repository is in the process of being opened, but files have not been | |
| 3691 | + * written to disk. Load from the database. */ | |
| 3692 | + blob_appendf(&versionedPathname, ".fossil-settings/%s", zName); | |
| 3693 | + if( historical_blob(zCkin ? zCkin : g.zOpenRevision, | |
| 3694 | + blob_str(&versionedPathname), | |
| 3695 | + &setting, 0) | |
| 3696 | + ){ | |
| 3697 | + found = 1; | |
| 3698 | + } | |
| 3699 | + }else{ | |
| 3700 | + blob_appendf(&versionedPathname, "%s.fossil-settings/%s", | |
| 3701 | + g.zLocalRoot, zName); | |
| 3702 | + if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){ | |
| 3703 | + /* File exists, and contains the value for this setting. Load from | |
| 3704 | + ** the file. */ | |
| 3705 | + const char *zFile = blob_str(&versionedPathname); | |
| 3706 | + if( blob_read_from_file(&setting, zFile, ExtFILE)>=0 ){ | |
| 3707 | + found = 1; | |
| 3708 | + } | |
| 3709 | + /* See if there's a no-warn flag */ | |
| 3710 | + blob_append(&versionedPathname, ".no-warn", -1); | |
| 3711 | + if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){ | |
| 3712 | + noWarn = 1; | |
| 3713 | + } | |
| 3708 | 3714 | } |
| 3709 | 3715 | } |
| 3710 | 3716 | blob_reset(&versionedPathname); |
| 3711 | 3717 | if( found ){ |
| 3712 | 3718 | blob_strip_comment_lines(&setting, &setting); |
| @@ -3713,20 +3719,27 @@ | ||
| 3713 | 3719 | blob_trim(&setting); /* Avoid non-obvious problems with line endings |
| 3714 | 3720 | ** on boolean properties */ |
| 3715 | 3721 | zVersionedSetting = fossil_strdup(blob_str(&setting)); |
| 3716 | 3722 | } |
| 3717 | 3723 | blob_reset(&setting); |
| 3724 | + | |
| 3718 | 3725 | /* Store result in cache, which can be the value or 0 if not found */ |
| 3719 | - cacheEntry = (struct _cacheEntry*)fossil_malloc(sizeof(struct _cacheEntry)); | |
| 3720 | - cacheEntry->next = cache; | |
| 3721 | - cacheEntry->zName = zName; | |
| 3722 | - cacheEntry->zValue = fossil_strdup(zVersionedSetting); | |
| 3723 | - cache = cacheEntry; | |
| 3726 | + if( zCkin==0 ){ | |
| 3727 | + cacheEntry = (struct _cacheEntry*)fossil_malloc(sizeof(*cacheEntry)); | |
| 3728 | + cacheEntry->next = cache; | |
| 3729 | + cacheEntry->zName = zName; | |
| 3730 | + cacheEntry->zValue = fossil_strdup(zVersionedSetting); | |
| 3731 | + cache = cacheEntry; | |
| 3732 | + } | |
| 3724 | 3733 | } |
| 3734 | + | |
| 3725 | 3735 | /* Display a warning? */ |
| 3726 | - if( zVersionedSetting!=0 && zNonVersionedSetting!=0 | |
| 3727 | - && zNonVersionedSetting[0]!='\0' && !noWarn | |
| 3736 | + if( zVersionedSetting!=0 | |
| 3737 | + && zNonVersionedSetting!=0 | |
| 3738 | + && zNonVersionedSetting[0]!='\0' | |
| 3739 | + && zCkin==0 | |
| 3740 | + && !noWarn | |
| 3728 | 3741 | ){ |
| 3729 | 3742 | /* There's a versioned setting, and a non-versioned setting. Tell |
| 3730 | 3743 | ** the user about the conflict */ |
| 3731 | 3744 | fossil_warning( |
| 3732 | 3745 | "setting %s has both versioned and non-versioned values: using " |
| @@ -3735,10 +3748,11 @@ | ||
| 3735 | 3748 | "\"%/.fossil-settings/%s.no-warn\" in the check-out root, or delete " |
| 3736 | 3749 | "the non-versioned setting with \"fossil unset %s\")", zName, |
| 3737 | 3750 | g.zLocalRoot, zName, g.zLocalRoot, zName, zName |
| 3738 | 3751 | ); |
| 3739 | 3752 | } |
| 3753 | + | |
| 3740 | 3754 | /* Prefer the versioned setting */ |
| 3741 | 3755 | return ( zVersionedSetting!=0 ) ? zVersionedSetting : zNonVersionedSetting; |
| 3742 | 3756 | } |
| 3743 | 3757 | |
| 3744 | 3758 | |
| @@ -3778,11 +3792,11 @@ | ||
| 3778 | 3792 | } |
| 3779 | 3793 | if( pSetting!=0 && pSetting->versionable ){ |
| 3780 | 3794 | /* This is a versionable setting, try and get the info from a |
| 3781 | 3795 | ** checked-out file */ |
| 3782 | 3796 | char * zZ = z; |
| 3783 | - z = db_get_versioned(zName, z); | |
| 3797 | + z = db_get_versioned(zName, z, 0); | |
| 3784 | 3798 | if(zZ != z){ |
| 3785 | 3799 | fossil_free(zZ); |
| 3786 | 3800 | } |
| 3787 | 3801 | } |
| 3788 | 3802 | if( z==0 ){ |
| @@ -3919,11 +3933,11 @@ | ||
| 3919 | 3933 | } |
| 3920 | 3934 | fossil_free(zVal); |
| 3921 | 3935 | return dflt; |
| 3922 | 3936 | } |
| 3923 | 3937 | int db_get_versioned_boolean(const char *zName, int dflt){ |
| 3924 | - char *zVal = db_get_versioned(zName, 0); | |
| 3938 | + char *zVal = db_get_versioned(zName, 0, 0); | |
| 3925 | 3939 | if( zVal==0 ) return dflt; |
| 3926 | 3940 | if( is_truth(zVal) ) return 1; |
| 3927 | 3941 | if( is_false(zVal) ) return 0; |
| 3928 | 3942 | return dflt; |
| 3929 | 3943 | } |
| @@ -4048,14 +4062,30 @@ | ||
| 4048 | 4062 | ** Get the manifest setting. For backwards compatibility first check if the |
| 4049 | 4063 | ** value is a boolean. If it's not a boolean, treat each character as a flag |
| 4050 | 4064 | ** to enable a manifest type. This system puts certain boundary conditions on |
| 4051 | 4065 | ** which letters can be used to represent flags (any permutation of flags must |
| 4052 | 4066 | ** not be able to fully form one of the boolean values). |
| 4067 | +** | |
| 4068 | +** "manifest" is a versionable setting. But we do not issue a warning | |
| 4069 | +** if there is a conflict. Instead, the value returned is the value for | |
| 4070 | +** the versioned setting if the versioned setting exists, or the ordinary | |
| 4071 | +** setting otherwise. | |
| 4072 | +** | |
| 4073 | +** The argument zCkin is the specific check-in for which we want the | |
| 4074 | +** manifest setting. | |
| 4053 | 4075 | */ |
| 4054 | -int db_get_manifest_setting(void){ | |
| 4076 | +int db_get_manifest_setting(const char *zCkin){ | |
| 4055 | 4077 | int flg; |
| 4056 | - char *zVal = db_get("manifest", 0); | |
| 4078 | + char *zVal; | |
| 4079 | + | |
| 4080 | + /* Look for the versioned setting first */ | |
| 4081 | + zVal = db_get_versioned("manifest", 0, zCkin); | |
| 4082 | + | |
| 4083 | + if( zVal==0 && g.repositoryOpen ){ | |
| 4084 | + /* No versioned setting, look for the repository setting second */ | |
| 4085 | + zVal = db_text(0, "SELECT value FROM config WHERE name='manifest'"); | |
| 4086 | + } | |
| 4057 | 4087 | if( zVal==0 || is_false(zVal) ){ |
| 4058 | 4088 | return 0; |
| 4059 | 4089 | }else if( is_truth(zVal) ){ |
| 4060 | 4090 | return MFESTFLG_RAW|MFESTFLG_UUID; |
| 4061 | 4091 | } |
| @@ -4068,10 +4098,37 @@ | ||
| 4068 | 4098 | } |
| 4069 | 4099 | zVal++; |
| 4070 | 4100 | } |
| 4071 | 4101 | return flg; |
| 4072 | 4102 | } |
| 4103 | + | |
| 4104 | +/* | |
| 4105 | +** COMMAND: test-manifest-setting | |
| 4106 | +** | |
| 4107 | +** Usage: %fossil test-manifest-setting VERSION VERSION ... | |
| 4108 | +** | |
| 4109 | +** Display the value for the "manifest" setting for various versions | |
| 4110 | +** of the repository. | |
| 4111 | +*/ | |
| 4112 | +void test_manfest_setting_cmd(void){ | |
| 4113 | + int i; | |
| 4114 | + db_find_and_open_repository(0, 0); | |
| 4115 | + for(i=2; i<g.argc; i++){ | |
| 4116 | + int m = db_get_manifest_setting(g.argv[i]); | |
| 4117 | + fossil_print("%s:\n", g.argv[i]); | |
| 4118 | + fossil_print(" flags = 0x%02x\n", m); | |
| 4119 | + if( m & MFESTFLG_RAW ){ | |
| 4120 | + fossil_print(" manifest\n"); | |
| 4121 | + } | |
| 4122 | + if( m & MFESTFLG_UUID ){ | |
| 4123 | + fossil_print(" manifest.uuid\n"); | |
| 4124 | + } | |
| 4125 | + if( m & MFESTFLG_TAGS ){ | |
| 4126 | + fossil_print(" manifest.tags\n"); | |
| 4127 | + } | |
| 4128 | + } | |
| 4129 | +} | |
| 4073 | 4130 | |
| 4074 | 4131 | |
| 4075 | 4132 | /* |
| 4076 | 4133 | ** Record the name of a local repository in the global_config() database. |
| 4077 | 4134 | ** The repository filename %s is recorded as an entry with a "name" field |
| @@ -4371,16 +4428,37 @@ | ||
| 4371 | 4428 | } |
| 4372 | 4429 | } |
| 4373 | 4430 | g.argc = 2; |
| 4374 | 4431 | info_cmd(); |
| 4375 | 4432 | } |
| 4433 | + | |
| 4434 | +/* | |
| 4435 | +** Return true if pSetting has its default value assuming its | |
| 4436 | +** current value is zVal. | |
| 4437 | +*/ | |
| 4438 | +int setting_has_default_value(const Setting *pSetting, const char *zVal){ | |
| 4439 | + if( zVal==0 ) return 1; | |
| 4440 | + if( pSetting->def==0 ) return 0; | |
| 4441 | + if( pSetting->width==0 ){ | |
| 4442 | + return is_false(pSetting->def)==is_false(zVal); | |
| 4443 | + } | |
| 4444 | + if( fossil_strcmp(pSetting->def, zVal)==0 ) return 1; | |
| 4445 | + if( is_false(zVal) && is_false(pSetting->def) ) return 1; | |
| 4446 | + if( is_truth(zVal) && is_truth(pSetting->def) ) return 1; | |
| 4447 | + return 0; | |
| 4448 | +} | |
| 4376 | 4449 | |
| 4377 | 4450 | /* |
| 4378 | 4451 | ** Print the current value of a setting identified by the pSetting |
| 4379 | 4452 | ** pointer. |
| 4453 | +** | |
| 4454 | +** Only show the value, not the setting name, if valueOnly is true. | |
| 4455 | +** | |
| 4456 | +** Show nothing if bIfChng is true and the setting is not currently set | |
| 4457 | +** or is set to its default value. | |
| 4380 | 4458 | */ |
| 4381 | -void print_setting(const Setting *pSetting, int valueOnly){ | |
| 4459 | +void print_setting(const Setting *pSetting, int valueOnly, int bIfChng){ | |
| 4382 | 4460 | Stmt q; |
| 4383 | 4461 | int versioned = 0; |
| 4384 | 4462 | if( pSetting->versionable && g.localOpen ){ |
| 4385 | 4463 | /* Check to see if this is overridden by a versionable settings file */ |
| 4386 | 4464 | Blob versionedPathname; |
| @@ -4391,11 +4469,16 @@ | ||
| 4391 | 4469 | versioned = 1; |
| 4392 | 4470 | } |
| 4393 | 4471 | blob_reset(&versionedPathname); |
| 4394 | 4472 | } |
| 4395 | 4473 | if( valueOnly && versioned ){ |
| 4396 | - fossil_print("%s\n", db_get_versioned(pSetting->name, NULL)); | |
| 4474 | + const char *zVal = db_get_versioned(pSetting->name, NULL, NULL); | |
| 4475 | + if( !bIfChng || (zVal!=0 && fossil_strcmp(zVal, pSetting->def)!=0) ){ | |
| 4476 | + fossil_print("%s\n", db_get_versioned(pSetting->name, NULL, NULL)); | |
| 4477 | + }else{ | |
| 4478 | + versioned = 0; | |
| 4479 | + } | |
| 4397 | 4480 | return; |
| 4398 | 4481 | } |
| 4399 | 4482 | if( g.repositoryOpen ){ |
| 4400 | 4483 | db_prepare(&q, |
| 4401 | 4484 | "SELECT '(local)', value FROM config WHERE name=%Q" |
| @@ -4408,20 +4491,47 @@ | ||
| 4408 | 4491 | "SELECT '(global)', value FROM global_config WHERE name=%Q", |
| 4409 | 4492 | pSetting->name |
| 4410 | 4493 | ); |
| 4411 | 4494 | } |
| 4412 | 4495 | if( db_step(&q)==SQLITE_ROW ){ |
| 4413 | - if( valueOnly ){ | |
| 4496 | + const char *zVal = db_column_text(&q,1); | |
| 4497 | + if( bIfChng && setting_has_default_value(pSetting,zVal) ){ | |
| 4498 | + if( versioned ){ | |
| 4499 | + fossil_print("%-24s (versioned)\n", pSetting->name); | |
| 4500 | + versioned = 0; | |
| 4501 | + } | |
| 4502 | + }else if( valueOnly ){ | |
| 4414 | 4503 | fossil_print("%s\n", db_column_text(&q, 1)); |
| 4415 | 4504 | }else{ |
| 4416 | - fossil_print("%-20s %-8s %s\n", pSetting->name, db_column_text(&q, 0), | |
| 4417 | - db_column_text(&q, 1)); | |
| 4505 | + const char *zVal = (const char*)db_column_text(&q,1); | |
| 4506 | + const char *zName = (const char*)db_column_text(&q,0); | |
| 4507 | + if( zVal==0 ) zVal = "NULL"; | |
| 4508 | + if( strchr(zVal,'\n')==0 ){ | |
| 4509 | + fossil_print("%-24s %-11s %s\n", pSetting->name, zName, zVal); | |
| 4510 | + }else{ | |
| 4511 | + fossil_print("%-24s %-11s\n", pSetting->name, zName); | |
| 4512 | + while( zVal[0] ){ | |
| 4513 | + char *zNL = strchr(zVal, '\n'); | |
| 4514 | + if( zNL==0 ){ | |
| 4515 | + fossil_print(" %s\n", zVal); | |
| 4516 | + break; | |
| 4517 | + }else{ | |
| 4518 | + int n = (int)(zNL - zVal); | |
| 4519 | + while( n>0 && fossil_isspace(zVal[n-1]) ){ n--; } | |
| 4520 | + fossil_print(" %.*s\n", n, zVal); | |
| 4521 | + zVal = zNL+1; | |
| 4522 | + } | |
| 4523 | + } | |
| 4524 | + } | |
| 4418 | 4525 | } |
| 4526 | + }else if( bIfChng ){ | |
| 4527 | + /* Display nothing */ | |
| 4528 | + versioned = 0; | |
| 4419 | 4529 | }else if( valueOnly ){ |
| 4420 | 4530 | fossil_print("\n"); |
| 4421 | 4531 | }else{ |
| 4422 | - fossil_print("%-20s\n", pSetting->name); | |
| 4532 | + fossil_print("%-24s\n", pSetting->name); | |
| 4423 | 4533 | } |
| 4424 | 4534 | if( versioned ){ |
| 4425 | 4535 | fossil_print(" (overridden by contents of file .fossil-settings/%s)\n", |
| 4426 | 4536 | pSetting->name); |
| 4427 | 4537 | } |
| @@ -4454,21 +4564,22 @@ | ||
| 4454 | 4564 | char versionable; /* Is this setting versionable? */ |
| 4455 | 4565 | char forceTextArea; /* Force using a text area for display? */ |
| 4456 | 4566 | char sensitive; /* True if this a security-sensitive setting */ |
| 4457 | 4567 | const char *def; /* Default value */ |
| 4458 | 4568 | }; |
| 4569 | + | |
| 4459 | 4570 | #endif /* INTERFACE */ |
| 4460 | 4571 | |
| 4461 | 4572 | /* |
| 4462 | -** SETTING: access-log boolean default=off | |
| 4573 | +** SETTING: access-log boolean default=on | |
| 4463 | 4574 | ** |
| 4464 | 4575 | ** When the access-log setting is enabled, all login attempts (successful |
| 4465 | 4576 | ** and unsuccessful) on the web interface are recorded in the "access" table |
| 4466 | 4577 | ** of the repository. |
| 4467 | 4578 | */ |
| 4468 | 4579 | /* |
| 4469 | -** SETTING: admin-log boolean default=off | |
| 4580 | +** SETTING: admin-log boolean default=on | |
| 4470 | 4581 | ** |
| 4471 | 4582 | ** When the admin-log setting is enabled, configuration changes are recorded |
| 4472 | 4583 | ** in the "admin_log" table of the repository. |
| 4473 | 4584 | */ |
| 4474 | 4585 | /* |
| @@ -4707,10 +4818,18 @@ | ||
| 4707 | 4818 | */ |
| 4708 | 4819 | /* |
| 4709 | 4820 | ** SETTING: editor width=32 sensitive |
| 4710 | 4821 | ** The value is an external command that will launch the |
| 4711 | 4822 | ** text editor command used for check-in comments. |
| 4823 | +** | |
| 4824 | +** If this value is not set, then environment variables VISUAL and | |
| 4825 | +** EDITOR are consulted, in that order. If neither of those are set, | |
| 4826 | +** then a search is made for common text editors, including | |
| 4827 | +** "notepad", "nano", "pico", "jove", "edit", "vi", "vim", and "ed". | |
| 4828 | +** | |
| 4829 | +** If this setting is false ("off", "no", "false", or "0") then no | |
| 4830 | +** text editor is used. | |
| 4712 | 4831 | */ |
| 4713 | 4832 | /* |
| 4714 | 4833 | ** SETTING: empty-dirs width=40 versionable block-text |
| 4715 | 4834 | ** The value is a list of pathnames parsed according to the same rules as |
| 4716 | 4835 | ** the *-glob settings. On update and checkout commands, if no directory |
| @@ -4757,13 +4876,14 @@ | ||
| 4757 | 4876 | ** send the "pragma avoid-delta-manifests" statement in its reply, |
| 4758 | 4877 | ** which will cause the client to avoid generating a delta |
| 4759 | 4878 | ** manifest. |
| 4760 | 4879 | */ |
| 4761 | 4880 | /* |
| 4762 | -** SETTING: gdiff-command width=40 default=gdiff sensitive | |
| 4881 | +** SETTING: gdiff-command width=40 sensitive | |
| 4763 | 4882 | ** The value is an external command to run when performing a graphical |
| 4764 | -** diff. If undefined, text diff will be used. | |
| 4883 | +** diff. If undefined, a --tk diff is done if commands "tclsh" and "wish" | |
| 4884 | +** are on PATH, or a --by diff is done if "tclsh" or "wish" are unavailable. | |
| 4765 | 4885 | */ |
| 4766 | 4886 | /* |
| 4767 | 4887 | ** SETTING: gmerge-command width=40 sensitive |
| 4768 | 4888 | ** The value is a graphical merge conflict resolver command operating |
| 4769 | 4889 | ** on four files. Examples: |
| @@ -5152,20 +5272,22 @@ | ||
| 5152 | 5272 | ** configuration database. If both a local and a global value exists for a |
| 5153 | 5273 | ** setting, the local value takes precedence. This command normally operates |
| 5154 | 5274 | ** on the local settings. Use the --global option to change global settings. |
| 5155 | 5275 | ** |
| 5156 | 5276 | ** Options: |
| 5277 | +** --changed Only show settings if the value differs from the default | |
| 5278 | +** --exact Only consider exact name matches | |
| 5157 | 5279 | ** --global Set or unset the given property globally instead of |
| 5158 | 5280 | ** setting or unsetting it for the open repository only |
| 5159 | -** --exact Only consider exact name matches | |
| 5160 | 5281 | ** --value Only show the value of a given property (implies --exact) |
| 5161 | 5282 | ** |
| 5162 | 5283 | ** See also: [[configuration]] |
| 5163 | 5284 | */ |
| 5164 | 5285 | void setting_cmd(void){ |
| 5165 | 5286 | int i; |
| 5166 | 5287 | int globalFlag = find_option("global","g",0)!=0; |
| 5288 | + int bIfChng = find_option("changed",0,0)!=0; | |
| 5167 | 5289 | int exactFlag = find_option("exact",0,0)!=0; |
| 5168 | 5290 | int valueFlag = find_option("value",0,0)!=0; |
| 5169 | 5291 | /* Undocumented "--test-for-subsystem SUBSYS" option used to test |
| 5170 | 5292 | ** the db_get_for_subsystem() interface: */ |
| 5171 | 5293 | const char *zSubsys = find_option("test-for-subsystem",0,1); |
| @@ -5186,16 +5308,15 @@ | ||
| 5186 | 5308 | } |
| 5187 | 5309 | if( valueFlag ){ |
| 5188 | 5310 | if( g.argc!=3 ){ |
| 5189 | 5311 | fossil_fatal("--value is only supported when qurying a given property"); |
| 5190 | 5312 | } |
| 5191 | - exactFlag = 1; | |
| 5192 | 5313 | } |
| 5193 | 5314 | |
| 5194 | 5315 | if( g.argc==2 ){ |
| 5195 | 5316 | for(i=0; i<nSetting; i++){ |
| 5196 | - print_setting(&aSetting[i], 0); | |
| 5317 | + print_setting(&aSetting[i], 0, bIfChng); | |
| 5197 | 5318 | } |
| 5198 | 5319 | }else if( g.argc==3 || g.argc==4 ){ |
| 5199 | 5320 | const char *zName = g.argv[2]; |
| 5200 | 5321 | int n = (int)strlen(zName); |
| 5201 | 5322 | const Setting *pSetting = db_find_setting(zName, !exactFlag); |
| @@ -5246,11 +5367,11 @@ | ||
| 5246 | 5367 | fossil_print(" [%s]", zValue); |
| 5247 | 5368 | fossil_free(zValue); |
| 5248 | 5369 | } |
| 5249 | 5370 | fossil_print("\n"); |
| 5250 | 5371 | }else{ |
| 5251 | - print_setting(pSetting, valueFlag); | |
| 5372 | + print_setting(pSetting, valueFlag, bIfChng); | |
| 5252 | 5373 | } |
| 5253 | 5374 | pSetting++; |
| 5254 | 5375 | } |
| 5255 | 5376 | } |
| 5256 | 5377 | }else{ |
| 5257 | 5378 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -3204,23 +3204,16 @@ | |
| 3204 | |
| 3205 | db_unprotect(PROTECT_ALL); |
| 3206 | db_set("content-schema", CONTENT_SCHEMA, 0); |
| 3207 | db_set("aux-schema", AUX_SCHEMA_MAX, 0); |
| 3208 | db_set("rebuilt", get_version(), 0); |
| 3209 | db_set("admin-log", "1", 0); |
| 3210 | db_set("access-log", "1", 0); |
| 3211 | db_multi_exec( |
| 3212 | "INSERT INTO config(name,value,mtime)" |
| 3213 | " VALUES('server-code', lower(hex(randomblob(20))),now());" |
| 3214 | "INSERT INTO config(name,value,mtime)" |
| 3215 | " VALUES('project-code', lower(hex(randomblob(20))),now());" |
| 3216 | ); |
| 3217 | if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0); |
| 3218 | if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0); |
| 3219 | if( !db_is_global("timeline-plaintext") ){ |
| 3220 | db_set_int("timeline-plaintext", 1, 0); |
| 3221 | } |
| 3222 | db_create_default_users(0, zDefaultUser); |
| 3223 | if( zDefaultUser ) g.zLogin = zDefaultUser; |
| 3224 | user_select(); |
| 3225 | |
| 3226 | if( zTemplate ){ |
| @@ -3647,66 +3640,79 @@ | |
| 3647 | ** |
| 3648 | ** If the zNonVersionedSetting parameter is not NULL then it holds the |
| 3649 | ** non-versioned value for this setting. If both a versioned and a |
| 3650 | ** non-versioned value exist and are not equal, then a warning message |
| 3651 | ** might be generated. |
| 3652 | */ |
| 3653 | char *db_get_versioned(const char *zName, char *zNonVersionedSetting){ |
| 3654 | char *zVersionedSetting = 0; |
| 3655 | int noWarn = 0; |
| 3656 | int found = 0; |
| 3657 | struct _cacheEntry { |
| 3658 | struct _cacheEntry *next; |
| 3659 | const char *zName, *zValue; |
| 3660 | } *cacheEntry = 0; |
| 3661 | static struct _cacheEntry *cache = 0; |
| 3662 | |
| 3663 | if( !g.localOpen && g.zOpenRevision==0 ) return zNonVersionedSetting; |
| 3664 | /* Look up name in cache */ |
| 3665 | cacheEntry = cache; |
| 3666 | while( cacheEntry!=0 ){ |
| 3667 | if( fossil_strcmp(cacheEntry->zName, zName)==0 ){ |
| 3668 | zVersionedSetting = fossil_strdup(cacheEntry->zValue); |
| 3669 | break; |
| 3670 | } |
| 3671 | cacheEntry = cacheEntry->next; |
| 3672 | } |
| 3673 | /* Attempt to read value from file in check-out if there wasn't a cache hit.*/ |
| 3674 | if( cacheEntry==0 ){ |
| 3675 | Blob versionedPathname; |
| 3676 | Blob setting; |
| 3677 | blob_zero(&versionedPathname); |
| 3678 | blob_zero(&setting); |
| 3679 | blob_appendf(&versionedPathname, "%s.fossil-settings/%s", |
| 3680 | g.zLocalRoot, zName); |
| 3681 | if( !g.localOpen ){ |
| 3682 | /* Repository is in the process of being opened, but files have not been |
| 3683 | * written to disk. Load from the database. */ |
| 3684 | Blob noWarnFile; |
| 3685 | if( historical_blob(g.zOpenRevision, blob_str(&versionedPathname), |
| 3686 | &setting, 0) ){ |
| 3687 | found = 1; |
| 3688 | } |
| 3689 | /* See if there's a no-warn flag */ |
| 3690 | blob_append(&versionedPathname, ".no-warn", -1); |
| 3691 | blob_zero(&noWarnFile); |
| 3692 | if( historical_blob(g.zOpenRevision, blob_str(&versionedPathname), |
| 3693 | &noWarnFile, 0) ){ |
| 3694 | noWarn = 1; |
| 3695 | } |
| 3696 | blob_reset(&noWarnFile); |
| 3697 | }else if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){ |
| 3698 | /* File exists, and contains the value for this setting. Load from |
| 3699 | ** the file. */ |
| 3700 | const char *zFile = blob_str(&versionedPathname); |
| 3701 | if( blob_read_from_file(&setting, zFile, ExtFILE)>=0 ){ |
| 3702 | found = 1; |
| 3703 | } |
| 3704 | /* See if there's a no-warn flag */ |
| 3705 | blob_append(&versionedPathname, ".no-warn", -1); |
| 3706 | if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){ |
| 3707 | noWarn = 1; |
| 3708 | } |
| 3709 | } |
| 3710 | blob_reset(&versionedPathname); |
| 3711 | if( found ){ |
| 3712 | blob_strip_comment_lines(&setting, &setting); |
| @@ -3713,20 +3719,27 @@ | |
| 3713 | blob_trim(&setting); /* Avoid non-obvious problems with line endings |
| 3714 | ** on boolean properties */ |
| 3715 | zVersionedSetting = fossil_strdup(blob_str(&setting)); |
| 3716 | } |
| 3717 | blob_reset(&setting); |
| 3718 | /* Store result in cache, which can be the value or 0 if not found */ |
| 3719 | cacheEntry = (struct _cacheEntry*)fossil_malloc(sizeof(struct _cacheEntry)); |
| 3720 | cacheEntry->next = cache; |
| 3721 | cacheEntry->zName = zName; |
| 3722 | cacheEntry->zValue = fossil_strdup(zVersionedSetting); |
| 3723 | cache = cacheEntry; |
| 3724 | } |
| 3725 | /* Display a warning? */ |
| 3726 | if( zVersionedSetting!=0 && zNonVersionedSetting!=0 |
| 3727 | && zNonVersionedSetting[0]!='\0' && !noWarn |
| 3728 | ){ |
| 3729 | /* There's a versioned setting, and a non-versioned setting. Tell |
| 3730 | ** the user about the conflict */ |
| 3731 | fossil_warning( |
| 3732 | "setting %s has both versioned and non-versioned values: using " |
| @@ -3735,10 +3748,11 @@ | |
| 3735 | "\"%/.fossil-settings/%s.no-warn\" in the check-out root, or delete " |
| 3736 | "the non-versioned setting with \"fossil unset %s\")", zName, |
| 3737 | g.zLocalRoot, zName, g.zLocalRoot, zName, zName |
| 3738 | ); |
| 3739 | } |
| 3740 | /* Prefer the versioned setting */ |
| 3741 | return ( zVersionedSetting!=0 ) ? zVersionedSetting : zNonVersionedSetting; |
| 3742 | } |
| 3743 | |
| 3744 | |
| @@ -3778,11 +3792,11 @@ | |
| 3778 | } |
| 3779 | if( pSetting!=0 && pSetting->versionable ){ |
| 3780 | /* This is a versionable setting, try and get the info from a |
| 3781 | ** checked-out file */ |
| 3782 | char * zZ = z; |
| 3783 | z = db_get_versioned(zName, z); |
| 3784 | if(zZ != z){ |
| 3785 | fossil_free(zZ); |
| 3786 | } |
| 3787 | } |
| 3788 | if( z==0 ){ |
| @@ -3919,11 +3933,11 @@ | |
| 3919 | } |
| 3920 | fossil_free(zVal); |
| 3921 | return dflt; |
| 3922 | } |
| 3923 | int db_get_versioned_boolean(const char *zName, int dflt){ |
| 3924 | char *zVal = db_get_versioned(zName, 0); |
| 3925 | if( zVal==0 ) return dflt; |
| 3926 | if( is_truth(zVal) ) return 1; |
| 3927 | if( is_false(zVal) ) return 0; |
| 3928 | return dflt; |
| 3929 | } |
| @@ -4048,14 +4062,30 @@ | |
| 4048 | ** Get the manifest setting. For backwards compatibility first check if the |
| 4049 | ** value is a boolean. If it's not a boolean, treat each character as a flag |
| 4050 | ** to enable a manifest type. This system puts certain boundary conditions on |
| 4051 | ** which letters can be used to represent flags (any permutation of flags must |
| 4052 | ** not be able to fully form one of the boolean values). |
| 4053 | */ |
| 4054 | int db_get_manifest_setting(void){ |
| 4055 | int flg; |
| 4056 | char *zVal = db_get("manifest", 0); |
| 4057 | if( zVal==0 || is_false(zVal) ){ |
| 4058 | return 0; |
| 4059 | }else if( is_truth(zVal) ){ |
| 4060 | return MFESTFLG_RAW|MFESTFLG_UUID; |
| 4061 | } |
| @@ -4068,10 +4098,37 @@ | |
| 4068 | } |
| 4069 | zVal++; |
| 4070 | } |
| 4071 | return flg; |
| 4072 | } |
| 4073 | |
| 4074 | |
| 4075 | /* |
| 4076 | ** Record the name of a local repository in the global_config() database. |
| 4077 | ** The repository filename %s is recorded as an entry with a "name" field |
| @@ -4371,16 +4428,37 @@ | |
| 4371 | } |
| 4372 | } |
| 4373 | g.argc = 2; |
| 4374 | info_cmd(); |
| 4375 | } |
| 4376 | |
| 4377 | /* |
| 4378 | ** Print the current value of a setting identified by the pSetting |
| 4379 | ** pointer. |
| 4380 | */ |
| 4381 | void print_setting(const Setting *pSetting, int valueOnly){ |
| 4382 | Stmt q; |
| 4383 | int versioned = 0; |
| 4384 | if( pSetting->versionable && g.localOpen ){ |
| 4385 | /* Check to see if this is overridden by a versionable settings file */ |
| 4386 | Blob versionedPathname; |
| @@ -4391,11 +4469,16 @@ | |
| 4391 | versioned = 1; |
| 4392 | } |
| 4393 | blob_reset(&versionedPathname); |
| 4394 | } |
| 4395 | if( valueOnly && versioned ){ |
| 4396 | fossil_print("%s\n", db_get_versioned(pSetting->name, NULL)); |
| 4397 | return; |
| 4398 | } |
| 4399 | if( g.repositoryOpen ){ |
| 4400 | db_prepare(&q, |
| 4401 | "SELECT '(local)', value FROM config WHERE name=%Q" |
| @@ -4408,20 +4491,47 @@ | |
| 4408 | "SELECT '(global)', value FROM global_config WHERE name=%Q", |
| 4409 | pSetting->name |
| 4410 | ); |
| 4411 | } |
| 4412 | if( db_step(&q)==SQLITE_ROW ){ |
| 4413 | if( valueOnly ){ |
| 4414 | fossil_print("%s\n", db_column_text(&q, 1)); |
| 4415 | }else{ |
| 4416 | fossil_print("%-20s %-8s %s\n", pSetting->name, db_column_text(&q, 0), |
| 4417 | db_column_text(&q, 1)); |
| 4418 | } |
| 4419 | }else if( valueOnly ){ |
| 4420 | fossil_print("\n"); |
| 4421 | }else{ |
| 4422 | fossil_print("%-20s\n", pSetting->name); |
| 4423 | } |
| 4424 | if( versioned ){ |
| 4425 | fossil_print(" (overridden by contents of file .fossil-settings/%s)\n", |
| 4426 | pSetting->name); |
| 4427 | } |
| @@ -4454,21 +4564,22 @@ | |
| 4454 | char versionable; /* Is this setting versionable? */ |
| 4455 | char forceTextArea; /* Force using a text area for display? */ |
| 4456 | char sensitive; /* True if this a security-sensitive setting */ |
| 4457 | const char *def; /* Default value */ |
| 4458 | }; |
| 4459 | #endif /* INTERFACE */ |
| 4460 | |
| 4461 | /* |
| 4462 | ** SETTING: access-log boolean default=off |
| 4463 | ** |
| 4464 | ** When the access-log setting is enabled, all login attempts (successful |
| 4465 | ** and unsuccessful) on the web interface are recorded in the "access" table |
| 4466 | ** of the repository. |
| 4467 | */ |
| 4468 | /* |
| 4469 | ** SETTING: admin-log boolean default=off |
| 4470 | ** |
| 4471 | ** When the admin-log setting is enabled, configuration changes are recorded |
| 4472 | ** in the "admin_log" table of the repository. |
| 4473 | */ |
| 4474 | /* |
| @@ -4707,10 +4818,18 @@ | |
| 4707 | */ |
| 4708 | /* |
| 4709 | ** SETTING: editor width=32 sensitive |
| 4710 | ** The value is an external command that will launch the |
| 4711 | ** text editor command used for check-in comments. |
| 4712 | */ |
| 4713 | /* |
| 4714 | ** SETTING: empty-dirs width=40 versionable block-text |
| 4715 | ** The value is a list of pathnames parsed according to the same rules as |
| 4716 | ** the *-glob settings. On update and checkout commands, if no directory |
| @@ -4757,13 +4876,14 @@ | |
| 4757 | ** send the "pragma avoid-delta-manifests" statement in its reply, |
| 4758 | ** which will cause the client to avoid generating a delta |
| 4759 | ** manifest. |
| 4760 | */ |
| 4761 | /* |
| 4762 | ** SETTING: gdiff-command width=40 default=gdiff sensitive |
| 4763 | ** The value is an external command to run when performing a graphical |
| 4764 | ** diff. If undefined, text diff will be used. |
| 4765 | */ |
| 4766 | /* |
| 4767 | ** SETTING: gmerge-command width=40 sensitive |
| 4768 | ** The value is a graphical merge conflict resolver command operating |
| 4769 | ** on four files. Examples: |
| @@ -5152,20 +5272,22 @@ | |
| 5152 | ** configuration database. If both a local and a global value exists for a |
| 5153 | ** setting, the local value takes precedence. This command normally operates |
| 5154 | ** on the local settings. Use the --global option to change global settings. |
| 5155 | ** |
| 5156 | ** Options: |
| 5157 | ** --global Set or unset the given property globally instead of |
| 5158 | ** setting or unsetting it for the open repository only |
| 5159 | ** --exact Only consider exact name matches |
| 5160 | ** --value Only show the value of a given property (implies --exact) |
| 5161 | ** |
| 5162 | ** See also: [[configuration]] |
| 5163 | */ |
| 5164 | void setting_cmd(void){ |
| 5165 | int i; |
| 5166 | int globalFlag = find_option("global","g",0)!=0; |
| 5167 | int exactFlag = find_option("exact",0,0)!=0; |
| 5168 | int valueFlag = find_option("value",0,0)!=0; |
| 5169 | /* Undocumented "--test-for-subsystem SUBSYS" option used to test |
| 5170 | ** the db_get_for_subsystem() interface: */ |
| 5171 | const char *zSubsys = find_option("test-for-subsystem",0,1); |
| @@ -5186,16 +5308,15 @@ | |
| 5186 | } |
| 5187 | if( valueFlag ){ |
| 5188 | if( g.argc!=3 ){ |
| 5189 | fossil_fatal("--value is only supported when qurying a given property"); |
| 5190 | } |
| 5191 | exactFlag = 1; |
| 5192 | } |
| 5193 | |
| 5194 | if( g.argc==2 ){ |
| 5195 | for(i=0; i<nSetting; i++){ |
| 5196 | print_setting(&aSetting[i], 0); |
| 5197 | } |
| 5198 | }else if( g.argc==3 || g.argc==4 ){ |
| 5199 | const char *zName = g.argv[2]; |
| 5200 | int n = (int)strlen(zName); |
| 5201 | const Setting *pSetting = db_find_setting(zName, !exactFlag); |
| @@ -5246,11 +5367,11 @@ | |
| 5246 | fossil_print(" [%s]", zValue); |
| 5247 | fossil_free(zValue); |
| 5248 | } |
| 5249 | fossil_print("\n"); |
| 5250 | }else{ |
| 5251 | print_setting(pSetting, valueFlag); |
| 5252 | } |
| 5253 | pSetting++; |
| 5254 | } |
| 5255 | } |
| 5256 | }else{ |
| 5257 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -3204,23 +3204,16 @@ | |
| 3204 | |
| 3205 | db_unprotect(PROTECT_ALL); |
| 3206 | db_set("content-schema", CONTENT_SCHEMA, 0); |
| 3207 | db_set("aux-schema", AUX_SCHEMA_MAX, 0); |
| 3208 | db_set("rebuilt", get_version(), 0); |
| 3209 | db_multi_exec( |
| 3210 | "INSERT INTO config(name,value,mtime)" |
| 3211 | " VALUES('server-code', lower(hex(randomblob(20))),now());" |
| 3212 | "INSERT INTO config(name,value,mtime)" |
| 3213 | " VALUES('project-code', lower(hex(randomblob(20))),now());" |
| 3214 | ); |
| 3215 | db_create_default_users(0, zDefaultUser); |
| 3216 | if( zDefaultUser ) g.zLogin = zDefaultUser; |
| 3217 | user_select(); |
| 3218 | |
| 3219 | if( zTemplate ){ |
| @@ -3647,66 +3640,79 @@ | |
| 3640 | ** |
| 3641 | ** If the zNonVersionedSetting parameter is not NULL then it holds the |
| 3642 | ** non-versioned value for this setting. If both a versioned and a |
| 3643 | ** non-versioned value exist and are not equal, then a warning message |
| 3644 | ** might be generated. |
| 3645 | ** |
| 3646 | ** zCkin is normally NULL. In that case, the versioned setting is |
| 3647 | ** take from the local check-out, if a local checkout exists, or from |
| 3648 | ** checkin named by the g.zOpenRevision global variable. If zCkin is |
| 3649 | ** not NULL, then zCkin is the name of the specific checkin from which |
| 3650 | ** versioned setting value is taken. When zCkin is not NULL, the cache |
| 3651 | ** is bypassed. |
| 3652 | */ |
| 3653 | char *db_get_versioned( |
| 3654 | const char *zName, |
| 3655 | char *zNonVersionedSetting, |
| 3656 | const char *zCkin |
| 3657 | ){ |
| 3658 | char *zVersionedSetting = 0; |
| 3659 | int noWarn = 0; |
| 3660 | int found = 0; |
| 3661 | struct _cacheEntry { |
| 3662 | struct _cacheEntry *next; |
| 3663 | const char *zName, *zValue; |
| 3664 | } *cacheEntry = 0; |
| 3665 | static struct _cacheEntry *cache = 0; |
| 3666 | |
| 3667 | if( !g.localOpen && g.zOpenRevision==0 && zCkin==0 ){ |
| 3668 | return zNonVersionedSetting; |
| 3669 | } |
| 3670 | |
| 3671 | /* Look up name in cache */ |
| 3672 | if( zCkin==0 ){ |
| 3673 | cacheEntry = cache; |
| 3674 | while( cacheEntry!=0 ){ |
| 3675 | if( fossil_strcmp(cacheEntry->zName, zName)==0 ){ |
| 3676 | zVersionedSetting = fossil_strdup(cacheEntry->zValue); |
| 3677 | break; |
| 3678 | } |
| 3679 | cacheEntry = cacheEntry->next; |
| 3680 | } |
| 3681 | } |
| 3682 | |
| 3683 | /* Attempt to read value from file in check-out if there wasn't a cache hit.*/ |
| 3684 | if( cacheEntry==0 ){ |
| 3685 | Blob versionedPathname; |
| 3686 | Blob setting; |
| 3687 | blob_init(&versionedPathname, 0, 0); |
| 3688 | blob_init(&setting, 0, 0); |
| 3689 | if( !g.localOpen || zCkin!=0 ){ |
| 3690 | /* Repository is in the process of being opened, but files have not been |
| 3691 | * written to disk. Load from the database. */ |
| 3692 | blob_appendf(&versionedPathname, ".fossil-settings/%s", zName); |
| 3693 | if( historical_blob(zCkin ? zCkin : g.zOpenRevision, |
| 3694 | blob_str(&versionedPathname), |
| 3695 | &setting, 0) |
| 3696 | ){ |
| 3697 | found = 1; |
| 3698 | } |
| 3699 | }else{ |
| 3700 | blob_appendf(&versionedPathname, "%s.fossil-settings/%s", |
| 3701 | g.zLocalRoot, zName); |
| 3702 | if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){ |
| 3703 | /* File exists, and contains the value for this setting. Load from |
| 3704 | ** the file. */ |
| 3705 | const char *zFile = blob_str(&versionedPathname); |
| 3706 | if( blob_read_from_file(&setting, zFile, ExtFILE)>=0 ){ |
| 3707 | found = 1; |
| 3708 | } |
| 3709 | /* See if there's a no-warn flag */ |
| 3710 | blob_append(&versionedPathname, ".no-warn", -1); |
| 3711 | if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){ |
| 3712 | noWarn = 1; |
| 3713 | } |
| 3714 | } |
| 3715 | } |
| 3716 | blob_reset(&versionedPathname); |
| 3717 | if( found ){ |
| 3718 | blob_strip_comment_lines(&setting, &setting); |
| @@ -3713,20 +3719,27 @@ | |
| 3719 | blob_trim(&setting); /* Avoid non-obvious problems with line endings |
| 3720 | ** on boolean properties */ |
| 3721 | zVersionedSetting = fossil_strdup(blob_str(&setting)); |
| 3722 | } |
| 3723 | blob_reset(&setting); |
| 3724 | |
| 3725 | /* Store result in cache, which can be the value or 0 if not found */ |
| 3726 | if( zCkin==0 ){ |
| 3727 | cacheEntry = (struct _cacheEntry*)fossil_malloc(sizeof(*cacheEntry)); |
| 3728 | cacheEntry->next = cache; |
| 3729 | cacheEntry->zName = zName; |
| 3730 | cacheEntry->zValue = fossil_strdup(zVersionedSetting); |
| 3731 | cache = cacheEntry; |
| 3732 | } |
| 3733 | } |
| 3734 | |
| 3735 | /* Display a warning? */ |
| 3736 | if( zVersionedSetting!=0 |
| 3737 | && zNonVersionedSetting!=0 |
| 3738 | && zNonVersionedSetting[0]!='\0' |
| 3739 | && zCkin==0 |
| 3740 | && !noWarn |
| 3741 | ){ |
| 3742 | /* There's a versioned setting, and a non-versioned setting. Tell |
| 3743 | ** the user about the conflict */ |
| 3744 | fossil_warning( |
| 3745 | "setting %s has both versioned and non-versioned values: using " |
| @@ -3735,10 +3748,11 @@ | |
| 3748 | "\"%/.fossil-settings/%s.no-warn\" in the check-out root, or delete " |
| 3749 | "the non-versioned setting with \"fossil unset %s\")", zName, |
| 3750 | g.zLocalRoot, zName, g.zLocalRoot, zName, zName |
| 3751 | ); |
| 3752 | } |
| 3753 | |
| 3754 | /* Prefer the versioned setting */ |
| 3755 | return ( zVersionedSetting!=0 ) ? zVersionedSetting : zNonVersionedSetting; |
| 3756 | } |
| 3757 | |
| 3758 | |
| @@ -3778,11 +3792,11 @@ | |
| 3792 | } |
| 3793 | if( pSetting!=0 && pSetting->versionable ){ |
| 3794 | /* This is a versionable setting, try and get the info from a |
| 3795 | ** checked-out file */ |
| 3796 | char * zZ = z; |
| 3797 | z = db_get_versioned(zName, z, 0); |
| 3798 | if(zZ != z){ |
| 3799 | fossil_free(zZ); |
| 3800 | } |
| 3801 | } |
| 3802 | if( z==0 ){ |
| @@ -3919,11 +3933,11 @@ | |
| 3933 | } |
| 3934 | fossil_free(zVal); |
| 3935 | return dflt; |
| 3936 | } |
| 3937 | int db_get_versioned_boolean(const char *zName, int dflt){ |
| 3938 | char *zVal = db_get_versioned(zName, 0, 0); |
| 3939 | if( zVal==0 ) return dflt; |
| 3940 | if( is_truth(zVal) ) return 1; |
| 3941 | if( is_false(zVal) ) return 0; |
| 3942 | return dflt; |
| 3943 | } |
| @@ -4048,14 +4062,30 @@ | |
| 4062 | ** Get the manifest setting. For backwards compatibility first check if the |
| 4063 | ** value is a boolean. If it's not a boolean, treat each character as a flag |
| 4064 | ** to enable a manifest type. This system puts certain boundary conditions on |
| 4065 | ** which letters can be used to represent flags (any permutation of flags must |
| 4066 | ** not be able to fully form one of the boolean values). |
| 4067 | ** |
| 4068 | ** "manifest" is a versionable setting. But we do not issue a warning |
| 4069 | ** if there is a conflict. Instead, the value returned is the value for |
| 4070 | ** the versioned setting if the versioned setting exists, or the ordinary |
| 4071 | ** setting otherwise. |
| 4072 | ** |
| 4073 | ** The argument zCkin is the specific check-in for which we want the |
| 4074 | ** manifest setting. |
| 4075 | */ |
| 4076 | int db_get_manifest_setting(const char *zCkin){ |
| 4077 | int flg; |
| 4078 | char *zVal; |
| 4079 | |
| 4080 | /* Look for the versioned setting first */ |
| 4081 | zVal = db_get_versioned("manifest", 0, zCkin); |
| 4082 | |
| 4083 | if( zVal==0 && g.repositoryOpen ){ |
| 4084 | /* No versioned setting, look for the repository setting second */ |
| 4085 | zVal = db_text(0, "SELECT value FROM config WHERE name='manifest'"); |
| 4086 | } |
| 4087 | if( zVal==0 || is_false(zVal) ){ |
| 4088 | return 0; |
| 4089 | }else if( is_truth(zVal) ){ |
| 4090 | return MFESTFLG_RAW|MFESTFLG_UUID; |
| 4091 | } |
| @@ -4068,10 +4098,37 @@ | |
| 4098 | } |
| 4099 | zVal++; |
| 4100 | } |
| 4101 | return flg; |
| 4102 | } |
| 4103 | |
| 4104 | /* |
| 4105 | ** COMMAND: test-manifest-setting |
| 4106 | ** |
| 4107 | ** Usage: %fossil test-manifest-setting VERSION VERSION ... |
| 4108 | ** |
| 4109 | ** Display the value for the "manifest" setting for various versions |
| 4110 | ** of the repository. |
| 4111 | */ |
| 4112 | void test_manfest_setting_cmd(void){ |
| 4113 | int i; |
| 4114 | db_find_and_open_repository(0, 0); |
| 4115 | for(i=2; i<g.argc; i++){ |
| 4116 | int m = db_get_manifest_setting(g.argv[i]); |
| 4117 | fossil_print("%s:\n", g.argv[i]); |
| 4118 | fossil_print(" flags = 0x%02x\n", m); |
| 4119 | if( m & MFESTFLG_RAW ){ |
| 4120 | fossil_print(" manifest\n"); |
| 4121 | } |
| 4122 | if( m & MFESTFLG_UUID ){ |
| 4123 | fossil_print(" manifest.uuid\n"); |
| 4124 | } |
| 4125 | if( m & MFESTFLG_TAGS ){ |
| 4126 | fossil_print(" manifest.tags\n"); |
| 4127 | } |
| 4128 | } |
| 4129 | } |
| 4130 | |
| 4131 | |
| 4132 | /* |
| 4133 | ** Record the name of a local repository in the global_config() database. |
| 4134 | ** The repository filename %s is recorded as an entry with a "name" field |
| @@ -4371,16 +4428,37 @@ | |
| 4428 | } |
| 4429 | } |
| 4430 | g.argc = 2; |
| 4431 | info_cmd(); |
| 4432 | } |
| 4433 | |
| 4434 | /* |
| 4435 | ** Return true if pSetting has its default value assuming its |
| 4436 | ** current value is zVal. |
| 4437 | */ |
| 4438 | int setting_has_default_value(const Setting *pSetting, const char *zVal){ |
| 4439 | if( zVal==0 ) return 1; |
| 4440 | if( pSetting->def==0 ) return 0; |
| 4441 | if( pSetting->width==0 ){ |
| 4442 | return is_false(pSetting->def)==is_false(zVal); |
| 4443 | } |
| 4444 | if( fossil_strcmp(pSetting->def, zVal)==0 ) return 1; |
| 4445 | if( is_false(zVal) && is_false(pSetting->def) ) return 1; |
| 4446 | if( is_truth(zVal) && is_truth(pSetting->def) ) return 1; |
| 4447 | return 0; |
| 4448 | } |
| 4449 | |
| 4450 | /* |
| 4451 | ** Print the current value of a setting identified by the pSetting |
| 4452 | ** pointer. |
| 4453 | ** |
| 4454 | ** Only show the value, not the setting name, if valueOnly is true. |
| 4455 | ** |
| 4456 | ** Show nothing if bIfChng is true and the setting is not currently set |
| 4457 | ** or is set to its default value. |
| 4458 | */ |
| 4459 | void print_setting(const Setting *pSetting, int valueOnly, int bIfChng){ |
| 4460 | Stmt q; |
| 4461 | int versioned = 0; |
| 4462 | if( pSetting->versionable && g.localOpen ){ |
| 4463 | /* Check to see if this is overridden by a versionable settings file */ |
| 4464 | Blob versionedPathname; |
| @@ -4391,11 +4469,16 @@ | |
| 4469 | versioned = 1; |
| 4470 | } |
| 4471 | blob_reset(&versionedPathname); |
| 4472 | } |
| 4473 | if( valueOnly && versioned ){ |
| 4474 | const char *zVal = db_get_versioned(pSetting->name, NULL, NULL); |
| 4475 | if( !bIfChng || (zVal!=0 && fossil_strcmp(zVal, pSetting->def)!=0) ){ |
| 4476 | fossil_print("%s\n", db_get_versioned(pSetting->name, NULL, NULL)); |
| 4477 | }else{ |
| 4478 | versioned = 0; |
| 4479 | } |
| 4480 | return; |
| 4481 | } |
| 4482 | if( g.repositoryOpen ){ |
| 4483 | db_prepare(&q, |
| 4484 | "SELECT '(local)', value FROM config WHERE name=%Q" |
| @@ -4408,20 +4491,47 @@ | |
| 4491 | "SELECT '(global)', value FROM global_config WHERE name=%Q", |
| 4492 | pSetting->name |
| 4493 | ); |
| 4494 | } |
| 4495 | if( db_step(&q)==SQLITE_ROW ){ |
| 4496 | const char *zVal = db_column_text(&q,1); |
| 4497 | if( bIfChng && setting_has_default_value(pSetting,zVal) ){ |
| 4498 | if( versioned ){ |
| 4499 | fossil_print("%-24s (versioned)\n", pSetting->name); |
| 4500 | versioned = 0; |
| 4501 | } |
| 4502 | }else if( valueOnly ){ |
| 4503 | fossil_print("%s\n", db_column_text(&q, 1)); |
| 4504 | }else{ |
| 4505 | const char *zVal = (const char*)db_column_text(&q,1); |
| 4506 | const char *zName = (const char*)db_column_text(&q,0); |
| 4507 | if( zVal==0 ) zVal = "NULL"; |
| 4508 | if( strchr(zVal,'\n')==0 ){ |
| 4509 | fossil_print("%-24s %-11s %s\n", pSetting->name, zName, zVal); |
| 4510 | }else{ |
| 4511 | fossil_print("%-24s %-11s\n", pSetting->name, zName); |
| 4512 | while( zVal[0] ){ |
| 4513 | char *zNL = strchr(zVal, '\n'); |
| 4514 | if( zNL==0 ){ |
| 4515 | fossil_print(" %s\n", zVal); |
| 4516 | break; |
| 4517 | }else{ |
| 4518 | int n = (int)(zNL - zVal); |
| 4519 | while( n>0 && fossil_isspace(zVal[n-1]) ){ n--; } |
| 4520 | fossil_print(" %.*s\n", n, zVal); |
| 4521 | zVal = zNL+1; |
| 4522 | } |
| 4523 | } |
| 4524 | } |
| 4525 | } |
| 4526 | }else if( bIfChng ){ |
| 4527 | /* Display nothing */ |
| 4528 | versioned = 0; |
| 4529 | }else if( valueOnly ){ |
| 4530 | fossil_print("\n"); |
| 4531 | }else{ |
| 4532 | fossil_print("%-24s\n", pSetting->name); |
| 4533 | } |
| 4534 | if( versioned ){ |
| 4535 | fossil_print(" (overridden by contents of file .fossil-settings/%s)\n", |
| 4536 | pSetting->name); |
| 4537 | } |
| @@ -4454,21 +4564,22 @@ | |
| 4564 | char versionable; /* Is this setting versionable? */ |
| 4565 | char forceTextArea; /* Force using a text area for display? */ |
| 4566 | char sensitive; /* True if this a security-sensitive setting */ |
| 4567 | const char *def; /* Default value */ |
| 4568 | }; |
| 4569 | |
| 4570 | #endif /* INTERFACE */ |
| 4571 | |
| 4572 | /* |
| 4573 | ** SETTING: access-log boolean default=on |
| 4574 | ** |
| 4575 | ** When the access-log setting is enabled, all login attempts (successful |
| 4576 | ** and unsuccessful) on the web interface are recorded in the "access" table |
| 4577 | ** of the repository. |
| 4578 | */ |
| 4579 | /* |
| 4580 | ** SETTING: admin-log boolean default=on |
| 4581 | ** |
| 4582 | ** When the admin-log setting is enabled, configuration changes are recorded |
| 4583 | ** in the "admin_log" table of the repository. |
| 4584 | */ |
| 4585 | /* |
| @@ -4707,10 +4818,18 @@ | |
| 4818 | */ |
| 4819 | /* |
| 4820 | ** SETTING: editor width=32 sensitive |
| 4821 | ** The value is an external command that will launch the |
| 4822 | ** text editor command used for check-in comments. |
| 4823 | ** |
| 4824 | ** If this value is not set, then environment variables VISUAL and |
| 4825 | ** EDITOR are consulted, in that order. If neither of those are set, |
| 4826 | ** then a search is made for common text editors, including |
| 4827 | ** "notepad", "nano", "pico", "jove", "edit", "vi", "vim", and "ed". |
| 4828 | ** |
| 4829 | ** If this setting is false ("off", "no", "false", or "0") then no |
| 4830 | ** text editor is used. |
| 4831 | */ |
| 4832 | /* |
| 4833 | ** SETTING: empty-dirs width=40 versionable block-text |
| 4834 | ** The value is a list of pathnames parsed according to the same rules as |
| 4835 | ** the *-glob settings. On update and checkout commands, if no directory |
| @@ -4757,13 +4876,14 @@ | |
| 4876 | ** send the "pragma avoid-delta-manifests" statement in its reply, |
| 4877 | ** which will cause the client to avoid generating a delta |
| 4878 | ** manifest. |
| 4879 | */ |
| 4880 | /* |
| 4881 | ** SETTING: gdiff-command width=40 sensitive |
| 4882 | ** The value is an external command to run when performing a graphical |
| 4883 | ** diff. If undefined, a --tk diff is done if commands "tclsh" and "wish" |
| 4884 | ** are on PATH, or a --by diff is done if "tclsh" or "wish" are unavailable. |
| 4885 | */ |
| 4886 | /* |
| 4887 | ** SETTING: gmerge-command width=40 sensitive |
| 4888 | ** The value is a graphical merge conflict resolver command operating |
| 4889 | ** on four files. Examples: |
| @@ -5152,20 +5272,22 @@ | |
| 5272 | ** configuration database. If both a local and a global value exists for a |
| 5273 | ** setting, the local value takes precedence. This command normally operates |
| 5274 | ** on the local settings. Use the --global option to change global settings. |
| 5275 | ** |
| 5276 | ** Options: |
| 5277 | ** --changed Only show settings if the value differs from the default |
| 5278 | ** --exact Only consider exact name matches |
| 5279 | ** --global Set or unset the given property globally instead of |
| 5280 | ** setting or unsetting it for the open repository only |
| 5281 | ** --value Only show the value of a given property (implies --exact) |
| 5282 | ** |
| 5283 | ** See also: [[configuration]] |
| 5284 | */ |
| 5285 | void setting_cmd(void){ |
| 5286 | int i; |
| 5287 | int globalFlag = find_option("global","g",0)!=0; |
| 5288 | int bIfChng = find_option("changed",0,0)!=0; |
| 5289 | int exactFlag = find_option("exact",0,0)!=0; |
| 5290 | int valueFlag = find_option("value",0,0)!=0; |
| 5291 | /* Undocumented "--test-for-subsystem SUBSYS" option used to test |
| 5292 | ** the db_get_for_subsystem() interface: */ |
| 5293 | const char *zSubsys = find_option("test-for-subsystem",0,1); |
| @@ -5186,16 +5308,15 @@ | |
| 5308 | } |
| 5309 | if( valueFlag ){ |
| 5310 | if( g.argc!=3 ){ |
| 5311 | fossil_fatal("--value is only supported when qurying a given property"); |
| 5312 | } |
| 5313 | } |
| 5314 | |
| 5315 | if( g.argc==2 ){ |
| 5316 | for(i=0; i<nSetting; i++){ |
| 5317 | print_setting(&aSetting[i], 0, bIfChng); |
| 5318 | } |
| 5319 | }else if( g.argc==3 || g.argc==4 ){ |
| 5320 | const char *zName = g.argv[2]; |
| 5321 | int n = (int)strlen(zName); |
| 5322 | const Setting *pSetting = db_find_setting(zName, !exactFlag); |
| @@ -5246,11 +5367,11 @@ | |
| 5367 | fossil_print(" [%s]", zValue); |
| 5368 | fossil_free(zValue); |
| 5369 | } |
| 5370 | fossil_print("\n"); |
| 5371 | }else{ |
| 5372 | print_setting(pSetting, valueFlag, bIfChng); |
| 5373 | } |
| 5374 | pSetting++; |
| 5375 | } |
| 5376 | } |
| 5377 | }else{ |
| 5378 |
+1
| --- src/default.css | ||
| +++ src/default.css | ||
| @@ -750,10 +750,11 @@ | ||
| 750 | 750 | body.tkt div.content ol.tkt-changes > li:target > ol { |
| 751 | 751 | border-left: 1px solid gold; |
| 752 | 752 | } |
| 753 | 753 | body.cpage-ckout .file-change-line, |
| 754 | 754 | body.cpage-info .file-change-line, |
| 755 | +body.cpage-vinfo .file-change-line, | |
| 755 | 756 | body.cpage-vdiff .file-change-line { |
| 756 | 757 | margin-top: 16px; |
| 757 | 758 | margin-bottom: 16px; |
| 758 | 759 | margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */; |
| 759 | 760 | display: flex; |
| 760 | 761 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -750,10 +750,11 @@ | |
| 750 | body.tkt div.content ol.tkt-changes > li:target > ol { |
| 751 | border-left: 1px solid gold; |
| 752 | } |
| 753 | body.cpage-ckout .file-change-line, |
| 754 | body.cpage-info .file-change-line, |
| 755 | body.cpage-vdiff .file-change-line { |
| 756 | margin-top: 16px; |
| 757 | margin-bottom: 16px; |
| 758 | margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */; |
| 759 | display: flex; |
| 760 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -750,10 +750,11 @@ | |
| 750 | body.tkt div.content ol.tkt-changes > li:target > ol { |
| 751 | border-left: 1px solid gold; |
| 752 | } |
| 753 | body.cpage-ckout .file-change-line, |
| 754 | body.cpage-info .file-change-line, |
| 755 | body.cpage-vinfo .file-change-line, |
| 756 | body.cpage-vdiff .file-change-line { |
| 757 | margin-top: 16px; |
| 758 | margin-bottom: 16px; |
| 759 | margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */; |
| 760 | display: flex; |
| 761 |
+7
-1
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -3175,11 +3175,13 @@ | ||
| 3175 | 3175 | } |
| 3176 | 3176 | g.diffCnt[1] += nIns; |
| 3177 | 3177 | g.diffCnt[2] += nDel; |
| 3178 | 3178 | if( nIns+nDel ){ |
| 3179 | 3179 | g.diffCnt[0]++; |
| 3180 | - blob_appendf(pOut, "%10d %10d", nIns, nDel); | |
| 3180 | + if( !(pCfg->diffFlags & DIFF_BRIEF) ){ | |
| 3181 | + blob_appendf(pOut, "%10d %10d", nIns, nDel); | |
| 3182 | + } | |
| 3181 | 3183 | } |
| 3182 | 3184 | }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){ |
| 3183 | 3185 | const int *R = c.aEdit; |
| 3184 | 3186 | unsigned int r; |
| 3185 | 3187 | for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){ |
| @@ -3335,10 +3337,14 @@ | ||
| 3335 | 3337 | if( zDiffBinary ){ |
| 3336 | 3338 | if( is_truth(zDiffBinary) ) diffFlags |= DIFF_INCBINARY; |
| 3337 | 3339 | }else if( db_get_boolean("diff-binary", 1) ){ |
| 3338 | 3340 | diffFlags |= DIFF_INCBINARY; |
| 3339 | 3341 | } |
| 3342 | + }else if( isGDiff) { | |
| 3343 | + /* No external gdiff command found, using --by */ | |
| 3344 | + diffFlags |= DIFF_HTML|DIFF_WEBPAGE|DIFF_LINENO|DIFF_BROWSER | |
| 3345 | + |DIFF_SIDEBYSIDE; | |
| 3340 | 3346 | } |
| 3341 | 3347 | } |
| 3342 | 3348 | if( find_option("verbose","v",0)!=0 ) diffFlags |= DIFF_VERBOSE; |
| 3343 | 3349 | /* Deprecated, but retained for script compatibility. */ |
| 3344 | 3350 | else if( find_option("new-file","N",0)!=0 ) diffFlags |= DIFF_VERBOSE; |
| 3345 | 3351 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -3175,11 +3175,13 @@ | |
| 3175 | } |
| 3176 | g.diffCnt[1] += nIns; |
| 3177 | g.diffCnt[2] += nDel; |
| 3178 | if( nIns+nDel ){ |
| 3179 | g.diffCnt[0]++; |
| 3180 | blob_appendf(pOut, "%10d %10d", nIns, nDel); |
| 3181 | } |
| 3182 | }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){ |
| 3183 | const int *R = c.aEdit; |
| 3184 | unsigned int r; |
| 3185 | for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){ |
| @@ -3335,10 +3337,14 @@ | |
| 3335 | if( zDiffBinary ){ |
| 3336 | if( is_truth(zDiffBinary) ) diffFlags |= DIFF_INCBINARY; |
| 3337 | }else if( db_get_boolean("diff-binary", 1) ){ |
| 3338 | diffFlags |= DIFF_INCBINARY; |
| 3339 | } |
| 3340 | } |
| 3341 | } |
| 3342 | if( find_option("verbose","v",0)!=0 ) diffFlags |= DIFF_VERBOSE; |
| 3343 | /* Deprecated, but retained for script compatibility. */ |
| 3344 | else if( find_option("new-file","N",0)!=0 ) diffFlags |= DIFF_VERBOSE; |
| 3345 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -3175,11 +3175,13 @@ | |
| 3175 | } |
| 3176 | g.diffCnt[1] += nIns; |
| 3177 | g.diffCnt[2] += nDel; |
| 3178 | if( nIns+nDel ){ |
| 3179 | g.diffCnt[0]++; |
| 3180 | if( !(pCfg->diffFlags & DIFF_BRIEF) ){ |
| 3181 | blob_appendf(pOut, "%10d %10d", nIns, nDel); |
| 3182 | } |
| 3183 | } |
| 3184 | }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){ |
| 3185 | const int *R = c.aEdit; |
| 3186 | unsigned int r; |
| 3187 | for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){ |
| @@ -3335,10 +3337,14 @@ | |
| 3337 | if( zDiffBinary ){ |
| 3338 | if( is_truth(zDiffBinary) ) diffFlags |= DIFF_INCBINARY; |
| 3339 | }else if( db_get_boolean("diff-binary", 1) ){ |
| 3340 | diffFlags |= DIFF_INCBINARY; |
| 3341 | } |
| 3342 | }else if( isGDiff) { |
| 3343 | /* No external gdiff command found, using --by */ |
| 3344 | diffFlags |= DIFF_HTML|DIFF_WEBPAGE|DIFF_LINENO|DIFF_BROWSER |
| 3345 | |DIFF_SIDEBYSIDE; |
| 3346 | } |
| 3347 | } |
| 3348 | if( find_option("verbose","v",0)!=0 ) diffFlags |= DIFF_VERBOSE; |
| 3349 | /* Deprecated, but retained for script compatibility. */ |
| 3350 | else if( find_option("new-file","N",0)!=0 ) diffFlags |= DIFF_VERBOSE; |
| 3351 |
+36
-7
| --- src/diff.tcl | ||
| +++ src/diff.tcl | ||
| @@ -90,28 +90,56 @@ | ||
| 90 | 90 | set x [lindex $difftxt $ii] |
| 91 | 91 | incr ii |
| 92 | 92 | return $x |
| 93 | 93 | } |
| 94 | 94 | |
| 95 | -proc readDiffs {fossilcmd} { | |
| 95 | +proc reloadDiff {} { | |
| 96 | + global fossilcmd difftxt | |
| 97 | + unset -nocomplain difftxt | |
| 98 | + set idx [.txtA index @0,0] | |
| 99 | + readDiffs $fossilcmd 1 | |
| 100 | + update | |
| 101 | + viewDiff $idx | |
| 102 | +} | |
| 103 | + | |
| 104 | +proc readDiffs {fossilcmd redo} { | |
| 96 | 105 | global difftxt debug |
| 97 | 106 | if {![info exists difftxt]} { |
| 98 | 107 | if {$debug} { |
| 99 | 108 | puts "# [list open $fossilcmd r]" |
| 100 | 109 | flush stdout |
| 101 | 110 | } |
| 102 | - set in [open $fossilcmd r] | |
| 103 | - fconfigure $in -encoding utf-8 | |
| 104 | - set difftxt [split [read $in] \n] | |
| 105 | - close $in | |
| 111 | + if {[catch { | |
| 112 | + set in [open $fossilcmd r] | |
| 113 | + fconfigure $in -encoding utf-8 | |
| 114 | + set difftxt [split [read $in] \n] | |
| 115 | + close $in | |
| 116 | + } msg]} { | |
| 117 | + if {$redo} { | |
| 118 | + tk_messageBox -type ok -title Error -message "Unable to refresh:\n$msg" | |
| 119 | + return 0 | |
| 120 | + } else { | |
| 121 | + puts $msg | |
| 122 | + exit 1 | |
| 123 | + } | |
| 124 | + } | |
| 106 | 125 | } |
| 107 | 126 | set N [llength $difftxt] |
| 108 | 127 | set ii 0 |
| 109 | 128 | set nDiffs 0 |
| 110 | 129 | set n1 0 |
| 111 | 130 | set n2 0 |
| 112 | 131 | array set widths {txt 3 ln 3 mkr 1} |
| 132 | + if {$redo} { | |
| 133 | + foreach c [cols] {$c config -state normal} | |
| 134 | + .lnA delete 1.0 end | |
| 135 | + .txtA delete 1.0 end | |
| 136 | + .lnB delete 1.0 end | |
| 137 | + .txtB delete 1.0 end | |
| 138 | + .mkr delete 1.0 end | |
| 139 | + .wfiles.lb delete 0 end | |
| 140 | + } | |
| 113 | 141 | |
| 114 | 142 | |
| 115 | 143 | set fromIndex [lsearch -glob $fossilcmd *-from] |
| 116 | 144 | set toIndex [lsearch -glob $fossilcmd *-to] |
| 117 | 145 | set branchIndex [lsearch -glob $fossilcmd *-branch] |
| @@ -459,11 +487,11 @@ | ||
| 459 | 487 | ::ttk::scrollbar .sby -command {.txtA yview} -orient vertical |
| 460 | 488 | ::ttk::scrollbar .sbxA -command {.txtA xview} -orient horizontal |
| 461 | 489 | ::ttk::scrollbar .sbxB -command {.txtB xview} -orient horizontal |
| 462 | 490 | frame .spacer |
| 463 | 491 | |
| 464 | -if {[readDiffs $fossilcmd] == 0} { | |
| 492 | +if {[readDiffs $fossilcmd 0] == 0} { | |
| 465 | 493 | tk_messageBox -type ok -title $CFG(TITLE) -message "No changes" |
| 466 | 494 | exit |
| 467 | 495 | } |
| 468 | 496 | update idletasks |
| 469 | 497 | |
| @@ -574,14 +602,15 @@ | ||
| 574 | 602 | $w tag config search -background {#fcc000} |
| 575 | 603 | } |
| 576 | 604 | set ::search $w |
| 577 | 605 | } |
| 578 | 606 | ::ttk::button .bb.quit -text {Quit} -command exit |
| 607 | +::ttk::button .bb.reload -text {Reload} -command reloadDiff | |
| 579 | 608 | ::ttk::button .bb.invert -text {Invert} -command invertDiff |
| 580 | 609 | ::ttk::button .bb.save -text {Save As...} -command saveDiff |
| 581 | 610 | ::ttk::button .bb.search -text {Search} -command searchOnOff |
| 582 | -pack .bb.quit .bb.invert -side left | |
| 611 | +pack .bb.quit .bb.reload .bb.invert -side left | |
| 583 | 612 | if {$fossilcmd!=""} {pack .bb.save -side left} |
| 584 | 613 | pack .bb.files .bb.search -side left |
| 585 | 614 | grid rowconfigure . 1 -weight 1 |
| 586 | 615 | grid columnconfigure . 1 -weight 1 |
| 587 | 616 | grid columnconfigure . 4 -weight 1 |
| 588 | 617 |
| --- src/diff.tcl | |
| +++ src/diff.tcl | |
| @@ -90,28 +90,56 @@ | |
| 90 | set x [lindex $difftxt $ii] |
| 91 | incr ii |
| 92 | return $x |
| 93 | } |
| 94 | |
| 95 | proc readDiffs {fossilcmd} { |
| 96 | global difftxt debug |
| 97 | if {![info exists difftxt]} { |
| 98 | if {$debug} { |
| 99 | puts "# [list open $fossilcmd r]" |
| 100 | flush stdout |
| 101 | } |
| 102 | set in [open $fossilcmd r] |
| 103 | fconfigure $in -encoding utf-8 |
| 104 | set difftxt [split [read $in] \n] |
| 105 | close $in |
| 106 | } |
| 107 | set N [llength $difftxt] |
| 108 | set ii 0 |
| 109 | set nDiffs 0 |
| 110 | set n1 0 |
| 111 | set n2 0 |
| 112 | array set widths {txt 3 ln 3 mkr 1} |
| 113 | |
| 114 | |
| 115 | set fromIndex [lsearch -glob $fossilcmd *-from] |
| 116 | set toIndex [lsearch -glob $fossilcmd *-to] |
| 117 | set branchIndex [lsearch -glob $fossilcmd *-branch] |
| @@ -459,11 +487,11 @@ | |
| 459 | ::ttk::scrollbar .sby -command {.txtA yview} -orient vertical |
| 460 | ::ttk::scrollbar .sbxA -command {.txtA xview} -orient horizontal |
| 461 | ::ttk::scrollbar .sbxB -command {.txtB xview} -orient horizontal |
| 462 | frame .spacer |
| 463 | |
| 464 | if {[readDiffs $fossilcmd] == 0} { |
| 465 | tk_messageBox -type ok -title $CFG(TITLE) -message "No changes" |
| 466 | exit |
| 467 | } |
| 468 | update idletasks |
| 469 | |
| @@ -574,14 +602,15 @@ | |
| 574 | $w tag config search -background {#fcc000} |
| 575 | } |
| 576 | set ::search $w |
| 577 | } |
| 578 | ::ttk::button .bb.quit -text {Quit} -command exit |
| 579 | ::ttk::button .bb.invert -text {Invert} -command invertDiff |
| 580 | ::ttk::button .bb.save -text {Save As...} -command saveDiff |
| 581 | ::ttk::button .bb.search -text {Search} -command searchOnOff |
| 582 | pack .bb.quit .bb.invert -side left |
| 583 | if {$fossilcmd!=""} {pack .bb.save -side left} |
| 584 | pack .bb.files .bb.search -side left |
| 585 | grid rowconfigure . 1 -weight 1 |
| 586 | grid columnconfigure . 1 -weight 1 |
| 587 | grid columnconfigure . 4 -weight 1 |
| 588 |
| --- src/diff.tcl | |
| +++ src/diff.tcl | |
| @@ -90,28 +90,56 @@ | |
| 90 | set x [lindex $difftxt $ii] |
| 91 | incr ii |
| 92 | return $x |
| 93 | } |
| 94 | |
| 95 | proc reloadDiff {} { |
| 96 | global fossilcmd difftxt |
| 97 | unset -nocomplain difftxt |
| 98 | set idx [.txtA index @0,0] |
| 99 | readDiffs $fossilcmd 1 |
| 100 | update |
| 101 | viewDiff $idx |
| 102 | } |
| 103 | |
| 104 | proc readDiffs {fossilcmd redo} { |
| 105 | global difftxt debug |
| 106 | if {![info exists difftxt]} { |
| 107 | if {$debug} { |
| 108 | puts "# [list open $fossilcmd r]" |
| 109 | flush stdout |
| 110 | } |
| 111 | if {[catch { |
| 112 | set in [open $fossilcmd r] |
| 113 | fconfigure $in -encoding utf-8 |
| 114 | set difftxt [split [read $in] \n] |
| 115 | close $in |
| 116 | } msg]} { |
| 117 | if {$redo} { |
| 118 | tk_messageBox -type ok -title Error -message "Unable to refresh:\n$msg" |
| 119 | return 0 |
| 120 | } else { |
| 121 | puts $msg |
| 122 | exit 1 |
| 123 | } |
| 124 | } |
| 125 | } |
| 126 | set N [llength $difftxt] |
| 127 | set ii 0 |
| 128 | set nDiffs 0 |
| 129 | set n1 0 |
| 130 | set n2 0 |
| 131 | array set widths {txt 3 ln 3 mkr 1} |
| 132 | if {$redo} { |
| 133 | foreach c [cols] {$c config -state normal} |
| 134 | .lnA delete 1.0 end |
| 135 | .txtA delete 1.0 end |
| 136 | .lnB delete 1.0 end |
| 137 | .txtB delete 1.0 end |
| 138 | .mkr delete 1.0 end |
| 139 | .wfiles.lb delete 0 end |
| 140 | } |
| 141 | |
| 142 | |
| 143 | set fromIndex [lsearch -glob $fossilcmd *-from] |
| 144 | set toIndex [lsearch -glob $fossilcmd *-to] |
| 145 | set branchIndex [lsearch -glob $fossilcmd *-branch] |
| @@ -459,11 +487,11 @@ | |
| 487 | ::ttk::scrollbar .sby -command {.txtA yview} -orient vertical |
| 488 | ::ttk::scrollbar .sbxA -command {.txtA xview} -orient horizontal |
| 489 | ::ttk::scrollbar .sbxB -command {.txtB xview} -orient horizontal |
| 490 | frame .spacer |
| 491 | |
| 492 | if {[readDiffs $fossilcmd 0] == 0} { |
| 493 | tk_messageBox -type ok -title $CFG(TITLE) -message "No changes" |
| 494 | exit |
| 495 | } |
| 496 | update idletasks |
| 497 | |
| @@ -574,14 +602,15 @@ | |
| 602 | $w tag config search -background {#fcc000} |
| 603 | } |
| 604 | set ::search $w |
| 605 | } |
| 606 | ::ttk::button .bb.quit -text {Quit} -command exit |
| 607 | ::ttk::button .bb.reload -text {Reload} -command reloadDiff |
| 608 | ::ttk::button .bb.invert -text {Invert} -command invertDiff |
| 609 | ::ttk::button .bb.save -text {Save As...} -command saveDiff |
| 610 | ::ttk::button .bb.search -text {Search} -command searchOnOff |
| 611 | pack .bb.quit .bb.reload .bb.invert -side left |
| 612 | if {$fossilcmd!=""} {pack .bb.save -side left} |
| 613 | pack .bb.files .bb.search -side left |
| 614 | grid rowconfigure . 1 -weight 1 |
| 615 | grid columnconfigure . 1 -weight 1 |
| 616 | grid columnconfigure . 4 -weight 1 |
| 617 |
+69
-30
| --- src/diffcmd.c | ||
| +++ src/diffcmd.c | ||
| @@ -127,16 +127,16 @@ | ||
| 127 | 127 | DIFF_HTML|DIFF_WEBPAGE|DIFF_BROWSER|DIFF_JSON|DIFF_TCL))==0 ){ |
| 128 | 128 | fossil_print("Fossil-Diff-From: %s\n", |
| 129 | 129 | zFrom[0]=='(' ? zFrom : mprintf("%S %s", |
| 130 | 130 | rid_to_uuid(symbolic_name_to_rid(zFrom, "ci")), |
| 131 | 131 | db_text("","SELECT datetime(%f)||' UTC'", |
| 132 | - symbolic_name_to_mtime(zFrom, 0)))); | |
| 132 | + symbolic_name_to_mtime(zFrom, 0, 0)))); | |
| 133 | 133 | fossil_print("Fossil-Diff-To: %s\n", |
| 134 | 134 | zTo[0]=='(' ? zTo : mprintf("%S %s", |
| 135 | 135 | rid_to_uuid(symbolic_name_to_rid(zTo, "ci")), |
| 136 | 136 | db_text("","SELECT datetime(%f)||' UTC'", |
| 137 | - symbolic_name_to_mtime(zTo, 0)))); | |
| 137 | + symbolic_name_to_mtime(zTo, 0, 1)))); | |
| 138 | 138 | fossil_print("%.66c\n", '-'); |
| 139 | 139 | } |
| 140 | 140 | } |
| 141 | 141 | |
| 142 | 142 | /* |
| @@ -587,20 +587,22 @@ | ||
| 587 | 587 | blob_read_from_file(&file2, zFile2, ExtFILE); |
| 588 | 588 | zName2 = zName; |
| 589 | 589 | } |
| 590 | 590 | |
| 591 | 591 | /* Compute and output the differences */ |
| 592 | - if( pCfg->diffFlags & DIFF_BRIEF ){ | |
| 592 | + if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){ | |
| 593 | 593 | if( blob_compare(pFile1, &file2) ){ |
| 594 | 594 | fossil_print("CHANGED %s\n", zName); |
| 595 | 595 | } |
| 596 | 596 | }else{ |
| 597 | 597 | blob_zero(&out); |
| 598 | 598 | text_diff(pFile1, &file2, &out, pCfg); |
| 599 | 599 | if( blob_size(&out) ){ |
| 600 | 600 | if( pCfg->diffFlags & DIFF_NUMSTAT ){ |
| 601 | - blob_appendf(pOut, "%s %s\n", blob_str(&out), zName); | |
| 601 | + if( !(pCfg->diffFlags & DIFF_BRIEF) ){ | |
| 602 | + blob_appendf(pOut, "%s %s\n", blob_str(&out), zName); | |
| 603 | + } | |
| 602 | 604 | }else{ |
| 603 | 605 | diff_print_filenames(zName, zName2, pCfg, pOut); |
| 604 | 606 | blob_appendf(pOut, "%s\n", blob_str(&out)); |
| 605 | 607 | } |
| 606 | 608 | } |
| @@ -665,11 +667,16 @@ | ||
| 665 | 667 | blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1); |
| 666 | 668 | blob_append_escaped_arg(&cmd, zFile2, 1); |
| 667 | 669 | } |
| 668 | 670 | |
| 669 | 671 | /* Run the external diff command */ |
| 670 | - fossil_system(blob_str(&cmd)); | |
| 672 | + if( fossil_system(blob_str(&cmd)) ){ | |
| 673 | +#if !defined(_WIN32) | |
| 674 | + /* On Windows, exit codes are unreliable. */ | |
| 675 | + fossil_warning("External diff command failed: %b\n", &cmd); | |
| 676 | +#endif | |
| 677 | + } | |
| 671 | 678 | |
| 672 | 679 | /* Delete the temporary file and clean up memory used */ |
| 673 | 680 | if( useTempfile ) file_delete(blob_str(&nameFile1)); |
| 674 | 681 | blob_reset(&nameFile1); |
| 675 | 682 | blob_reset(&cmd); |
| @@ -693,18 +700,22 @@ | ||
| 693 | 700 | Blob *pFile1, /* In memory content to compare from */ |
| 694 | 701 | Blob *pFile2, /* In memory content to compare to */ |
| 695 | 702 | const char *zName, /* Display name of the file */ |
| 696 | 703 | DiffConfig *pCfg /* Diff flags */ |
| 697 | 704 | ){ |
| 698 | - if( pCfg->diffFlags & DIFF_BRIEF ) return; | |
| 705 | + if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){ | |
| 706 | + return; | |
| 707 | + } | |
| 699 | 708 | if( pCfg->zDiffCmd==0 ){ |
| 700 | 709 | Blob out; /* Diff output text */ |
| 701 | 710 | |
| 702 | 711 | blob_zero(&out); |
| 703 | 712 | text_diff(pFile1, pFile2, &out, pCfg); |
| 704 | 713 | if( pCfg->diffFlags & DIFF_NUMSTAT ){ |
| 705 | - fossil_print("%s %s\n", blob_str(&out), zName); | |
| 714 | + if( !(pCfg->diffFlags & DIFF_BRIEF) ){ | |
| 715 | + fossil_print("%s %s\n", blob_str(&out), zName); | |
| 716 | + } | |
| 706 | 717 | }else{ |
| 707 | 718 | diff_print_filenames(zName, zName, pCfg, 0); |
| 708 | 719 | fossil_print("%s\n", blob_str(&out)); |
| 709 | 720 | } |
| 710 | 721 | |
| @@ -990,11 +1001,13 @@ | ||
| 990 | 1001 | }else if( pTo ){ |
| 991 | 1002 | zName = pTo->zName; |
| 992 | 1003 | }else{ |
| 993 | 1004 | zName = DIFF_NO_NAME; |
| 994 | 1005 | } |
| 995 | - if( pCfg->diffFlags & DIFF_BRIEF ) return; | |
| 1006 | + if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){ | |
| 1007 | + return; | |
| 1008 | + } | |
| 996 | 1009 | diff_print_index(zName, pCfg, 0); |
| 997 | 1010 | if( pFrom ){ |
| 998 | 1011 | rid = uuid_to_rid(pFrom->zUuid, 0); |
| 999 | 1012 | content_get(rid, &f1); |
| 1000 | 1013 | }else{ |
| @@ -1078,11 +1091,11 @@ | ||
| 1078 | 1091 | (void)file_dir_match(pFileDir, pFromFile->zName); /* Record name usage */ |
| 1079 | 1092 | pFromFile = manifest_file_next(pFrom,0); |
| 1080 | 1093 | pToFile = manifest_file_next(pTo,0); |
| 1081 | 1094 | }else{ |
| 1082 | 1095 | if( file_dir_match(pFileDir, pToFile->zName) ){ |
| 1083 | - if( pCfg->diffFlags & DIFF_BRIEF ){ | |
| 1096 | + if((pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT)){ | |
| 1084 | 1097 | fossil_print("CHANGED %s\n", pFromFile->zName); |
| 1085 | 1098 | }else{ |
| 1086 | 1099 | diff_manifest_entry(pFromFile, pToFile, pCfg); |
| 1087 | 1100 | } |
| 1088 | 1101 | } |
| @@ -1154,25 +1167,34 @@ | ||
| 1154 | 1167 | /* |
| 1155 | 1168 | ** Return the name of the external diff command, or return NULL if |
| 1156 | 1169 | ** no external diff command is defined. |
| 1157 | 1170 | */ |
| 1158 | 1171 | const char *diff_command_external(int guiDiff){ |
| 1159 | - const char *zDefault; | |
| 1160 | 1172 | const char *zName; |
| 1161 | - | |
| 1162 | - if( guiDiff ){ | |
| 1163 | -#if defined(_WIN32) | |
| 1164 | - zDefault = "WinDiff.exe"; | |
| 1165 | -#else | |
| 1166 | - zDefault = 0; | |
| 1167 | -#endif | |
| 1168 | - zName = "gdiff-command"; | |
| 1169 | - }else{ | |
| 1170 | - zDefault = 0; | |
| 1171 | - zName = "diff-command"; | |
| 1172 | - } | |
| 1173 | - return db_get(zName, zDefault); | |
| 1173 | + zName = guiDiff ? "gdiff-command" : "diff-command"; | |
| 1174 | + return db_get(zName, 0); | |
| 1175 | +} | |
| 1176 | + | |
| 1177 | +/* | |
| 1178 | +** Return true if it reasonable to run "diff -tk" for "gdiff". | |
| 1179 | +** | |
| 1180 | +** Details: Return true if all of the following are true: | |
| 1181 | +** | |
| 1182 | +** (1) The isGDiff flags is true | |
| 1183 | +** (2) The "gdiff-command" setting is undefined | |
| 1184 | +** (3) There is a "tclsh" on PATH | |
| 1185 | +** (4) There is a "wish" on PATH | |
| 1186 | +*/ | |
| 1187 | +int gdiff_using_tk(int isGdiff){ | |
| 1188 | + if( isGdiff | |
| 1189 | + && db_get("gdiff-command","")[0]==0 | |
| 1190 | + && fossil_app_on_path("tclsh",0) | |
| 1191 | + && fossil_app_on_path("wish",0) | |
| 1192 | + ){ | |
| 1193 | + return 1; | |
| 1194 | + } | |
| 1195 | + return 0; | |
| 1174 | 1196 | } |
| 1175 | 1197 | |
| 1176 | 1198 | /* |
| 1177 | 1199 | ** Show diff output in a Tcl/Tk window, in response to the --tk option |
| 1178 | 1200 | ** to the diff command. |
| @@ -1190,10 +1212,11 @@ | ||
| 1190 | 1212 | const char *zTempFile = 0; |
| 1191 | 1213 | char *zCmd; |
| 1192 | 1214 | const char *zTclsh; |
| 1193 | 1215 | int bDebug = find_option("tkdebug",0,0)!=0; |
| 1194 | 1216 | int bDarkMode = find_option("dark",0,0)!=0; |
| 1217 | + (void)find_option("debug",0,0); | |
| 1195 | 1218 | blob_zero(&script); |
| 1196 | 1219 | /* Caution: When this routine is called from the merge-info command, |
| 1197 | 1220 | ** the --tcl argument requires an argument. But merge-info does not |
| 1198 | 1221 | ** use -i, so we can take -i as that argument. This routine needs to |
| 1199 | 1222 | ** always have -i after --tcl. |
| @@ -1271,11 +1294,12 @@ | ||
| 1271 | 1294 | ** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...? |
| 1272 | 1295 | ** |
| 1273 | 1296 | ** Show the difference between the current version of each of the FILEs |
| 1274 | 1297 | ** specified (as they exist on disk) and that same file as it was checked- |
| 1275 | 1298 | ** out. Or if the FILE arguments are omitted, show all unsaved changes |
| 1276 | -** currently in the working check-out. | |
| 1299 | +** currently in the working check-out. The "gdiff" variant means to | |
| 1300 | +** to use a GUI diff. | |
| 1277 | 1301 | ** |
| 1278 | 1302 | ** The default output format is a "unified patch" (the same as the |
| 1279 | 1303 | ** output of "diff -u" on most unix systems). Many alternative formats |
| 1280 | 1304 | ** are available. A few of the more useful alternatives: |
| 1281 | 1305 | ** |
| @@ -1337,11 +1361,13 @@ | ||
| 1337 | 1361 | ** -i|--internal Use internal diff logic |
| 1338 | 1362 | ** --invert Invert the diff |
| 1339 | 1363 | ** --json Output formatted as JSON |
| 1340 | 1364 | ** -n|--linenum Show line numbers |
| 1341 | 1365 | ** -N|--new-file Alias for --verbose |
| 1342 | -** --numstat Show only the number of added and deleted lines | |
| 1366 | +** --numstat Show the number of added and deleted lines per | |
| 1367 | +** file, omitting the diff. When combined with | |
| 1368 | +** --brief, show only the total row. | |
| 1343 | 1369 | ** -y|--side-by-side Side-by-side diff |
| 1344 | 1370 | ** --strip-trailing-cr Strip trailing CR |
| 1345 | 1371 | ** --tcl Tcl-formatted output used internally by --tk |
| 1346 | 1372 | ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") |
| 1347 | 1373 | ** --tk Launch a Tcl/Tk GUI for display |
| @@ -1363,15 +1389,15 @@ | ||
| 1363 | 1389 | int againstUndo = 0; /* Diff against files in the undo buffer */ |
| 1364 | 1390 | FileDirList *pFileDir = 0; /* Restrict the diff to these files */ |
| 1365 | 1391 | DiffConfig DCfg; /* Diff configuration object */ |
| 1366 | 1392 | int bFromIsDir = 0; /* True if zFrom is a directory name */ |
| 1367 | 1393 | |
| 1368 | - if( find_option("tk",0,0)!=0 || has_option("tclsh") ){ | |
| 1394 | + isGDiff = g.argv[1][0]=='g'; | |
| 1395 | + if( find_option("tk",0,0)!=0|| has_option("tclsh") ){ | |
| 1369 | 1396 | diff_tk("diff", 2); |
| 1370 | 1397 | return; |
| 1371 | 1398 | } |
| 1372 | - isGDiff = g.argv[1][0]=='g'; | |
| 1373 | 1399 | zFrom = find_option("from", "r", 1); |
| 1374 | 1400 | zTo = find_option("to", 0, 1); |
| 1375 | 1401 | zCheckin = find_option("checkin", "ci", 1); |
| 1376 | 1402 | zBranch = find_option("branch", 0, 1); |
| 1377 | 1403 | againstUndo = find_option("undo",0,0)!=0; |
| @@ -1383,10 +1409,11 @@ | ||
| 1383 | 1409 | if( zTo || zFrom || zCheckin ){ |
| 1384 | 1410 | fossil_fatal("cannot use --from, --to, or --checkin with --branch"); |
| 1385 | 1411 | } |
| 1386 | 1412 | zTo = zBranch; |
| 1387 | 1413 | zFrom = mprintf("root:%s", zBranch); |
| 1414 | + zBranch = 0; | |
| 1388 | 1415 | } |
| 1389 | 1416 | if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){ |
| 1390 | 1417 | fossil_fatal("cannot use --checkin together with --from or --to"); |
| 1391 | 1418 | } |
| 1392 | 1419 | if( 0==zCheckin ){ |
| @@ -1397,10 +1424,19 @@ | ||
| 1397 | 1424 | }else{ |
| 1398 | 1425 | db_find_and_open_repository(0, 0); |
| 1399 | 1426 | } |
| 1400 | 1427 | }else{ |
| 1401 | 1428 | db_find_and_open_repository(0, 0); |
| 1429 | + } | |
| 1430 | + if( gdiff_using_tk(isGDiff) ){ | |
| 1431 | + restore_option("--from", zFrom, 1); | |
| 1432 | + restore_option("--to", zTo, 1); | |
| 1433 | + restore_option("--checkin", zCheckin, 1); | |
| 1434 | + restore_option("--branch", zBranch, 1); | |
| 1435 | + if( againstUndo ) restore_option("--undo", 0, 0); | |
| 1436 | + diff_tk("diff", 2); | |
| 1437 | + return; | |
| 1402 | 1438 | } |
| 1403 | 1439 | determine_exec_relative_option(1); |
| 1404 | 1440 | if( zFrom!=file_tail(zFrom) |
| 1405 | 1441 | && file_isdir(zFrom, ExtFILE)==1 |
| 1406 | 1442 | && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom) |
| @@ -1429,11 +1465,14 @@ | ||
| 1429 | 1465 | pFileDir[i-2].nName = blob_size(&fname); |
| 1430 | 1466 | pFileDir[i-2].nUsed = 0; |
| 1431 | 1467 | blob_reset(&fname); |
| 1432 | 1468 | } |
| 1433 | 1469 | } |
| 1434 | - if ( zCheckin!=0 ){ | |
| 1470 | + if( DCfg.diffFlags & DIFF_NUMSTAT ){ | |
| 1471 | + fossil_print("%10s %10s\n", "INSERTED", "DELETED"); | |
| 1472 | + } | |
| 1473 | + if( zCheckin!=0 ){ | |
| 1435 | 1474 | int ridTo = name_to_typed_rid(zCheckin, "ci"); |
| 1436 | 1475 | zTo = zCheckin; |
| 1437 | 1476 | zFrom = db_text(0, |
| 1438 | 1477 | "SELECT uuid FROM blob, plink" |
| 1439 | 1478 | " WHERE plink.cid=%d AND plink.isprim AND plink.pid=blob.rid", |
| @@ -1469,12 +1508,12 @@ | ||
| 1469 | 1508 | } |
| 1470 | 1509 | fossil_free(pFileDir); |
| 1471 | 1510 | } |
| 1472 | 1511 | diff_end(&DCfg, 0); |
| 1473 | 1512 | if ( DCfg.diffFlags & DIFF_NUMSTAT ){ |
| 1474 | - fossil_print("%10d %10d TOTAL over %d changed files\n", | |
| 1475 | - g.diffCnt[1], g.diffCnt[2], g.diffCnt[0]); | |
| 1513 | + fossil_print("%10d %10d TOTAL over %d changed file%s\n", | |
| 1514 | + g.diffCnt[1], g.diffCnt[2], g.diffCnt[0], g.diffCnt[0]!=1 ? "s": ""); | |
| 1476 | 1515 | } |
| 1477 | 1516 | } |
| 1478 | 1517 | |
| 1479 | 1518 | /* |
| 1480 | 1519 | ** WEBPAGE: vpatch |
| 1481 | 1520 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -127,16 +127,16 @@ | |
| 127 | DIFF_HTML|DIFF_WEBPAGE|DIFF_BROWSER|DIFF_JSON|DIFF_TCL))==0 ){ |
| 128 | fossil_print("Fossil-Diff-From: %s\n", |
| 129 | zFrom[0]=='(' ? zFrom : mprintf("%S %s", |
| 130 | rid_to_uuid(symbolic_name_to_rid(zFrom, "ci")), |
| 131 | db_text("","SELECT datetime(%f)||' UTC'", |
| 132 | symbolic_name_to_mtime(zFrom, 0)))); |
| 133 | fossil_print("Fossil-Diff-To: %s\n", |
| 134 | zTo[0]=='(' ? zTo : mprintf("%S %s", |
| 135 | rid_to_uuid(symbolic_name_to_rid(zTo, "ci")), |
| 136 | db_text("","SELECT datetime(%f)||' UTC'", |
| 137 | symbolic_name_to_mtime(zTo, 0)))); |
| 138 | fossil_print("%.66c\n", '-'); |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | /* |
| @@ -587,20 +587,22 @@ | |
| 587 | blob_read_from_file(&file2, zFile2, ExtFILE); |
| 588 | zName2 = zName; |
| 589 | } |
| 590 | |
| 591 | /* Compute and output the differences */ |
| 592 | if( pCfg->diffFlags & DIFF_BRIEF ){ |
| 593 | if( blob_compare(pFile1, &file2) ){ |
| 594 | fossil_print("CHANGED %s\n", zName); |
| 595 | } |
| 596 | }else{ |
| 597 | blob_zero(&out); |
| 598 | text_diff(pFile1, &file2, &out, pCfg); |
| 599 | if( blob_size(&out) ){ |
| 600 | if( pCfg->diffFlags & DIFF_NUMSTAT ){ |
| 601 | blob_appendf(pOut, "%s %s\n", blob_str(&out), zName); |
| 602 | }else{ |
| 603 | diff_print_filenames(zName, zName2, pCfg, pOut); |
| 604 | blob_appendf(pOut, "%s\n", blob_str(&out)); |
| 605 | } |
| 606 | } |
| @@ -665,11 +667,16 @@ | |
| 665 | blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1); |
| 666 | blob_append_escaped_arg(&cmd, zFile2, 1); |
| 667 | } |
| 668 | |
| 669 | /* Run the external diff command */ |
| 670 | fossil_system(blob_str(&cmd)); |
| 671 | |
| 672 | /* Delete the temporary file and clean up memory used */ |
| 673 | if( useTempfile ) file_delete(blob_str(&nameFile1)); |
| 674 | blob_reset(&nameFile1); |
| 675 | blob_reset(&cmd); |
| @@ -693,18 +700,22 @@ | |
| 693 | Blob *pFile1, /* In memory content to compare from */ |
| 694 | Blob *pFile2, /* In memory content to compare to */ |
| 695 | const char *zName, /* Display name of the file */ |
| 696 | DiffConfig *pCfg /* Diff flags */ |
| 697 | ){ |
| 698 | if( pCfg->diffFlags & DIFF_BRIEF ) return; |
| 699 | if( pCfg->zDiffCmd==0 ){ |
| 700 | Blob out; /* Diff output text */ |
| 701 | |
| 702 | blob_zero(&out); |
| 703 | text_diff(pFile1, pFile2, &out, pCfg); |
| 704 | if( pCfg->diffFlags & DIFF_NUMSTAT ){ |
| 705 | fossil_print("%s %s\n", blob_str(&out), zName); |
| 706 | }else{ |
| 707 | diff_print_filenames(zName, zName, pCfg, 0); |
| 708 | fossil_print("%s\n", blob_str(&out)); |
| 709 | } |
| 710 | |
| @@ -990,11 +1001,13 @@ | |
| 990 | }else if( pTo ){ |
| 991 | zName = pTo->zName; |
| 992 | }else{ |
| 993 | zName = DIFF_NO_NAME; |
| 994 | } |
| 995 | if( pCfg->diffFlags & DIFF_BRIEF ) return; |
| 996 | diff_print_index(zName, pCfg, 0); |
| 997 | if( pFrom ){ |
| 998 | rid = uuid_to_rid(pFrom->zUuid, 0); |
| 999 | content_get(rid, &f1); |
| 1000 | }else{ |
| @@ -1078,11 +1091,11 @@ | |
| 1078 | (void)file_dir_match(pFileDir, pFromFile->zName); /* Record name usage */ |
| 1079 | pFromFile = manifest_file_next(pFrom,0); |
| 1080 | pToFile = manifest_file_next(pTo,0); |
| 1081 | }else{ |
| 1082 | if( file_dir_match(pFileDir, pToFile->zName) ){ |
| 1083 | if( pCfg->diffFlags & DIFF_BRIEF ){ |
| 1084 | fossil_print("CHANGED %s\n", pFromFile->zName); |
| 1085 | }else{ |
| 1086 | diff_manifest_entry(pFromFile, pToFile, pCfg); |
| 1087 | } |
| 1088 | } |
| @@ -1154,25 +1167,34 @@ | |
| 1154 | /* |
| 1155 | ** Return the name of the external diff command, or return NULL if |
| 1156 | ** no external diff command is defined. |
| 1157 | */ |
| 1158 | const char *diff_command_external(int guiDiff){ |
| 1159 | const char *zDefault; |
| 1160 | const char *zName; |
| 1161 | |
| 1162 | if( guiDiff ){ |
| 1163 | #if defined(_WIN32) |
| 1164 | zDefault = "WinDiff.exe"; |
| 1165 | #else |
| 1166 | zDefault = 0; |
| 1167 | #endif |
| 1168 | zName = "gdiff-command"; |
| 1169 | }else{ |
| 1170 | zDefault = 0; |
| 1171 | zName = "diff-command"; |
| 1172 | } |
| 1173 | return db_get(zName, zDefault); |
| 1174 | } |
| 1175 | |
| 1176 | /* |
| 1177 | ** Show diff output in a Tcl/Tk window, in response to the --tk option |
| 1178 | ** to the diff command. |
| @@ -1190,10 +1212,11 @@ | |
| 1190 | const char *zTempFile = 0; |
| 1191 | char *zCmd; |
| 1192 | const char *zTclsh; |
| 1193 | int bDebug = find_option("tkdebug",0,0)!=0; |
| 1194 | int bDarkMode = find_option("dark",0,0)!=0; |
| 1195 | blob_zero(&script); |
| 1196 | /* Caution: When this routine is called from the merge-info command, |
| 1197 | ** the --tcl argument requires an argument. But merge-info does not |
| 1198 | ** use -i, so we can take -i as that argument. This routine needs to |
| 1199 | ** always have -i after --tcl. |
| @@ -1271,11 +1294,12 @@ | |
| 1271 | ** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...? |
| 1272 | ** |
| 1273 | ** Show the difference between the current version of each of the FILEs |
| 1274 | ** specified (as they exist on disk) and that same file as it was checked- |
| 1275 | ** out. Or if the FILE arguments are omitted, show all unsaved changes |
| 1276 | ** currently in the working check-out. |
| 1277 | ** |
| 1278 | ** The default output format is a "unified patch" (the same as the |
| 1279 | ** output of "diff -u" on most unix systems). Many alternative formats |
| 1280 | ** are available. A few of the more useful alternatives: |
| 1281 | ** |
| @@ -1337,11 +1361,13 @@ | |
| 1337 | ** -i|--internal Use internal diff logic |
| 1338 | ** --invert Invert the diff |
| 1339 | ** --json Output formatted as JSON |
| 1340 | ** -n|--linenum Show line numbers |
| 1341 | ** -N|--new-file Alias for --verbose |
| 1342 | ** --numstat Show only the number of added and deleted lines |
| 1343 | ** -y|--side-by-side Side-by-side diff |
| 1344 | ** --strip-trailing-cr Strip trailing CR |
| 1345 | ** --tcl Tcl-formatted output used internally by --tk |
| 1346 | ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") |
| 1347 | ** --tk Launch a Tcl/Tk GUI for display |
| @@ -1363,15 +1389,15 @@ | |
| 1363 | int againstUndo = 0; /* Diff against files in the undo buffer */ |
| 1364 | FileDirList *pFileDir = 0; /* Restrict the diff to these files */ |
| 1365 | DiffConfig DCfg; /* Diff configuration object */ |
| 1366 | int bFromIsDir = 0; /* True if zFrom is a directory name */ |
| 1367 | |
| 1368 | if( find_option("tk",0,0)!=0 || has_option("tclsh") ){ |
| 1369 | diff_tk("diff", 2); |
| 1370 | return; |
| 1371 | } |
| 1372 | isGDiff = g.argv[1][0]=='g'; |
| 1373 | zFrom = find_option("from", "r", 1); |
| 1374 | zTo = find_option("to", 0, 1); |
| 1375 | zCheckin = find_option("checkin", "ci", 1); |
| 1376 | zBranch = find_option("branch", 0, 1); |
| 1377 | againstUndo = find_option("undo",0,0)!=0; |
| @@ -1383,10 +1409,11 @@ | |
| 1383 | if( zTo || zFrom || zCheckin ){ |
| 1384 | fossil_fatal("cannot use --from, --to, or --checkin with --branch"); |
| 1385 | } |
| 1386 | zTo = zBranch; |
| 1387 | zFrom = mprintf("root:%s", zBranch); |
| 1388 | } |
| 1389 | if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){ |
| 1390 | fossil_fatal("cannot use --checkin together with --from or --to"); |
| 1391 | } |
| 1392 | if( 0==zCheckin ){ |
| @@ -1397,10 +1424,19 @@ | |
| 1397 | }else{ |
| 1398 | db_find_and_open_repository(0, 0); |
| 1399 | } |
| 1400 | }else{ |
| 1401 | db_find_and_open_repository(0, 0); |
| 1402 | } |
| 1403 | determine_exec_relative_option(1); |
| 1404 | if( zFrom!=file_tail(zFrom) |
| 1405 | && file_isdir(zFrom, ExtFILE)==1 |
| 1406 | && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom) |
| @@ -1429,11 +1465,14 @@ | |
| 1429 | pFileDir[i-2].nName = blob_size(&fname); |
| 1430 | pFileDir[i-2].nUsed = 0; |
| 1431 | blob_reset(&fname); |
| 1432 | } |
| 1433 | } |
| 1434 | if ( zCheckin!=0 ){ |
| 1435 | int ridTo = name_to_typed_rid(zCheckin, "ci"); |
| 1436 | zTo = zCheckin; |
| 1437 | zFrom = db_text(0, |
| 1438 | "SELECT uuid FROM blob, plink" |
| 1439 | " WHERE plink.cid=%d AND plink.isprim AND plink.pid=blob.rid", |
| @@ -1469,12 +1508,12 @@ | |
| 1469 | } |
| 1470 | fossil_free(pFileDir); |
| 1471 | } |
| 1472 | diff_end(&DCfg, 0); |
| 1473 | if ( DCfg.diffFlags & DIFF_NUMSTAT ){ |
| 1474 | fossil_print("%10d %10d TOTAL over %d changed files\n", |
| 1475 | g.diffCnt[1], g.diffCnt[2], g.diffCnt[0]); |
| 1476 | } |
| 1477 | } |
| 1478 | |
| 1479 | /* |
| 1480 | ** WEBPAGE: vpatch |
| 1481 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -127,16 +127,16 @@ | |
| 127 | DIFF_HTML|DIFF_WEBPAGE|DIFF_BROWSER|DIFF_JSON|DIFF_TCL))==0 ){ |
| 128 | fossil_print("Fossil-Diff-From: %s\n", |
| 129 | zFrom[0]=='(' ? zFrom : mprintf("%S %s", |
| 130 | rid_to_uuid(symbolic_name_to_rid(zFrom, "ci")), |
| 131 | db_text("","SELECT datetime(%f)||' UTC'", |
| 132 | symbolic_name_to_mtime(zFrom, 0, 0)))); |
| 133 | fossil_print("Fossil-Diff-To: %s\n", |
| 134 | zTo[0]=='(' ? zTo : mprintf("%S %s", |
| 135 | rid_to_uuid(symbolic_name_to_rid(zTo, "ci")), |
| 136 | db_text("","SELECT datetime(%f)||' UTC'", |
| 137 | symbolic_name_to_mtime(zTo, 0, 1)))); |
| 138 | fossil_print("%.66c\n", '-'); |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | /* |
| @@ -587,20 +587,22 @@ | |
| 587 | blob_read_from_file(&file2, zFile2, ExtFILE); |
| 588 | zName2 = zName; |
| 589 | } |
| 590 | |
| 591 | /* Compute and output the differences */ |
| 592 | if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){ |
| 593 | if( blob_compare(pFile1, &file2) ){ |
| 594 | fossil_print("CHANGED %s\n", zName); |
| 595 | } |
| 596 | }else{ |
| 597 | blob_zero(&out); |
| 598 | text_diff(pFile1, &file2, &out, pCfg); |
| 599 | if( blob_size(&out) ){ |
| 600 | if( pCfg->diffFlags & DIFF_NUMSTAT ){ |
| 601 | if( !(pCfg->diffFlags & DIFF_BRIEF) ){ |
| 602 | blob_appendf(pOut, "%s %s\n", blob_str(&out), zName); |
| 603 | } |
| 604 | }else{ |
| 605 | diff_print_filenames(zName, zName2, pCfg, pOut); |
| 606 | blob_appendf(pOut, "%s\n", blob_str(&out)); |
| 607 | } |
| 608 | } |
| @@ -665,11 +667,16 @@ | |
| 667 | blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1); |
| 668 | blob_append_escaped_arg(&cmd, zFile2, 1); |
| 669 | } |
| 670 | |
| 671 | /* Run the external diff command */ |
| 672 | if( fossil_system(blob_str(&cmd)) ){ |
| 673 | #if !defined(_WIN32) |
| 674 | /* On Windows, exit codes are unreliable. */ |
| 675 | fossil_warning("External diff command failed: %b\n", &cmd); |
| 676 | #endif |
| 677 | } |
| 678 | |
| 679 | /* Delete the temporary file and clean up memory used */ |
| 680 | if( useTempfile ) file_delete(blob_str(&nameFile1)); |
| 681 | blob_reset(&nameFile1); |
| 682 | blob_reset(&cmd); |
| @@ -693,18 +700,22 @@ | |
| 700 | Blob *pFile1, /* In memory content to compare from */ |
| 701 | Blob *pFile2, /* In memory content to compare to */ |
| 702 | const char *zName, /* Display name of the file */ |
| 703 | DiffConfig *pCfg /* Diff flags */ |
| 704 | ){ |
| 705 | if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){ |
| 706 | return; |
| 707 | } |
| 708 | if( pCfg->zDiffCmd==0 ){ |
| 709 | Blob out; /* Diff output text */ |
| 710 | |
| 711 | blob_zero(&out); |
| 712 | text_diff(pFile1, pFile2, &out, pCfg); |
| 713 | if( pCfg->diffFlags & DIFF_NUMSTAT ){ |
| 714 | if( !(pCfg->diffFlags & DIFF_BRIEF) ){ |
| 715 | fossil_print("%s %s\n", blob_str(&out), zName); |
| 716 | } |
| 717 | }else{ |
| 718 | diff_print_filenames(zName, zName, pCfg, 0); |
| 719 | fossil_print("%s\n", blob_str(&out)); |
| 720 | } |
| 721 | |
| @@ -990,11 +1001,13 @@ | |
| 1001 | }else if( pTo ){ |
| 1002 | zName = pTo->zName; |
| 1003 | }else{ |
| 1004 | zName = DIFF_NO_NAME; |
| 1005 | } |
| 1006 | if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){ |
| 1007 | return; |
| 1008 | } |
| 1009 | diff_print_index(zName, pCfg, 0); |
| 1010 | if( pFrom ){ |
| 1011 | rid = uuid_to_rid(pFrom->zUuid, 0); |
| 1012 | content_get(rid, &f1); |
| 1013 | }else{ |
| @@ -1078,11 +1091,11 @@ | |
| 1091 | (void)file_dir_match(pFileDir, pFromFile->zName); /* Record name usage */ |
| 1092 | pFromFile = manifest_file_next(pFrom,0); |
| 1093 | pToFile = manifest_file_next(pTo,0); |
| 1094 | }else{ |
| 1095 | if( file_dir_match(pFileDir, pToFile->zName) ){ |
| 1096 | if((pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT)){ |
| 1097 | fossil_print("CHANGED %s\n", pFromFile->zName); |
| 1098 | }else{ |
| 1099 | diff_manifest_entry(pFromFile, pToFile, pCfg); |
| 1100 | } |
| 1101 | } |
| @@ -1154,25 +1167,34 @@ | |
| 1167 | /* |
| 1168 | ** Return the name of the external diff command, or return NULL if |
| 1169 | ** no external diff command is defined. |
| 1170 | */ |
| 1171 | const char *diff_command_external(int guiDiff){ |
| 1172 | const char *zName; |
| 1173 | zName = guiDiff ? "gdiff-command" : "diff-command"; |
| 1174 | return db_get(zName, 0); |
| 1175 | } |
| 1176 | |
| 1177 | /* |
| 1178 | ** Return true if it reasonable to run "diff -tk" for "gdiff". |
| 1179 | ** |
| 1180 | ** Details: Return true if all of the following are true: |
| 1181 | ** |
| 1182 | ** (1) The isGDiff flags is true |
| 1183 | ** (2) The "gdiff-command" setting is undefined |
| 1184 | ** (3) There is a "tclsh" on PATH |
| 1185 | ** (4) There is a "wish" on PATH |
| 1186 | */ |
| 1187 | int gdiff_using_tk(int isGdiff){ |
| 1188 | if( isGdiff |
| 1189 | && db_get("gdiff-command","")[0]==0 |
| 1190 | && fossil_app_on_path("tclsh",0) |
| 1191 | && fossil_app_on_path("wish",0) |
| 1192 | ){ |
| 1193 | return 1; |
| 1194 | } |
| 1195 | return 0; |
| 1196 | } |
| 1197 | |
| 1198 | /* |
| 1199 | ** Show diff output in a Tcl/Tk window, in response to the --tk option |
| 1200 | ** to the diff command. |
| @@ -1190,10 +1212,11 @@ | |
| 1212 | const char *zTempFile = 0; |
| 1213 | char *zCmd; |
| 1214 | const char *zTclsh; |
| 1215 | int bDebug = find_option("tkdebug",0,0)!=0; |
| 1216 | int bDarkMode = find_option("dark",0,0)!=0; |
| 1217 | (void)find_option("debug",0,0); |
| 1218 | blob_zero(&script); |
| 1219 | /* Caution: When this routine is called from the merge-info command, |
| 1220 | ** the --tcl argument requires an argument. But merge-info does not |
| 1221 | ** use -i, so we can take -i as that argument. This routine needs to |
| 1222 | ** always have -i after --tcl. |
| @@ -1271,11 +1294,12 @@ | |
| 1294 | ** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...? |
| 1295 | ** |
| 1296 | ** Show the difference between the current version of each of the FILEs |
| 1297 | ** specified (as they exist on disk) and that same file as it was checked- |
| 1298 | ** out. Or if the FILE arguments are omitted, show all unsaved changes |
| 1299 | ** currently in the working check-out. The "gdiff" variant means to |
| 1300 | ** to use a GUI diff. |
| 1301 | ** |
| 1302 | ** The default output format is a "unified patch" (the same as the |
| 1303 | ** output of "diff -u" on most unix systems). Many alternative formats |
| 1304 | ** are available. A few of the more useful alternatives: |
| 1305 | ** |
| @@ -1337,11 +1361,13 @@ | |
| 1361 | ** -i|--internal Use internal diff logic |
| 1362 | ** --invert Invert the diff |
| 1363 | ** --json Output formatted as JSON |
| 1364 | ** -n|--linenum Show line numbers |
| 1365 | ** -N|--new-file Alias for --verbose |
| 1366 | ** --numstat Show the number of added and deleted lines per |
| 1367 | ** file, omitting the diff. When combined with |
| 1368 | ** --brief, show only the total row. |
| 1369 | ** -y|--side-by-side Side-by-side diff |
| 1370 | ** --strip-trailing-cr Strip trailing CR |
| 1371 | ** --tcl Tcl-formatted output used internally by --tk |
| 1372 | ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") |
| 1373 | ** --tk Launch a Tcl/Tk GUI for display |
| @@ -1363,15 +1389,15 @@ | |
| 1389 | int againstUndo = 0; /* Diff against files in the undo buffer */ |
| 1390 | FileDirList *pFileDir = 0; /* Restrict the diff to these files */ |
| 1391 | DiffConfig DCfg; /* Diff configuration object */ |
| 1392 | int bFromIsDir = 0; /* True if zFrom is a directory name */ |
| 1393 | |
| 1394 | isGDiff = g.argv[1][0]=='g'; |
| 1395 | if( find_option("tk",0,0)!=0|| has_option("tclsh") ){ |
| 1396 | diff_tk("diff", 2); |
| 1397 | return; |
| 1398 | } |
| 1399 | zFrom = find_option("from", "r", 1); |
| 1400 | zTo = find_option("to", 0, 1); |
| 1401 | zCheckin = find_option("checkin", "ci", 1); |
| 1402 | zBranch = find_option("branch", 0, 1); |
| 1403 | againstUndo = find_option("undo",0,0)!=0; |
| @@ -1383,10 +1409,11 @@ | |
| 1409 | if( zTo || zFrom || zCheckin ){ |
| 1410 | fossil_fatal("cannot use --from, --to, or --checkin with --branch"); |
| 1411 | } |
| 1412 | zTo = zBranch; |
| 1413 | zFrom = mprintf("root:%s", zBranch); |
| 1414 | zBranch = 0; |
| 1415 | } |
| 1416 | if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){ |
| 1417 | fossil_fatal("cannot use --checkin together with --from or --to"); |
| 1418 | } |
| 1419 | if( 0==zCheckin ){ |
| @@ -1397,10 +1424,19 @@ | |
| 1424 | }else{ |
| 1425 | db_find_and_open_repository(0, 0); |
| 1426 | } |
| 1427 | }else{ |
| 1428 | db_find_and_open_repository(0, 0); |
| 1429 | } |
| 1430 | if( gdiff_using_tk(isGDiff) ){ |
| 1431 | restore_option("--from", zFrom, 1); |
| 1432 | restore_option("--to", zTo, 1); |
| 1433 | restore_option("--checkin", zCheckin, 1); |
| 1434 | restore_option("--branch", zBranch, 1); |
| 1435 | if( againstUndo ) restore_option("--undo", 0, 0); |
| 1436 | diff_tk("diff", 2); |
| 1437 | return; |
| 1438 | } |
| 1439 | determine_exec_relative_option(1); |
| 1440 | if( zFrom!=file_tail(zFrom) |
| 1441 | && file_isdir(zFrom, ExtFILE)==1 |
| 1442 | && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom) |
| @@ -1429,11 +1465,14 @@ | |
| 1465 | pFileDir[i-2].nName = blob_size(&fname); |
| 1466 | pFileDir[i-2].nUsed = 0; |
| 1467 | blob_reset(&fname); |
| 1468 | } |
| 1469 | } |
| 1470 | if( DCfg.diffFlags & DIFF_NUMSTAT ){ |
| 1471 | fossil_print("%10s %10s\n", "INSERTED", "DELETED"); |
| 1472 | } |
| 1473 | if( zCheckin!=0 ){ |
| 1474 | int ridTo = name_to_typed_rid(zCheckin, "ci"); |
| 1475 | zTo = zCheckin; |
| 1476 | zFrom = db_text(0, |
| 1477 | "SELECT uuid FROM blob, plink" |
| 1478 | " WHERE plink.cid=%d AND plink.isprim AND plink.pid=blob.rid", |
| @@ -1469,12 +1508,12 @@ | |
| 1508 | } |
| 1509 | fossil_free(pFileDir); |
| 1510 | } |
| 1511 | diff_end(&DCfg, 0); |
| 1512 | if ( DCfg.diffFlags & DIFF_NUMSTAT ){ |
| 1513 | fossil_print("%10d %10d TOTAL over %d changed file%s\n", |
| 1514 | g.diffCnt[1], g.diffCnt[2], g.diffCnt[0], g.diffCnt[0]!=1 ? "s": ""); |
| 1515 | } |
| 1516 | } |
| 1517 | |
| 1518 | /* |
| 1519 | ** WEBPAGE: vpatch |
| 1520 |
+31
-1
| --- src/dispatch.c | ||
| +++ src/dispatch.c | ||
| @@ -811,10 +811,32 @@ | ||
| 811 | 811 | fossil_print(" %s\n", az[j]); |
| 812 | 812 | } |
| 813 | 813 | } |
| 814 | 814 | } |
| 815 | 815 | |
| 816 | + | |
| 817 | +/* | |
| 818 | +** Returns 1 if the command or page name zName is known to be a | |
| 819 | +** command/page which is only available in certain builds/platforms, | |
| 820 | +** else returns 0. | |
| 821 | +*/ | |
| 822 | +static int help_is_platform_command(const char *zName){ | |
| 823 | + const char *aList[] = { | |
| 824 | + /* List of commands/pages which are known to only be available in | |
| 825 | + ** certain builds/platforms. */ | |
| 826 | + "winsrv", | |
| 827 | + "json", "/json", | |
| 828 | + NULL /* end-of-list sentinel */ | |
| 829 | + }; | |
| 830 | + int i = 0; | |
| 831 | + const char *z; | |
| 832 | + for( z = aList[0]; z ; z = aList[++i] ){ | |
| 833 | + if( 0==fossil_strcmp(zName, z) ) return 1; | |
| 834 | + } | |
| 835 | + return 0; | |
| 836 | +} | |
| 837 | + | |
| 816 | 838 | /* |
| 817 | 839 | ** WEBPAGE: help |
| 818 | 840 | ** URL: /help?name=CMD |
| 819 | 841 | ** |
| 820 | 842 | ** Show the built-in help text for CMD. CMD can be a command-line interface |
| @@ -860,11 +882,15 @@ | ||
| 860 | 882 | @ <h1>The "%h(pCmd->zName)" setting:</h1> |
| 861 | 883 | }else{ |
| 862 | 884 | @ <h1>The "%h(pCmd->zName)" command:</h1> |
| 863 | 885 | } |
| 864 | 886 | if( rc==1 || (rc==2 && zCmd[0]=='/') ){ |
| 865 | - @ Unknown topic: "%h(zCmd)" | |
| 887 | + if( zCmd && help_is_platform_command(zCmd) ){ | |
| 888 | + @ Not available in this build: "%h(zCmd)" | |
| 889 | + }else{ | |
| 890 | + @ Unknown topic: "%h(zCmd)" | |
| 891 | + } | |
| 866 | 892 | }else if( rc==2 ){ |
| 867 | 893 | @ Ambiguous prefix: "%h(zCmd)" |
| 868 | 894 | }else{ |
| 869 | 895 | if( pCmd->zHelp[0]==0 ){ |
| 870 | 896 | @ No help available for "%h(pCmd->zName)" |
| @@ -1518,10 +1544,14 @@ | ||
| 1518 | 1544 | rc = dispatch_name_search(g.argv[2], mask|CMDFLAG_PREFIX, &pCmd); |
| 1519 | 1545 | if( rc ){ |
| 1520 | 1546 | int i, n; |
| 1521 | 1547 | const char *az[5]; |
| 1522 | 1548 | if( rc==1 ){ |
| 1549 | + if( help_is_platform_command(g.argv[2]) ){ | |
| 1550 | + fossil_print("Not available in this build: %s\n", g.argv[2]); | |
| 1551 | + return; | |
| 1552 | + } | |
| 1523 | 1553 | fossil_print("unknown %s: %s\n", zCmdOrPage, g.argv[2]); |
| 1524 | 1554 | }else{ |
| 1525 | 1555 | fossil_print("ambiguous %s prefix: %s\n", |
| 1526 | 1556 | zCmdOrPage, g.argv[2]); |
| 1527 | 1557 | } |
| 1528 | 1558 |
| --- src/dispatch.c | |
| +++ src/dispatch.c | |
| @@ -811,10 +811,32 @@ | |
| 811 | fossil_print(" %s\n", az[j]); |
| 812 | } |
| 813 | } |
| 814 | } |
| 815 | |
| 816 | /* |
| 817 | ** WEBPAGE: help |
| 818 | ** URL: /help?name=CMD |
| 819 | ** |
| 820 | ** Show the built-in help text for CMD. CMD can be a command-line interface |
| @@ -860,11 +882,15 @@ | |
| 860 | @ <h1>The "%h(pCmd->zName)" setting:</h1> |
| 861 | }else{ |
| 862 | @ <h1>The "%h(pCmd->zName)" command:</h1> |
| 863 | } |
| 864 | if( rc==1 || (rc==2 && zCmd[0]=='/') ){ |
| 865 | @ Unknown topic: "%h(zCmd)" |
| 866 | }else if( rc==2 ){ |
| 867 | @ Ambiguous prefix: "%h(zCmd)" |
| 868 | }else{ |
| 869 | if( pCmd->zHelp[0]==0 ){ |
| 870 | @ No help available for "%h(pCmd->zName)" |
| @@ -1518,10 +1544,14 @@ | |
| 1518 | rc = dispatch_name_search(g.argv[2], mask|CMDFLAG_PREFIX, &pCmd); |
| 1519 | if( rc ){ |
| 1520 | int i, n; |
| 1521 | const char *az[5]; |
| 1522 | if( rc==1 ){ |
| 1523 | fossil_print("unknown %s: %s\n", zCmdOrPage, g.argv[2]); |
| 1524 | }else{ |
| 1525 | fossil_print("ambiguous %s prefix: %s\n", |
| 1526 | zCmdOrPage, g.argv[2]); |
| 1527 | } |
| 1528 |
| --- src/dispatch.c | |
| +++ src/dispatch.c | |
| @@ -811,10 +811,32 @@ | |
| 811 | fossil_print(" %s\n", az[j]); |
| 812 | } |
| 813 | } |
| 814 | } |
| 815 | |
| 816 | |
| 817 | /* |
| 818 | ** Returns 1 if the command or page name zName is known to be a |
| 819 | ** command/page which is only available in certain builds/platforms, |
| 820 | ** else returns 0. |
| 821 | */ |
| 822 | static int help_is_platform_command(const char *zName){ |
| 823 | const char *aList[] = { |
| 824 | /* List of commands/pages which are known to only be available in |
| 825 | ** certain builds/platforms. */ |
| 826 | "winsrv", |
| 827 | "json", "/json", |
| 828 | NULL /* end-of-list sentinel */ |
| 829 | }; |
| 830 | int i = 0; |
| 831 | const char *z; |
| 832 | for( z = aList[0]; z ; z = aList[++i] ){ |
| 833 | if( 0==fossil_strcmp(zName, z) ) return 1; |
| 834 | } |
| 835 | return 0; |
| 836 | } |
| 837 | |
| 838 | /* |
| 839 | ** WEBPAGE: help |
| 840 | ** URL: /help?name=CMD |
| 841 | ** |
| 842 | ** Show the built-in help text for CMD. CMD can be a command-line interface |
| @@ -860,11 +882,15 @@ | |
| 882 | @ <h1>The "%h(pCmd->zName)" setting:</h1> |
| 883 | }else{ |
| 884 | @ <h1>The "%h(pCmd->zName)" command:</h1> |
| 885 | } |
| 886 | if( rc==1 || (rc==2 && zCmd[0]=='/') ){ |
| 887 | if( zCmd && help_is_platform_command(zCmd) ){ |
| 888 | @ Not available in this build: "%h(zCmd)" |
| 889 | }else{ |
| 890 | @ Unknown topic: "%h(zCmd)" |
| 891 | } |
| 892 | }else if( rc==2 ){ |
| 893 | @ Ambiguous prefix: "%h(zCmd)" |
| 894 | }else{ |
| 895 | if( pCmd->zHelp[0]==0 ){ |
| 896 | @ No help available for "%h(pCmd->zName)" |
| @@ -1518,10 +1544,14 @@ | |
| 1544 | rc = dispatch_name_search(g.argv[2], mask|CMDFLAG_PREFIX, &pCmd); |
| 1545 | if( rc ){ |
| 1546 | int i, n; |
| 1547 | const char *az[5]; |
| 1548 | if( rc==1 ){ |
| 1549 | if( help_is_platform_command(g.argv[2]) ){ |
| 1550 | fossil_print("Not available in this build: %s\n", g.argv[2]); |
| 1551 | return; |
| 1552 | } |
| 1553 | fossil_print("unknown %s: %s\n", zCmdOrPage, g.argv[2]); |
| 1554 | }else{ |
| 1555 | fossil_print("ambiguous %s prefix: %s\n", |
| 1556 | zCmdOrPage, g.argv[2]); |
| 1557 | } |
| 1558 |
+13
-8
| --- src/export.c | ||
| +++ src/export.c | ||
| @@ -1074,12 +1074,11 @@ | ||
| 1074 | 1074 | */ |
| 1075 | 1075 | static int gitmirror_send_checkin( |
| 1076 | 1076 | FILE *xCmd, /* Write fast-import text on this pipe */ |
| 1077 | 1077 | int rid, /* BLOB.RID for the check-in to export */ |
| 1078 | 1078 | const char *zUuid, /* BLOB.UUID for the check-in to export */ |
| 1079 | - int *pnLimit, /* Stop when the counter reaches zero */ | |
| 1080 | - int fManifest /* MFESTFLG_* values */ | |
| 1079 | + int *pnLimit /* Stop when the counter reaches zero */ | |
| 1081 | 1080 | ){ |
| 1082 | 1081 | Manifest *pMan; /* The check-in to be output */ |
| 1083 | 1082 | int i; /* Loop counter */ |
| 1084 | 1083 | int iParent; /* Which immediate ancestor is primary. -1 for none */ |
| 1085 | 1084 | Stmt q; /* An SQL query */ |
| @@ -1089,10 +1088,12 @@ | ||
| 1089 | 1088 | Blob comment; /* The comment text for the check-in */ |
| 1090 | 1089 | int nErr = 0; /* Number of errors */ |
| 1091 | 1090 | int bPhantomOk; /* True if phantom files should be ignored */ |
| 1092 | 1091 | char buf[24]; |
| 1093 | 1092 | char *zEmail; /* Contact info for Git committer field */ |
| 1093 | + int fManifest; /* Should the manifest files be included? */ | |
| 1094 | + int fPManifest = 0; /* OR of the manifest files for all parents */ | |
| 1094 | 1095 | |
| 1095 | 1096 | pMan = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 1096 | 1097 | if( pMan==0 ){ |
| 1097 | 1098 | /* Must be a phantom. Return without doing anything, and in particular |
| 1098 | 1099 | ** without creating a mark for this check-in. */ |
| @@ -1106,11 +1107,11 @@ | ||
| 1106 | 1107 | char *zPMark = gitmirror_find_mark(pMan->azParent[i], 0, 0); |
| 1107 | 1108 | if( zPMark==0 ){ |
| 1108 | 1109 | int prid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", |
| 1109 | 1110 | pMan->azParent[i]); |
| 1110 | 1111 | int rc = gitmirror_send_checkin(xCmd, prid, pMan->azParent[i], |
| 1111 | - pnLimit, fManifest); | |
| 1112 | + pnLimit); | |
| 1112 | 1113 | if( rc || *pnLimit<=0 ){ |
| 1113 | 1114 | manifest_destroy(pMan); |
| 1114 | 1115 | return 1; |
| 1115 | 1116 | } |
| 1116 | 1117 | } |
| @@ -1215,10 +1216,11 @@ | ||
| 1215 | 1216 | blob_reset(&comment); |
| 1216 | 1217 | iParent = -1; /* Which ancestor is the primary parent */ |
| 1217 | 1218 | for(i=0; i<pMan->nParent; i++){ |
| 1218 | 1219 | char *zOther = gitmirror_find_mark(pMan->azParent[i],0,0); |
| 1219 | 1220 | if( zOther==0 ) continue; |
| 1221 | + fPManifest |= db_get_manifest_setting(pMan->azParent[i]); | |
| 1220 | 1222 | if( iParent<0 ){ |
| 1221 | 1223 | iParent = i; |
| 1222 | 1224 | fprintf(xCmd, "from %s\n", zOther); |
| 1223 | 1225 | }else{ |
| 1224 | 1226 | fprintf(xCmd, "merge %s\n", zOther); |
| @@ -1271,29 +1273,36 @@ | ||
| 1271 | 1273 | db_finalize(&q); |
| 1272 | 1274 | manifest_destroy(pMan); |
| 1273 | 1275 | pMan = 0; |
| 1274 | 1276 | |
| 1275 | 1277 | /* Include Fossil-generated auxiliary files in the check-in */ |
| 1278 | + fManifest = db_get_manifest_setting(zUuid); | |
| 1276 | 1279 | if( fManifest & MFESTFLG_RAW ){ |
| 1277 | 1280 | Blob manifest; |
| 1278 | 1281 | content_get(rid, &manifest); |
| 1279 | 1282 | sterilize_manifest(&manifest, CFTYPE_MANIFEST); |
| 1280 | 1283 | fprintf(xCmd,"M 100644 inline manifest\ndata %d\n%s\n", |
| 1281 | 1284 | blob_strlen(&manifest), blob_str(&manifest)); |
| 1282 | 1285 | blob_reset(&manifest); |
| 1286 | + }else if( fPManifest & MFESTFLG_RAW ){ | |
| 1287 | + fprintf(xCmd, "D manifest\n"); | |
| 1283 | 1288 | } |
| 1284 | 1289 | if( fManifest & MFESTFLG_UUID ){ |
| 1285 | 1290 | int n = (int)strlen(zUuid); |
| 1286 | 1291 | fprintf(xCmd,"M 100644 inline manifest.uuid\ndata %d\n%s\n\n", n+1, zUuid); |
| 1292 | + }else if( fPManifest & MFESTFLG_UUID ){ | |
| 1293 | + fprintf(xCmd, "D manifest.uuid\n"); | |
| 1287 | 1294 | } |
| 1288 | 1295 | if( fManifest & MFESTFLG_TAGS ){ |
| 1289 | 1296 | Blob tagslist; |
| 1290 | 1297 | blob_init(&tagslist, 0, 0); |
| 1291 | 1298 | get_checkin_taglist(rid, &tagslist); |
| 1292 | 1299 | fprintf(xCmd,"M 100644 inline manifest.tags\ndata %d\n%s\n", |
| 1293 | 1300 | blob_strlen(&tagslist), blob_str(&tagslist)); |
| 1294 | 1301 | blob_reset(&tagslist); |
| 1302 | + }else if( fPManifest & MFESTFLG_TAGS ){ | |
| 1303 | + fprintf(xCmd, "D manifest.tags\n"); | |
| 1295 | 1304 | } |
| 1296 | 1305 | |
| 1297 | 1306 | /* The check-in is finished, so decrement the counter */ |
| 1298 | 1307 | (*pnLimit)--; |
| 1299 | 1308 | return 0; |
| @@ -1383,11 +1392,10 @@ | ||
| 1383 | 1392 | char *zPushUrl; /* URL to sync the mirror to */ |
| 1384 | 1393 | double rEnd; /* time of most recent export */ |
| 1385 | 1394 | int rc; /* Result code */ |
| 1386 | 1395 | int bForce; /* Do the export and sync even if no changes*/ |
| 1387 | 1396 | int bNeedRepack = 0; /* True if we should run repack at the end */ |
| 1388 | - int fManifest; /* Current "manifest" setting */ | |
| 1389 | 1397 | int bIfExists; /* The --if-mirrored flag */ |
| 1390 | 1398 | FILE *xCmd; /* Pipe to the "git fast-import" command */ |
| 1391 | 1399 | FILE *pMarks; /* Git mark files */ |
| 1392 | 1400 | Stmt q; /* Queries */ |
| 1393 | 1401 | char zLine[200]; /* One line of a mark file */ |
| @@ -1521,13 +1529,10 @@ | ||
| 1521 | 1529 | gitmirror_message(VERB_NORMAL, "no changes\n"); |
| 1522 | 1530 | db_commit_transaction(); |
| 1523 | 1531 | return; |
| 1524 | 1532 | } |
| 1525 | 1533 | |
| 1526 | - /* Do we need to include manifest files in the clone? */ | |
| 1527 | - fManifest = db_get_manifest_setting(); | |
| 1528 | - | |
| 1529 | 1534 | /* Change to the MIRROR directory so that the Git commands will work */ |
| 1530 | 1535 | rc = file_chdir(zMirror, 0); |
| 1531 | 1536 | if( rc ) fossil_fatal("cannot change the working directory to \"%s\"", |
| 1532 | 1537 | zMirror); |
| 1533 | 1538 | |
| @@ -1579,11 +1584,11 @@ | ||
| 1579 | 1584 | while( nLimit && db_step(&q)==SQLITE_ROW ){ |
| 1580 | 1585 | int rid = db_column_int(&q, 0); |
| 1581 | 1586 | double rMTime = db_column_double(&q, 1); |
| 1582 | 1587 | const char *zUuid = db_column_text(&q, 2); |
| 1583 | 1588 | if( rMTime>rEnd ) rEnd = rMTime; |
| 1584 | - rc = gitmirror_send_checkin(xCmd, rid, zUuid, &nLimit, fManifest); | |
| 1589 | + rc = gitmirror_send_checkin(xCmd, rid, zUuid, &nLimit); | |
| 1585 | 1590 | if( rc ) break; |
| 1586 | 1591 | gitmirror_message(VERB_NORMAL,"%d/%d \r", nTotal-nLimit, nTotal); |
| 1587 | 1592 | fflush(stdout); |
| 1588 | 1593 | } |
| 1589 | 1594 | db_finalize(&q); |
| 1590 | 1595 |
| --- src/export.c | |
| +++ src/export.c | |
| @@ -1074,12 +1074,11 @@ | |
| 1074 | */ |
| 1075 | static int gitmirror_send_checkin( |
| 1076 | FILE *xCmd, /* Write fast-import text on this pipe */ |
| 1077 | int rid, /* BLOB.RID for the check-in to export */ |
| 1078 | const char *zUuid, /* BLOB.UUID for the check-in to export */ |
| 1079 | int *pnLimit, /* Stop when the counter reaches zero */ |
| 1080 | int fManifest /* MFESTFLG_* values */ |
| 1081 | ){ |
| 1082 | Manifest *pMan; /* The check-in to be output */ |
| 1083 | int i; /* Loop counter */ |
| 1084 | int iParent; /* Which immediate ancestor is primary. -1 for none */ |
| 1085 | Stmt q; /* An SQL query */ |
| @@ -1089,10 +1088,12 @@ | |
| 1089 | Blob comment; /* The comment text for the check-in */ |
| 1090 | int nErr = 0; /* Number of errors */ |
| 1091 | int bPhantomOk; /* True if phantom files should be ignored */ |
| 1092 | char buf[24]; |
| 1093 | char *zEmail; /* Contact info for Git committer field */ |
| 1094 | |
| 1095 | pMan = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 1096 | if( pMan==0 ){ |
| 1097 | /* Must be a phantom. Return without doing anything, and in particular |
| 1098 | ** without creating a mark for this check-in. */ |
| @@ -1106,11 +1107,11 @@ | |
| 1106 | char *zPMark = gitmirror_find_mark(pMan->azParent[i], 0, 0); |
| 1107 | if( zPMark==0 ){ |
| 1108 | int prid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", |
| 1109 | pMan->azParent[i]); |
| 1110 | int rc = gitmirror_send_checkin(xCmd, prid, pMan->azParent[i], |
| 1111 | pnLimit, fManifest); |
| 1112 | if( rc || *pnLimit<=0 ){ |
| 1113 | manifest_destroy(pMan); |
| 1114 | return 1; |
| 1115 | } |
| 1116 | } |
| @@ -1215,10 +1216,11 @@ | |
| 1215 | blob_reset(&comment); |
| 1216 | iParent = -1; /* Which ancestor is the primary parent */ |
| 1217 | for(i=0; i<pMan->nParent; i++){ |
| 1218 | char *zOther = gitmirror_find_mark(pMan->azParent[i],0,0); |
| 1219 | if( zOther==0 ) continue; |
| 1220 | if( iParent<0 ){ |
| 1221 | iParent = i; |
| 1222 | fprintf(xCmd, "from %s\n", zOther); |
| 1223 | }else{ |
| 1224 | fprintf(xCmd, "merge %s\n", zOther); |
| @@ -1271,29 +1273,36 @@ | |
| 1271 | db_finalize(&q); |
| 1272 | manifest_destroy(pMan); |
| 1273 | pMan = 0; |
| 1274 | |
| 1275 | /* Include Fossil-generated auxiliary files in the check-in */ |
| 1276 | if( fManifest & MFESTFLG_RAW ){ |
| 1277 | Blob manifest; |
| 1278 | content_get(rid, &manifest); |
| 1279 | sterilize_manifest(&manifest, CFTYPE_MANIFEST); |
| 1280 | fprintf(xCmd,"M 100644 inline manifest\ndata %d\n%s\n", |
| 1281 | blob_strlen(&manifest), blob_str(&manifest)); |
| 1282 | blob_reset(&manifest); |
| 1283 | } |
| 1284 | if( fManifest & MFESTFLG_UUID ){ |
| 1285 | int n = (int)strlen(zUuid); |
| 1286 | fprintf(xCmd,"M 100644 inline manifest.uuid\ndata %d\n%s\n\n", n+1, zUuid); |
| 1287 | } |
| 1288 | if( fManifest & MFESTFLG_TAGS ){ |
| 1289 | Blob tagslist; |
| 1290 | blob_init(&tagslist, 0, 0); |
| 1291 | get_checkin_taglist(rid, &tagslist); |
| 1292 | fprintf(xCmd,"M 100644 inline manifest.tags\ndata %d\n%s\n", |
| 1293 | blob_strlen(&tagslist), blob_str(&tagslist)); |
| 1294 | blob_reset(&tagslist); |
| 1295 | } |
| 1296 | |
| 1297 | /* The check-in is finished, so decrement the counter */ |
| 1298 | (*pnLimit)--; |
| 1299 | return 0; |
| @@ -1383,11 +1392,10 @@ | |
| 1383 | char *zPushUrl; /* URL to sync the mirror to */ |
| 1384 | double rEnd; /* time of most recent export */ |
| 1385 | int rc; /* Result code */ |
| 1386 | int bForce; /* Do the export and sync even if no changes*/ |
| 1387 | int bNeedRepack = 0; /* True if we should run repack at the end */ |
| 1388 | int fManifest; /* Current "manifest" setting */ |
| 1389 | int bIfExists; /* The --if-mirrored flag */ |
| 1390 | FILE *xCmd; /* Pipe to the "git fast-import" command */ |
| 1391 | FILE *pMarks; /* Git mark files */ |
| 1392 | Stmt q; /* Queries */ |
| 1393 | char zLine[200]; /* One line of a mark file */ |
| @@ -1521,13 +1529,10 @@ | |
| 1521 | gitmirror_message(VERB_NORMAL, "no changes\n"); |
| 1522 | db_commit_transaction(); |
| 1523 | return; |
| 1524 | } |
| 1525 | |
| 1526 | /* Do we need to include manifest files in the clone? */ |
| 1527 | fManifest = db_get_manifest_setting(); |
| 1528 | |
| 1529 | /* Change to the MIRROR directory so that the Git commands will work */ |
| 1530 | rc = file_chdir(zMirror, 0); |
| 1531 | if( rc ) fossil_fatal("cannot change the working directory to \"%s\"", |
| 1532 | zMirror); |
| 1533 | |
| @@ -1579,11 +1584,11 @@ | |
| 1579 | while( nLimit && db_step(&q)==SQLITE_ROW ){ |
| 1580 | int rid = db_column_int(&q, 0); |
| 1581 | double rMTime = db_column_double(&q, 1); |
| 1582 | const char *zUuid = db_column_text(&q, 2); |
| 1583 | if( rMTime>rEnd ) rEnd = rMTime; |
| 1584 | rc = gitmirror_send_checkin(xCmd, rid, zUuid, &nLimit, fManifest); |
| 1585 | if( rc ) break; |
| 1586 | gitmirror_message(VERB_NORMAL,"%d/%d \r", nTotal-nLimit, nTotal); |
| 1587 | fflush(stdout); |
| 1588 | } |
| 1589 | db_finalize(&q); |
| 1590 |
| --- src/export.c | |
| +++ src/export.c | |
| @@ -1074,12 +1074,11 @@ | |
| 1074 | */ |
| 1075 | static int gitmirror_send_checkin( |
| 1076 | FILE *xCmd, /* Write fast-import text on this pipe */ |
| 1077 | int rid, /* BLOB.RID for the check-in to export */ |
| 1078 | const char *zUuid, /* BLOB.UUID for the check-in to export */ |
| 1079 | int *pnLimit /* Stop when the counter reaches zero */ |
| 1080 | ){ |
| 1081 | Manifest *pMan; /* The check-in to be output */ |
| 1082 | int i; /* Loop counter */ |
| 1083 | int iParent; /* Which immediate ancestor is primary. -1 for none */ |
| 1084 | Stmt q; /* An SQL query */ |
| @@ -1089,10 +1088,12 @@ | |
| 1088 | Blob comment; /* The comment text for the check-in */ |
| 1089 | int nErr = 0; /* Number of errors */ |
| 1090 | int bPhantomOk; /* True if phantom files should be ignored */ |
| 1091 | char buf[24]; |
| 1092 | char *zEmail; /* Contact info for Git committer field */ |
| 1093 | int fManifest; /* Should the manifest files be included? */ |
| 1094 | int fPManifest = 0; /* OR of the manifest files for all parents */ |
| 1095 | |
| 1096 | pMan = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 1097 | if( pMan==0 ){ |
| 1098 | /* Must be a phantom. Return without doing anything, and in particular |
| 1099 | ** without creating a mark for this check-in. */ |
| @@ -1106,11 +1107,11 @@ | |
| 1107 | char *zPMark = gitmirror_find_mark(pMan->azParent[i], 0, 0); |
| 1108 | if( zPMark==0 ){ |
| 1109 | int prid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", |
| 1110 | pMan->azParent[i]); |
| 1111 | int rc = gitmirror_send_checkin(xCmd, prid, pMan->azParent[i], |
| 1112 | pnLimit); |
| 1113 | if( rc || *pnLimit<=0 ){ |
| 1114 | manifest_destroy(pMan); |
| 1115 | return 1; |
| 1116 | } |
| 1117 | } |
| @@ -1215,10 +1216,11 @@ | |
| 1216 | blob_reset(&comment); |
| 1217 | iParent = -1; /* Which ancestor is the primary parent */ |
| 1218 | for(i=0; i<pMan->nParent; i++){ |
| 1219 | char *zOther = gitmirror_find_mark(pMan->azParent[i],0,0); |
| 1220 | if( zOther==0 ) continue; |
| 1221 | fPManifest |= db_get_manifest_setting(pMan->azParent[i]); |
| 1222 | if( iParent<0 ){ |
| 1223 | iParent = i; |
| 1224 | fprintf(xCmd, "from %s\n", zOther); |
| 1225 | }else{ |
| 1226 | fprintf(xCmd, "merge %s\n", zOther); |
| @@ -1271,29 +1273,36 @@ | |
| 1273 | db_finalize(&q); |
| 1274 | manifest_destroy(pMan); |
| 1275 | pMan = 0; |
| 1276 | |
| 1277 | /* Include Fossil-generated auxiliary files in the check-in */ |
| 1278 | fManifest = db_get_manifest_setting(zUuid); |
| 1279 | if( fManifest & MFESTFLG_RAW ){ |
| 1280 | Blob manifest; |
| 1281 | content_get(rid, &manifest); |
| 1282 | sterilize_manifest(&manifest, CFTYPE_MANIFEST); |
| 1283 | fprintf(xCmd,"M 100644 inline manifest\ndata %d\n%s\n", |
| 1284 | blob_strlen(&manifest), blob_str(&manifest)); |
| 1285 | blob_reset(&manifest); |
| 1286 | }else if( fPManifest & MFESTFLG_RAW ){ |
| 1287 | fprintf(xCmd, "D manifest\n"); |
| 1288 | } |
| 1289 | if( fManifest & MFESTFLG_UUID ){ |
| 1290 | int n = (int)strlen(zUuid); |
| 1291 | fprintf(xCmd,"M 100644 inline manifest.uuid\ndata %d\n%s\n\n", n+1, zUuid); |
| 1292 | }else if( fPManifest & MFESTFLG_UUID ){ |
| 1293 | fprintf(xCmd, "D manifest.uuid\n"); |
| 1294 | } |
| 1295 | if( fManifest & MFESTFLG_TAGS ){ |
| 1296 | Blob tagslist; |
| 1297 | blob_init(&tagslist, 0, 0); |
| 1298 | get_checkin_taglist(rid, &tagslist); |
| 1299 | fprintf(xCmd,"M 100644 inline manifest.tags\ndata %d\n%s\n", |
| 1300 | blob_strlen(&tagslist), blob_str(&tagslist)); |
| 1301 | blob_reset(&tagslist); |
| 1302 | }else if( fPManifest & MFESTFLG_TAGS ){ |
| 1303 | fprintf(xCmd, "D manifest.tags\n"); |
| 1304 | } |
| 1305 | |
| 1306 | /* The check-in is finished, so decrement the counter */ |
| 1307 | (*pnLimit)--; |
| 1308 | return 0; |
| @@ -1383,11 +1392,10 @@ | |
| 1392 | char *zPushUrl; /* URL to sync the mirror to */ |
| 1393 | double rEnd; /* time of most recent export */ |
| 1394 | int rc; /* Result code */ |
| 1395 | int bForce; /* Do the export and sync even if no changes*/ |
| 1396 | int bNeedRepack = 0; /* True if we should run repack at the end */ |
| 1397 | int bIfExists; /* The --if-mirrored flag */ |
| 1398 | FILE *xCmd; /* Pipe to the "git fast-import" command */ |
| 1399 | FILE *pMarks; /* Git mark files */ |
| 1400 | Stmt q; /* Queries */ |
| 1401 | char zLine[200]; /* One line of a mark file */ |
| @@ -1521,13 +1529,10 @@ | |
| 1529 | gitmirror_message(VERB_NORMAL, "no changes\n"); |
| 1530 | db_commit_transaction(); |
| 1531 | return; |
| 1532 | } |
| 1533 | |
| 1534 | /* Change to the MIRROR directory so that the Git commands will work */ |
| 1535 | rc = file_chdir(zMirror, 0); |
| 1536 | if( rc ) fossil_fatal("cannot change the working directory to \"%s\"", |
| 1537 | zMirror); |
| 1538 | |
| @@ -1579,11 +1584,11 @@ | |
| 1584 | while( nLimit && db_step(&q)==SQLITE_ROW ){ |
| 1585 | int rid = db_column_int(&q, 0); |
| 1586 | double rMTime = db_column_double(&q, 1); |
| 1587 | const char *zUuid = db_column_text(&q, 2); |
| 1588 | if( rMTime>rEnd ) rEnd = rMTime; |
| 1589 | rc = gitmirror_send_checkin(xCmd, rid, zUuid, &nLimit); |
| 1590 | if( rc ) break; |
| 1591 | gitmirror_message(VERB_NORMAL,"%d/%d \r", nTotal-nLimit, nTotal); |
| 1592 | fflush(stdout); |
| 1593 | } |
| 1594 | db_finalize(&q); |
| 1595 |
+7
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -1572,10 +1572,17 @@ | ||
| 1572 | 1572 | ** |
| 1573 | 1573 | ** Usage: %fossil test-which ARGS... |
| 1574 | 1574 | ** |
| 1575 | 1575 | ** For each argument, search the PATH for the executable with the name |
| 1576 | 1576 | ** and print its full pathname. |
| 1577 | +** | |
| 1578 | +** See also the "which" command (without the "test-" prefix). The plain | |
| 1579 | +** "which" command is more convenient to use since it provides the -a/-all | |
| 1580 | +** option, and because it is shorter. The "fossil which" command without | |
| 1581 | +** the "test-" prefix is recommended for day-to-day use. This command is | |
| 1582 | +** retained because it tests the internal file_fullexename() function | |
| 1583 | +** whereas plain "which" does not. | |
| 1577 | 1584 | */ |
| 1578 | 1585 | void test_which_cmd(void){ |
| 1579 | 1586 | int i; |
| 1580 | 1587 | for(i=2; i<g.argc; i++){ |
| 1581 | 1588 | char *z = file_fullexename(g.argv[i]); |
| 1582 | 1589 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -1572,10 +1572,17 @@ | |
| 1572 | ** |
| 1573 | ** Usage: %fossil test-which ARGS... |
| 1574 | ** |
| 1575 | ** For each argument, search the PATH for the executable with the name |
| 1576 | ** and print its full pathname. |
| 1577 | */ |
| 1578 | void test_which_cmd(void){ |
| 1579 | int i; |
| 1580 | for(i=2; i<g.argc; i++){ |
| 1581 | char *z = file_fullexename(g.argv[i]); |
| 1582 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -1572,10 +1572,17 @@ | |
| 1572 | ** |
| 1573 | ** Usage: %fossil test-which ARGS... |
| 1574 | ** |
| 1575 | ** For each argument, search the PATH for the executable with the name |
| 1576 | ** and print its full pathname. |
| 1577 | ** |
| 1578 | ** See also the "which" command (without the "test-" prefix). The plain |
| 1579 | ** "which" command is more convenient to use since it provides the -a/-all |
| 1580 | ** option, and because it is shorter. The "fossil which" command without |
| 1581 | ** the "test-" prefix is recommended for day-to-day use. This command is |
| 1582 | ** retained because it tests the internal file_fullexename() function |
| 1583 | ** whereas plain "which" does not. |
| 1584 | */ |
| 1585 | void test_which_cmd(void){ |
| 1586 | int i; |
| 1587 | for(i=2; i<g.argc; i++){ |
| 1588 | char *z = file_fullexename(g.argv[i]); |
| 1589 |
+2
-2
| --- src/finfo.c | ||
| +++ src/finfo.c | ||
| @@ -498,16 +498,16 @@ | ||
| 498 | 498 | " AND event.objid=mlink.mid\n", |
| 499 | 499 | TAG_BRANCH |
| 500 | 500 | ); |
| 501 | 501 | if( (zA = P("a"))!=0 ){ |
| 502 | 502 | blob_append_sql(&sql, " AND event.mtime>=%.16g\n", |
| 503 | - symbolic_name_to_mtime(zA,0)); | |
| 503 | + symbolic_name_to_mtime(zA,0,0)); | |
| 504 | 504 | url_add_parameter(&url, "a", zA); |
| 505 | 505 | } |
| 506 | 506 | if( (zB = P("b"))!=0 ){ |
| 507 | 507 | blob_append_sql(&sql, " AND event.mtime<=%.16g\n", |
| 508 | - symbolic_name_to_mtime(zB,0)); | |
| 508 | + symbolic_name_to_mtime(zB,0,1)); | |
| 509 | 509 | url_add_parameter(&url, "b", zB); |
| 510 | 510 | } |
| 511 | 511 | if( ridFrom ){ |
| 512 | 512 | blob_append_sql(&sql, |
| 513 | 513 | " AND mlink.mid IN (SELECT rid FROM ancestor)\n" |
| 514 | 514 |
| --- src/finfo.c | |
| +++ src/finfo.c | |
| @@ -498,16 +498,16 @@ | |
| 498 | " AND event.objid=mlink.mid\n", |
| 499 | TAG_BRANCH |
| 500 | ); |
| 501 | if( (zA = P("a"))!=0 ){ |
| 502 | blob_append_sql(&sql, " AND event.mtime>=%.16g\n", |
| 503 | symbolic_name_to_mtime(zA,0)); |
| 504 | url_add_parameter(&url, "a", zA); |
| 505 | } |
| 506 | if( (zB = P("b"))!=0 ){ |
| 507 | blob_append_sql(&sql, " AND event.mtime<=%.16g\n", |
| 508 | symbolic_name_to_mtime(zB,0)); |
| 509 | url_add_parameter(&url, "b", zB); |
| 510 | } |
| 511 | if( ridFrom ){ |
| 512 | blob_append_sql(&sql, |
| 513 | " AND mlink.mid IN (SELECT rid FROM ancestor)\n" |
| 514 |
| --- src/finfo.c | |
| +++ src/finfo.c | |
| @@ -498,16 +498,16 @@ | |
| 498 | " AND event.objid=mlink.mid\n", |
| 499 | TAG_BRANCH |
| 500 | ); |
| 501 | if( (zA = P("a"))!=0 ){ |
| 502 | blob_append_sql(&sql, " AND event.mtime>=%.16g\n", |
| 503 | symbolic_name_to_mtime(zA,0,0)); |
| 504 | url_add_parameter(&url, "a", zA); |
| 505 | } |
| 506 | if( (zB = P("b"))!=0 ){ |
| 507 | blob_append_sql(&sql, " AND event.mtime<=%.16g\n", |
| 508 | symbolic_name_to_mtime(zB,0,1)); |
| 509 | url_add_parameter(&url, "b", zB); |
| 510 | } |
| 511 | if( ridFrom ){ |
| 512 | blob_append_sql(&sql, |
| 513 | " AND mlink.mid IN (SELECT rid FROM ancestor)\n" |
| 514 |
+1
-1
| --- src/graph.c | ||
| +++ src/graph.c | ||
| @@ -84,11 +84,11 @@ | ||
| 84 | 84 | |
| 85 | 85 | GraphRow *pNext; /* Next row down in the list of all rows */ |
| 86 | 86 | GraphRow *pPrev; /* Previous row */ |
| 87 | 87 | |
| 88 | 88 | int idx; /* Row index. Top row is smallest. */ |
| 89 | - int idxTop; /* Direct descendent highest up on the graph */ | |
| 89 | + int idxTop; /* Direct descendant highest up on the graph */ | |
| 90 | 90 | GraphRow *pChild; /* Child immediately above this node */ |
| 91 | 91 | u8 isDup; /* True if this is duplicate of a prior entry */ |
| 92 | 92 | u8 isLeaf; /* True if this is a leaf node */ |
| 93 | 93 | u8 isStepParent; /* pChild is actually a step-child. The thick |
| 94 | 94 | ** arrow up to the child is dashed, not solid */ |
| 95 | 95 |
| --- src/graph.c | |
| +++ src/graph.c | |
| @@ -84,11 +84,11 @@ | |
| 84 | |
| 85 | GraphRow *pNext; /* Next row down in the list of all rows */ |
| 86 | GraphRow *pPrev; /* Previous row */ |
| 87 | |
| 88 | int idx; /* Row index. Top row is smallest. */ |
| 89 | int idxTop; /* Direct descendent highest up on the graph */ |
| 90 | GraphRow *pChild; /* Child immediately above this node */ |
| 91 | u8 isDup; /* True if this is duplicate of a prior entry */ |
| 92 | u8 isLeaf; /* True if this is a leaf node */ |
| 93 | u8 isStepParent; /* pChild is actually a step-child. The thick |
| 94 | ** arrow up to the child is dashed, not solid */ |
| 95 |
| --- src/graph.c | |
| +++ src/graph.c | |
| @@ -84,11 +84,11 @@ | |
| 84 | |
| 85 | GraphRow *pNext; /* Next row down in the list of all rows */ |
| 86 | GraphRow *pPrev; /* Previous row */ |
| 87 | |
| 88 | int idx; /* Row index. Top row is smallest. */ |
| 89 | int idxTop; /* Direct descendant highest up on the graph */ |
| 90 | GraphRow *pChild; /* Child immediately above this node */ |
| 91 | u8 isDup; /* True if this is duplicate of a prior entry */ |
| 92 | u8 isLeaf; /* True if this is a leaf node */ |
| 93 | u8 isStepParent; /* pChild is actually a step-child. The thick |
| 94 | ** arrow up to the child is dashed, not solid */ |
| 95 |
+2
-2
| --- src/http_ssl.c | ||
| +++ src/http_ssl.c | ||
| @@ -910,12 +910,12 @@ | ||
| 910 | 910 | if( file_isdir(zPath, ExtFILE)>0 ) *pzStore = zPath; |
| 911 | 911 | } |
| 912 | 912 | #endif /* FOSSIL_ENABLE_SSL */ |
| 913 | 913 | |
| 914 | 914 | /* |
| 915 | -** COMMAND: tls-config* abbreviated-subcommands | |
| 916 | -** COMMAND: ssl-config abbreviated-subcommands | |
| 915 | +** COMMAND: tls-config* abbrv-subcom | |
| 916 | +** COMMAND: ssl-config abbrv-subcom | |
| 917 | 917 | ** |
| 918 | 918 | ** Usage: %fossil ssl-config [SUBCOMMAND] [OPTIONS...] [ARGS...] |
| 919 | 919 | ** |
| 920 | 920 | ** This command is used to view or modify the TLS (Transport Layer |
| 921 | 921 | ** Security) configuration for Fossil. TLS (formerly SSL) is the |
| 922 | 922 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -910,12 +910,12 @@ | |
| 910 | if( file_isdir(zPath, ExtFILE)>0 ) *pzStore = zPath; |
| 911 | } |
| 912 | #endif /* FOSSIL_ENABLE_SSL */ |
| 913 | |
| 914 | /* |
| 915 | ** COMMAND: tls-config* abbreviated-subcommands |
| 916 | ** COMMAND: ssl-config abbreviated-subcommands |
| 917 | ** |
| 918 | ** Usage: %fossil ssl-config [SUBCOMMAND] [OPTIONS...] [ARGS...] |
| 919 | ** |
| 920 | ** This command is used to view or modify the TLS (Transport Layer |
| 921 | ** Security) configuration for Fossil. TLS (formerly SSL) is the |
| 922 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -910,12 +910,12 @@ | |
| 910 | if( file_isdir(zPath, ExtFILE)>0 ) *pzStore = zPath; |
| 911 | } |
| 912 | #endif /* FOSSIL_ENABLE_SSL */ |
| 913 | |
| 914 | /* |
| 915 | ** COMMAND: tls-config* abbrv-subcom |
| 916 | ** COMMAND: ssl-config abbrv-subcom |
| 917 | ** |
| 918 | ** Usage: %fossil ssl-config [SUBCOMMAND] [OPTIONS...] [ARGS...] |
| 919 | ** |
| 920 | ** This command is used to view or modify the TLS (Transport Layer |
| 921 | ** Security) configuration for Fossil. TLS (formerly SSL) is the |
| 922 |
+89
-91
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -257,17 +257,19 @@ | ||
| 257 | 257 | } |
| 258 | 258 | fossil_print("fossil: %z\n", file_fullexename(g.nameOfExe)); |
| 259 | 259 | fossil_print("version: %s", z); |
| 260 | 260 | blob_reset(&vx); |
| 261 | 261 | } |
| 262 | - }else{ | |
| 262 | + }else if( g.repositoryOpen ){ | |
| 263 | 263 | int rid; |
| 264 | 264 | rid = name_to_rid(g.argv[2]); |
| 265 | 265 | if( rid==0 ){ |
| 266 | 266 | fossil_fatal("no such object: %s", g.argv[2]); |
| 267 | 267 | } |
| 268 | 268 | show_common_info(rid, "hash:", 1, 1); |
| 269 | + }else{ | |
| 270 | + fossil_fatal("Could not find or open a Fossil repository"); | |
| 269 | 271 | } |
| 270 | 272 | } |
| 271 | 273 | |
| 272 | 274 | /* |
| 273 | 275 | ** Show the context graph (immediate parents and children) for |
| @@ -925,12 +927,12 @@ | ||
| 925 | 927 | " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim", |
| 926 | 928 | rid |
| 927 | 929 | ); |
| 928 | 930 | isLeaf = !db_exists("SELECT 1 FROM plink WHERE pid=%d", rid); |
| 929 | 931 | db_prepare(&q1, |
| 930 | - "SELECT uuid, datetime(mtime,toLocal()), user, comment," | |
| 931 | - " datetime(omtime,toLocal()), mtime" | |
| 932 | + "SELECT uuid, datetime(mtime,toLocal(),'subsec'), user, comment," | |
| 933 | + " datetime(omtime,toLocal(),'subsec'), mtime" | |
| 932 | 934 | " FROM blob, event" |
| 933 | 935 | " WHERE blob.rid=%d" |
| 934 | 936 | " AND event.objid=%d", |
| 935 | 937 | rid, rid |
| 936 | 938 | ); |
| @@ -1366,72 +1368,10 @@ | ||
| 1366 | 1368 | webpage_error("Artifact %s is not a check-in.", P(zParam)); |
| 1367 | 1369 | return 0; |
| 1368 | 1370 | } |
| 1369 | 1371 | return manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 1370 | 1372 | } |
| 1371 | - | |
| 1372 | -#if 0 /* not used */ | |
| 1373 | -/* | |
| 1374 | -** Output a description of a check-in | |
| 1375 | -*/ | |
| 1376 | -static void checkin_description(int rid){ | |
| 1377 | - Stmt q; | |
| 1378 | - db_prepare(&q, | |
| 1379 | - "SELECT datetime(mtime), coalesce(euser,user)," | |
| 1380 | - " coalesce(ecomment,comment), uuid," | |
| 1381 | - " (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref" | |
| 1382 | - " WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid" | |
| 1383 | - " AND tagxref.rid=blob.rid AND tagxref.tagtype>0)" | |
| 1384 | - " FROM event, blob" | |
| 1385 | - " WHERE event.objid=%d AND type='ci'" | |
| 1386 | - " AND blob.rid=%d", | |
| 1387 | - rid, rid | |
| 1388 | - ); | |
| 1389 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 1390 | - const char *zDate = db_column_text(&q, 0); | |
| 1391 | - const char *zUser = db_column_text(&q, 1); | |
| 1392 | - const char *zUuid = db_column_text(&q, 3); | |
| 1393 | - const char *zTagList = db_column_text(&q, 4); | |
| 1394 | - Blob comment; | |
| 1395 | - int wikiFlags = WIKI_INLINE|WIKI_NOBADLINKS; | |
| 1396 | - if( db_get_boolean("timeline-block-markup", 0)==0 ){ | |
| 1397 | - wikiFlags |= WIKI_NOBLOCK; | |
| 1398 | - } | |
| 1399 | - hyperlink_to_version(zUuid); | |
| 1400 | - blob_zero(&comment); | |
| 1401 | - db_column_blob(&q, 2, &comment); | |
| 1402 | - wiki_convert(&comment, 0, wikiFlags); | |
| 1403 | - blob_reset(&comment); | |
| 1404 | - @ (user: | |
| 1405 | - hyperlink_to_user(zUser,zDate,","); | |
| 1406 | - if( zTagList && zTagList[0] && g.perm.Hyperlink ){ | |
| 1407 | - int i; | |
| 1408 | - const char *z = zTagList; | |
| 1409 | - Blob links; | |
| 1410 | - blob_zero(&links); | |
| 1411 | - while( z && z[0] ){ | |
| 1412 | - for(i=0; z[i] && (z[i]!=',' || z[i+1]!=' '); i++){} | |
| 1413 | - blob_appendf(&links, | |
| 1414 | - "%z%#h</a>%.2s", | |
| 1415 | - href("%R/timeline?r=%#t&nd&c=%t",i,z,zDate), i,z, &z[i] | |
| 1416 | - ); | |
| 1417 | - if( z[i]==0 ) break; | |
| 1418 | - z += i+2; | |
| 1419 | - } | |
| 1420 | - @ tags: %s(blob_str(&links)), | |
| 1421 | - blob_reset(&links); | |
| 1422 | - }else{ | |
| 1423 | - @ tags: %h(zTagList), | |
| 1424 | - } | |
| 1425 | - @ date: | |
| 1426 | - hyperlink_to_date(zDate, ")"); | |
| 1427 | - tag_private_status(rid); | |
| 1428 | - } | |
| 1429 | - db_finalize(&q); | |
| 1430 | -} | |
| 1431 | -#endif /* not used */ | |
| 1432 | - | |
| 1433 | 1373 | |
| 1434 | 1374 | /* |
| 1435 | 1375 | ** WEBPAGE: vdiff |
| 1436 | 1376 | ** URL: /vdiff?from=TAG&to=TAG |
| 1437 | 1377 | ** |
| @@ -3941,25 +3881,26 @@ | ||
| 3941 | 3881 | ** Usage: %fossil amend HASH OPTION ?OPTION ...? |
| 3942 | 3882 | ** |
| 3943 | 3883 | ** Amend the tags on check-in HASH to change how it displays in the timeline. |
| 3944 | 3884 | ** |
| 3945 | 3885 | ** Options: |
| 3946 | -** --author USER Make USER the author for check-in | |
| 3947 | -** -m|--comment COMMENT Make COMMENT the check-in comment | |
| 3948 | -** -M|--message-file FILE Read the amended comment from FILE | |
| 3949 | -** -e|--edit-comment Launch editor to revise comment | |
| 3950 | -** --date DATETIME Make DATETIME the check-in time | |
| 3951 | -** --bgcolor COLOR Apply COLOR to this check-in | |
| 3952 | -** --branchcolor COLOR Apply and propagate COLOR to the branch | |
| 3953 | -** --tag TAG Add new TAG to this check-in | |
| 3954 | -** --cancel TAG Cancel TAG from this check-in | |
| 3955 | -** --branch NAME Rename branch of check-in to NAME | |
| 3956 | -** --hide Hide branch starting from this check-in | |
| 3957 | -** --close Mark this "leaf" as closed | |
| 3958 | -** -n|--dry-run Print control artifact, but make no changes | |
| 3959 | -** --date-override DATETIME Set the change time on the control artifact | |
| 3960 | -** --user-override USER Set the user name on the control artifact | |
| 3886 | +** --author USER Make USER the author for check-in | |
| 3887 | +** --bgcolor COLOR Apply COLOR to this check-in | |
| 3888 | +** --branch NAME Rename branch of check-in to NAME | |
| 3889 | +** --branchcolor COLOR Apply and propagate COLOR to the branch | |
| 3890 | +** --cancel TAG Cancel TAG from this check-in | |
| 3891 | +** --close Mark this "leaf" as closed | |
| 3892 | +** --date DATETIME Make DATETIME the check-in time | |
| 3893 | +** --date-override DATETIME Set the change time on the control artifact | |
| 3894 | +** -e|--edit-comment Launch editor to revise comment | |
| 3895 | +** --hide Hide branch starting from this check-in | |
| 3896 | +** -m|--comment COMMENT Make COMMENT the check-in comment | |
| 3897 | +** -M|--message-file FILE Read the amended comment from FILE | |
| 3898 | +** -n|--dry-run Print control artifact, but make no changes | |
| 3899 | +** --no-verify-comment Do not validate the check-in comment | |
| 3900 | +** --tag TAG Add new TAG to this check-in | |
| 3901 | +** --user-override USER Set the user name on the control artifact | |
| 3961 | 3902 | ** |
| 3962 | 3903 | ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in |
| 3963 | 3904 | ** year-month-day form, it may be truncated, the "T" may be replaced by |
| 3964 | 3905 | ** a space, and it may also name a timezone offset from UTC as "-HH:MM" |
| 3965 | 3906 | ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" |
| @@ -3986,19 +3927,22 @@ | ||
| 3986 | 3927 | int fNewPropagateColor = 0; /* True if color propagates after amend */ |
| 3987 | 3928 | int fHasHidden = 0; /* True if hidden tag already set */ |
| 3988 | 3929 | int fHasClosed = 0; /* True if closed tag already set */ |
| 3989 | 3930 | int fEditComment; /* True if editor to be used for comment */ |
| 3990 | 3931 | int fDryRun; /* Print control artifact, make no changes */ |
| 3932 | + int noVerifyCom = 0; /* Allow suspicious check-in comments */ | |
| 3991 | 3933 | const char *zChngTime; /* The change time on the control artifact */ |
| 3992 | 3934 | const char *zUserOvrd; /* The user name on the control artifact */ |
| 3993 | 3935 | const char *zUuid; |
| 3994 | 3936 | Blob ctrl; |
| 3995 | 3937 | Blob comment; |
| 3996 | 3938 | char *zNow; |
| 3997 | 3939 | int nTags, nCancels; |
| 3998 | 3940 | int i; |
| 3999 | 3941 | Stmt q; |
| 3942 | + int ckComFlgs; /* Flags passed to suspicious_comment() */ | |
| 3943 | + | |
| 4000 | 3944 | |
| 4001 | 3945 | fEditComment = find_option("edit-comment","e",0)!=0; |
| 4002 | 3946 | zNewComment = find_option("comment","m",1); |
| 4003 | 3947 | zComFile = find_option("message-file","M",1); |
| 4004 | 3948 | zNewBranch = find_option("branch",0,1); |
| @@ -4016,10 +3960,11 @@ | ||
| 4016 | 3960 | fHide = find_option("hide",0,0)!=0; |
| 4017 | 3961 | fDryRun = find_option("dry-run","n",0)!=0; |
| 4018 | 3962 | zChngTime = find_option("date-override",0,1); |
| 4019 | 3963 | if( zChngTime==0 ) zChngTime = find_option("chngtime",0,1); |
| 4020 | 3964 | zUserOvrd = find_option("user-override",0,1); |
| 3965 | + noVerifyCom = find_option("no-verify-comment",0,0)!=0; | |
| 4021 | 3966 | db_find_and_open_repository(0,0); |
| 4022 | 3967 | user_select(); |
| 4023 | 3968 | verify_all_options(); |
| 4024 | 3969 | if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT); |
| 4025 | 3970 | rid = name_to_typed_rid(g.argv[2], "ci"); |
| @@ -4074,21 +4019,74 @@ | ||
| 4074 | 4019 | ); |
| 4075 | 4020 | } |
| 4076 | 4021 | if( (zNewColor!=0 && zNewColor[0]==0) && (zColor && zColor[0] ) ){ |
| 4077 | 4022 | cancel_color(); |
| 4078 | 4023 | } |
| 4079 | - if( fEditComment ){ | |
| 4080 | - prepare_amend_comment(&comment, zComment, zUuid); | |
| 4081 | - zNewComment = blob_str(&comment); | |
| 4082 | - }else if( zComFile ){ | |
| 4083 | - blob_zero(&comment); | |
| 4084 | - blob_read_from_file(&comment, zComFile, ExtFILE); | |
| 4085 | - blob_to_utf8_no_bom(&comment, 1); | |
| 4086 | - zNewComment = blob_str(&comment); | |
| 4087 | - } | |
| 4088 | - if( zNewComment && zNewComment[0] | |
| 4089 | - && comment_compare(zComment,zNewComment)==0 ) add_comment(zNewComment); | |
| 4024 | + if( fEditComment || zNewComment || zComFile ){ | |
| 4025 | + blob_init(&comment, 0, 0); | |
| 4026 | + | |
| 4027 | + /* Figure out how much comment verification is requested */ | |
| 4028 | + if( noVerifyCom ){ | |
| 4029 | + ckComFlgs = 0; | |
| 4030 | + }else{ | |
| 4031 | + const char *zVerComs = db_get("verify-comments","on"); | |
| 4032 | + if( is_false(zVerComs) ){ | |
| 4033 | + ckComFlgs = 0; | |
| 4034 | + }else if( strcmp(zVerComs,"preview")==0 ){ | |
| 4035 | + ckComFlgs = COMCK_PREVIEW | COMCK_LINKS | COMCK_MARKUP; | |
| 4036 | + }else if( strcmp(zVerComs,"links")==0 ){ | |
| 4037 | + ckComFlgs = COMCK_LINKS; | |
| 4038 | + }else{ | |
| 4039 | + ckComFlgs = COMCK_LINKS | COMCK_MARKUP; | |
| 4040 | + } | |
| 4041 | + if( zNewComment || zComFile ){ | |
| 4042 | + ckComFlgs = (ckComFlgs & COMCK_LINKS) | COMCK_NOPREVIEW; | |
| 4043 | + } | |
| 4044 | + } | |
| 4045 | + if( fEditComment ){ | |
| 4046 | + prepare_amend_comment(&comment, zComment, zUuid); | |
| 4047 | + }else if( zComFile ){ | |
| 4048 | + blob_read_from_file(&comment, zComFile, ExtFILE); | |
| 4049 | + blob_to_utf8_no_bom(&comment, 1); | |
| 4050 | + }else if( zNewComment ){ | |
| 4051 | + blob_init(&comment, zNewComment, -1); | |
| 4052 | + } | |
| 4053 | + if( blob_size(&comment)>0 | |
| 4054 | + && comment_compare(zComment, blob_str(&comment))==0 | |
| 4055 | + ){ | |
| 4056 | + int rc; | |
| 4057 | + while( (rc = suspicious_comment(&comment, ckComFlgs))!=0 ){ | |
| 4058 | + char cReply; | |
| 4059 | + Blob ans; | |
| 4060 | + if( !fEditComment ){ | |
| 4061 | + fossil_fatal("Amend aborted; " | |
| 4062 | + "use --no-verify-comment to override"); | |
| 4063 | + } | |
| 4064 | + if( rc==COMCK_PREVIEW ){ | |
| 4065 | + prompt_user("\nContinue (Y/n/e=edit)? ", &ans); | |
| 4066 | + }else{ | |
| 4067 | + prompt_user("\nContinue (y/n/E=edit)? ", &ans); | |
| 4068 | + } | |
| 4069 | + cReply = blob_str(&ans)[0]; | |
| 4070 | + cReply = fossil_tolower(cReply); | |
| 4071 | + blob_reset(&ans); | |
| 4072 | + if( cReply=='n' ){ | |
| 4073 | + fossil_fatal("Amend aborted."); | |
| 4074 | + } | |
| 4075 | + if( cReply=='e' || (cReply!='y' && rc!=COMCK_PREVIEW) ){ | |
| 4076 | + char *zPrior = blob_materialize(&comment); | |
| 4077 | + blob_init(&comment, 0, 0); | |
| 4078 | + prepare_amend_comment(&comment, zPrior, zUuid); | |
| 4079 | + fossil_free(zPrior); | |
| 4080 | + continue; | |
| 4081 | + }else{ | |
| 4082 | + break; | |
| 4083 | + } | |
| 4084 | + } | |
| 4085 | + } | |
| 4086 | + add_comment(blob_str(&comment)); | |
| 4087 | + } | |
| 4090 | 4088 | if( zNewDate && zNewDate[0] && fossil_strcmp(zDate,zNewDate)!=0 ){ |
| 4091 | 4089 | if( is_datetime(zNewDate) ){ |
| 4092 | 4090 | add_date(zNewDate); |
| 4093 | 4091 | }else{ |
| 4094 | 4092 | fossil_fatal("Unsupported date format, use YYYY-MM-DD HH:MM:SS"); |
| 4095 | 4093 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -257,17 +257,19 @@ | |
| 257 | } |
| 258 | fossil_print("fossil: %z\n", file_fullexename(g.nameOfExe)); |
| 259 | fossil_print("version: %s", z); |
| 260 | blob_reset(&vx); |
| 261 | } |
| 262 | }else{ |
| 263 | int rid; |
| 264 | rid = name_to_rid(g.argv[2]); |
| 265 | if( rid==0 ){ |
| 266 | fossil_fatal("no such object: %s", g.argv[2]); |
| 267 | } |
| 268 | show_common_info(rid, "hash:", 1, 1); |
| 269 | } |
| 270 | } |
| 271 | |
| 272 | /* |
| 273 | ** Show the context graph (immediate parents and children) for |
| @@ -925,12 +927,12 @@ | |
| 925 | " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim", |
| 926 | rid |
| 927 | ); |
| 928 | isLeaf = !db_exists("SELECT 1 FROM plink WHERE pid=%d", rid); |
| 929 | db_prepare(&q1, |
| 930 | "SELECT uuid, datetime(mtime,toLocal()), user, comment," |
| 931 | " datetime(omtime,toLocal()), mtime" |
| 932 | " FROM blob, event" |
| 933 | " WHERE blob.rid=%d" |
| 934 | " AND event.objid=%d", |
| 935 | rid, rid |
| 936 | ); |
| @@ -1366,72 +1368,10 @@ | |
| 1366 | webpage_error("Artifact %s is not a check-in.", P(zParam)); |
| 1367 | return 0; |
| 1368 | } |
| 1369 | return manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 1370 | } |
| 1371 | |
| 1372 | #if 0 /* not used */ |
| 1373 | /* |
| 1374 | ** Output a description of a check-in |
| 1375 | */ |
| 1376 | static void checkin_description(int rid){ |
| 1377 | Stmt q; |
| 1378 | db_prepare(&q, |
| 1379 | "SELECT datetime(mtime), coalesce(euser,user)," |
| 1380 | " coalesce(ecomment,comment), uuid," |
| 1381 | " (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref" |
| 1382 | " WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid" |
| 1383 | " AND tagxref.rid=blob.rid AND tagxref.tagtype>0)" |
| 1384 | " FROM event, blob" |
| 1385 | " WHERE event.objid=%d AND type='ci'" |
| 1386 | " AND blob.rid=%d", |
| 1387 | rid, rid |
| 1388 | ); |
| 1389 | while( db_step(&q)==SQLITE_ROW ){ |
| 1390 | const char *zDate = db_column_text(&q, 0); |
| 1391 | const char *zUser = db_column_text(&q, 1); |
| 1392 | const char *zUuid = db_column_text(&q, 3); |
| 1393 | const char *zTagList = db_column_text(&q, 4); |
| 1394 | Blob comment; |
| 1395 | int wikiFlags = WIKI_INLINE|WIKI_NOBADLINKS; |
| 1396 | if( db_get_boolean("timeline-block-markup", 0)==0 ){ |
| 1397 | wikiFlags |= WIKI_NOBLOCK; |
| 1398 | } |
| 1399 | hyperlink_to_version(zUuid); |
| 1400 | blob_zero(&comment); |
| 1401 | db_column_blob(&q, 2, &comment); |
| 1402 | wiki_convert(&comment, 0, wikiFlags); |
| 1403 | blob_reset(&comment); |
| 1404 | @ (user: |
| 1405 | hyperlink_to_user(zUser,zDate,","); |
| 1406 | if( zTagList && zTagList[0] && g.perm.Hyperlink ){ |
| 1407 | int i; |
| 1408 | const char *z = zTagList; |
| 1409 | Blob links; |
| 1410 | blob_zero(&links); |
| 1411 | while( z && z[0] ){ |
| 1412 | for(i=0; z[i] && (z[i]!=',' || z[i+1]!=' '); i++){} |
| 1413 | blob_appendf(&links, |
| 1414 | "%z%#h</a>%.2s", |
| 1415 | href("%R/timeline?r=%#t&nd&c=%t",i,z,zDate), i,z, &z[i] |
| 1416 | ); |
| 1417 | if( z[i]==0 ) break; |
| 1418 | z += i+2; |
| 1419 | } |
| 1420 | @ tags: %s(blob_str(&links)), |
| 1421 | blob_reset(&links); |
| 1422 | }else{ |
| 1423 | @ tags: %h(zTagList), |
| 1424 | } |
| 1425 | @ date: |
| 1426 | hyperlink_to_date(zDate, ")"); |
| 1427 | tag_private_status(rid); |
| 1428 | } |
| 1429 | db_finalize(&q); |
| 1430 | } |
| 1431 | #endif /* not used */ |
| 1432 | |
| 1433 | |
| 1434 | /* |
| 1435 | ** WEBPAGE: vdiff |
| 1436 | ** URL: /vdiff?from=TAG&to=TAG |
| 1437 | ** |
| @@ -3941,25 +3881,26 @@ | |
| 3941 | ** Usage: %fossil amend HASH OPTION ?OPTION ...? |
| 3942 | ** |
| 3943 | ** Amend the tags on check-in HASH to change how it displays in the timeline. |
| 3944 | ** |
| 3945 | ** Options: |
| 3946 | ** --author USER Make USER the author for check-in |
| 3947 | ** -m|--comment COMMENT Make COMMENT the check-in comment |
| 3948 | ** -M|--message-file FILE Read the amended comment from FILE |
| 3949 | ** -e|--edit-comment Launch editor to revise comment |
| 3950 | ** --date DATETIME Make DATETIME the check-in time |
| 3951 | ** --bgcolor COLOR Apply COLOR to this check-in |
| 3952 | ** --branchcolor COLOR Apply and propagate COLOR to the branch |
| 3953 | ** --tag TAG Add new TAG to this check-in |
| 3954 | ** --cancel TAG Cancel TAG from this check-in |
| 3955 | ** --branch NAME Rename branch of check-in to NAME |
| 3956 | ** --hide Hide branch starting from this check-in |
| 3957 | ** --close Mark this "leaf" as closed |
| 3958 | ** -n|--dry-run Print control artifact, but make no changes |
| 3959 | ** --date-override DATETIME Set the change time on the control artifact |
| 3960 | ** --user-override USER Set the user name on the control artifact |
| 3961 | ** |
| 3962 | ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in |
| 3963 | ** year-month-day form, it may be truncated, the "T" may be replaced by |
| 3964 | ** a space, and it may also name a timezone offset from UTC as "-HH:MM" |
| 3965 | ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" |
| @@ -3986,19 +3927,22 @@ | |
| 3986 | int fNewPropagateColor = 0; /* True if color propagates after amend */ |
| 3987 | int fHasHidden = 0; /* True if hidden tag already set */ |
| 3988 | int fHasClosed = 0; /* True if closed tag already set */ |
| 3989 | int fEditComment; /* True if editor to be used for comment */ |
| 3990 | int fDryRun; /* Print control artifact, make no changes */ |
| 3991 | const char *zChngTime; /* The change time on the control artifact */ |
| 3992 | const char *zUserOvrd; /* The user name on the control artifact */ |
| 3993 | const char *zUuid; |
| 3994 | Blob ctrl; |
| 3995 | Blob comment; |
| 3996 | char *zNow; |
| 3997 | int nTags, nCancels; |
| 3998 | int i; |
| 3999 | Stmt q; |
| 4000 | |
| 4001 | fEditComment = find_option("edit-comment","e",0)!=0; |
| 4002 | zNewComment = find_option("comment","m",1); |
| 4003 | zComFile = find_option("message-file","M",1); |
| 4004 | zNewBranch = find_option("branch",0,1); |
| @@ -4016,10 +3960,11 @@ | |
| 4016 | fHide = find_option("hide",0,0)!=0; |
| 4017 | fDryRun = find_option("dry-run","n",0)!=0; |
| 4018 | zChngTime = find_option("date-override",0,1); |
| 4019 | if( zChngTime==0 ) zChngTime = find_option("chngtime",0,1); |
| 4020 | zUserOvrd = find_option("user-override",0,1); |
| 4021 | db_find_and_open_repository(0,0); |
| 4022 | user_select(); |
| 4023 | verify_all_options(); |
| 4024 | if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT); |
| 4025 | rid = name_to_typed_rid(g.argv[2], "ci"); |
| @@ -4074,21 +4019,74 @@ | |
| 4074 | ); |
| 4075 | } |
| 4076 | if( (zNewColor!=0 && zNewColor[0]==0) && (zColor && zColor[0] ) ){ |
| 4077 | cancel_color(); |
| 4078 | } |
| 4079 | if( fEditComment ){ |
| 4080 | prepare_amend_comment(&comment, zComment, zUuid); |
| 4081 | zNewComment = blob_str(&comment); |
| 4082 | }else if( zComFile ){ |
| 4083 | blob_zero(&comment); |
| 4084 | blob_read_from_file(&comment, zComFile, ExtFILE); |
| 4085 | blob_to_utf8_no_bom(&comment, 1); |
| 4086 | zNewComment = blob_str(&comment); |
| 4087 | } |
| 4088 | if( zNewComment && zNewComment[0] |
| 4089 | && comment_compare(zComment,zNewComment)==0 ) add_comment(zNewComment); |
| 4090 | if( zNewDate && zNewDate[0] && fossil_strcmp(zDate,zNewDate)!=0 ){ |
| 4091 | if( is_datetime(zNewDate) ){ |
| 4092 | add_date(zNewDate); |
| 4093 | }else{ |
| 4094 | fossil_fatal("Unsupported date format, use YYYY-MM-DD HH:MM:SS"); |
| 4095 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -257,17 +257,19 @@ | |
| 257 | } |
| 258 | fossil_print("fossil: %z\n", file_fullexename(g.nameOfExe)); |
| 259 | fossil_print("version: %s", z); |
| 260 | blob_reset(&vx); |
| 261 | } |
| 262 | }else if( g.repositoryOpen ){ |
| 263 | int rid; |
| 264 | rid = name_to_rid(g.argv[2]); |
| 265 | if( rid==0 ){ |
| 266 | fossil_fatal("no such object: %s", g.argv[2]); |
| 267 | } |
| 268 | show_common_info(rid, "hash:", 1, 1); |
| 269 | }else{ |
| 270 | fossil_fatal("Could not find or open a Fossil repository"); |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | /* |
| 275 | ** Show the context graph (immediate parents and children) for |
| @@ -925,12 +927,12 @@ | |
| 927 | " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim", |
| 928 | rid |
| 929 | ); |
| 930 | isLeaf = !db_exists("SELECT 1 FROM plink WHERE pid=%d", rid); |
| 931 | db_prepare(&q1, |
| 932 | "SELECT uuid, datetime(mtime,toLocal(),'subsec'), user, comment," |
| 933 | " datetime(omtime,toLocal(),'subsec'), mtime" |
| 934 | " FROM blob, event" |
| 935 | " WHERE blob.rid=%d" |
| 936 | " AND event.objid=%d", |
| 937 | rid, rid |
| 938 | ); |
| @@ -1366,72 +1368,10 @@ | |
| 1368 | webpage_error("Artifact %s is not a check-in.", P(zParam)); |
| 1369 | return 0; |
| 1370 | } |
| 1371 | return manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 1372 | } |
| 1373 | |
| 1374 | /* |
| 1375 | ** WEBPAGE: vdiff |
| 1376 | ** URL: /vdiff?from=TAG&to=TAG |
| 1377 | ** |
| @@ -3941,25 +3881,26 @@ | |
| 3881 | ** Usage: %fossil amend HASH OPTION ?OPTION ...? |
| 3882 | ** |
| 3883 | ** Amend the tags on check-in HASH to change how it displays in the timeline. |
| 3884 | ** |
| 3885 | ** Options: |
| 3886 | ** --author USER Make USER the author for check-in |
| 3887 | ** --bgcolor COLOR Apply COLOR to this check-in |
| 3888 | ** --branch NAME Rename branch of check-in to NAME |
| 3889 | ** --branchcolor COLOR Apply and propagate COLOR to the branch |
| 3890 | ** --cancel TAG Cancel TAG from this check-in |
| 3891 | ** --close Mark this "leaf" as closed |
| 3892 | ** --date DATETIME Make DATETIME the check-in time |
| 3893 | ** --date-override DATETIME Set the change time on the control artifact |
| 3894 | ** -e|--edit-comment Launch editor to revise comment |
| 3895 | ** --hide Hide branch starting from this check-in |
| 3896 | ** -m|--comment COMMENT Make COMMENT the check-in comment |
| 3897 | ** -M|--message-file FILE Read the amended comment from FILE |
| 3898 | ** -n|--dry-run Print control artifact, but make no changes |
| 3899 | ** --no-verify-comment Do not validate the check-in comment |
| 3900 | ** --tag TAG Add new TAG to this check-in |
| 3901 | ** --user-override USER Set the user name on the control artifact |
| 3902 | ** |
| 3903 | ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in |
| 3904 | ** year-month-day form, it may be truncated, the "T" may be replaced by |
| 3905 | ** a space, and it may also name a timezone offset from UTC as "-HH:MM" |
| 3906 | ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" |
| @@ -3986,19 +3927,22 @@ | |
| 3927 | int fNewPropagateColor = 0; /* True if color propagates after amend */ |
| 3928 | int fHasHidden = 0; /* True if hidden tag already set */ |
| 3929 | int fHasClosed = 0; /* True if closed tag already set */ |
| 3930 | int fEditComment; /* True if editor to be used for comment */ |
| 3931 | int fDryRun; /* Print control artifact, make no changes */ |
| 3932 | int noVerifyCom = 0; /* Allow suspicious check-in comments */ |
| 3933 | const char *zChngTime; /* The change time on the control artifact */ |
| 3934 | const char *zUserOvrd; /* The user name on the control artifact */ |
| 3935 | const char *zUuid; |
| 3936 | Blob ctrl; |
| 3937 | Blob comment; |
| 3938 | char *zNow; |
| 3939 | int nTags, nCancels; |
| 3940 | int i; |
| 3941 | Stmt q; |
| 3942 | int ckComFlgs; /* Flags passed to suspicious_comment() */ |
| 3943 | |
| 3944 | |
| 3945 | fEditComment = find_option("edit-comment","e",0)!=0; |
| 3946 | zNewComment = find_option("comment","m",1); |
| 3947 | zComFile = find_option("message-file","M",1); |
| 3948 | zNewBranch = find_option("branch",0,1); |
| @@ -4016,10 +3960,11 @@ | |
| 3960 | fHide = find_option("hide",0,0)!=0; |
| 3961 | fDryRun = find_option("dry-run","n",0)!=0; |
| 3962 | zChngTime = find_option("date-override",0,1); |
| 3963 | if( zChngTime==0 ) zChngTime = find_option("chngtime",0,1); |
| 3964 | zUserOvrd = find_option("user-override",0,1); |
| 3965 | noVerifyCom = find_option("no-verify-comment",0,0)!=0; |
| 3966 | db_find_and_open_repository(0,0); |
| 3967 | user_select(); |
| 3968 | verify_all_options(); |
| 3969 | if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT); |
| 3970 | rid = name_to_typed_rid(g.argv[2], "ci"); |
| @@ -4074,21 +4019,74 @@ | |
| 4019 | ); |
| 4020 | } |
| 4021 | if( (zNewColor!=0 && zNewColor[0]==0) && (zColor && zColor[0] ) ){ |
| 4022 | cancel_color(); |
| 4023 | } |
| 4024 | if( fEditComment || zNewComment || zComFile ){ |
| 4025 | blob_init(&comment, 0, 0); |
| 4026 | |
| 4027 | /* Figure out how much comment verification is requested */ |
| 4028 | if( noVerifyCom ){ |
| 4029 | ckComFlgs = 0; |
| 4030 | }else{ |
| 4031 | const char *zVerComs = db_get("verify-comments","on"); |
| 4032 | if( is_false(zVerComs) ){ |
| 4033 | ckComFlgs = 0; |
| 4034 | }else if( strcmp(zVerComs,"preview")==0 ){ |
| 4035 | ckComFlgs = COMCK_PREVIEW | COMCK_LINKS | COMCK_MARKUP; |
| 4036 | }else if( strcmp(zVerComs,"links")==0 ){ |
| 4037 | ckComFlgs = COMCK_LINKS; |
| 4038 | }else{ |
| 4039 | ckComFlgs = COMCK_LINKS | COMCK_MARKUP; |
| 4040 | } |
| 4041 | if( zNewComment || zComFile ){ |
| 4042 | ckComFlgs = (ckComFlgs & COMCK_LINKS) | COMCK_NOPREVIEW; |
| 4043 | } |
| 4044 | } |
| 4045 | if( fEditComment ){ |
| 4046 | prepare_amend_comment(&comment, zComment, zUuid); |
| 4047 | }else if( zComFile ){ |
| 4048 | blob_read_from_file(&comment, zComFile, ExtFILE); |
| 4049 | blob_to_utf8_no_bom(&comment, 1); |
| 4050 | }else if( zNewComment ){ |
| 4051 | blob_init(&comment, zNewComment, -1); |
| 4052 | } |
| 4053 | if( blob_size(&comment)>0 |
| 4054 | && comment_compare(zComment, blob_str(&comment))==0 |
| 4055 | ){ |
| 4056 | int rc; |
| 4057 | while( (rc = suspicious_comment(&comment, ckComFlgs))!=0 ){ |
| 4058 | char cReply; |
| 4059 | Blob ans; |
| 4060 | if( !fEditComment ){ |
| 4061 | fossil_fatal("Amend aborted; " |
| 4062 | "use --no-verify-comment to override"); |
| 4063 | } |
| 4064 | if( rc==COMCK_PREVIEW ){ |
| 4065 | prompt_user("\nContinue (Y/n/e=edit)? ", &ans); |
| 4066 | }else{ |
| 4067 | prompt_user("\nContinue (y/n/E=edit)? ", &ans); |
| 4068 | } |
| 4069 | cReply = blob_str(&ans)[0]; |
| 4070 | cReply = fossil_tolower(cReply); |
| 4071 | blob_reset(&ans); |
| 4072 | if( cReply=='n' ){ |
| 4073 | fossil_fatal("Amend aborted."); |
| 4074 | } |
| 4075 | if( cReply=='e' || (cReply!='y' && rc!=COMCK_PREVIEW) ){ |
| 4076 | char *zPrior = blob_materialize(&comment); |
| 4077 | blob_init(&comment, 0, 0); |
| 4078 | prepare_amend_comment(&comment, zPrior, zUuid); |
| 4079 | fossil_free(zPrior); |
| 4080 | continue; |
| 4081 | }else{ |
| 4082 | break; |
| 4083 | } |
| 4084 | } |
| 4085 | } |
| 4086 | add_comment(blob_str(&comment)); |
| 4087 | } |
| 4088 | if( zNewDate && zNewDate[0] && fossil_strcmp(zDate,zNewDate)!=0 ){ |
| 4089 | if( is_datetime(zNewDate) ){ |
| 4090 | add_date(zNewDate); |
| 4091 | }else{ |
| 4092 | fossil_fatal("Unsupported date format, use YYYY-MM-DD HH:MM:SS"); |
| 4093 |
+17
-6
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -998,15 +998,12 @@ | ||
| 998 | 998 | |
| 999 | 999 | /* |
| 1000 | 1000 | ** Remove n elements from g.argv beginning with the i-th element. |
| 1001 | 1001 | */ |
| 1002 | 1002 | static void remove_from_argv(int i, int n){ |
| 1003 | - int j; | |
| 1004 | - for(j=i+n; j<g.argc; i++, j++){ | |
| 1005 | - g.argv[i] = g.argv[j]; | |
| 1006 | - } | |
| 1007 | - g.argc = i; | |
| 1003 | + memmove(&g.argv[i], &g.argv[i+n], sizeof(g.argv[i])*(g.argc-i-n)); | |
| 1004 | + g.argc -= n; | |
| 1008 | 1005 | } |
| 1009 | 1006 | |
| 1010 | 1007 | |
| 1011 | 1008 | /* |
| 1012 | 1009 | ** Look for a command-line option. If present, remove it from the |
| @@ -1065,10 +1062,19 @@ | ||
| 1065 | 1062 | break; |
| 1066 | 1063 | } |
| 1067 | 1064 | } |
| 1068 | 1065 | return zReturn; |
| 1069 | 1066 | } |
| 1067 | + | |
| 1068 | +/* | |
| 1069 | +** Restore an option previously removed by find_option(). | |
| 1070 | +*/ | |
| 1071 | +void restore_option(const char *zName, const char *zValue, int hasOpt){ | |
| 1072 | + if( zValue==0 && hasOpt ) return; | |
| 1073 | + g.argv[g.argc++] = (char*)zName; | |
| 1074 | + if( hasOpt ) g.argv[g.argc++] = (char*)zValue; | |
| 1075 | +} | |
| 1070 | 1076 | |
| 1071 | 1077 | /* Return true if zOption exists in the command-line arguments, |
| 1072 | 1078 | ** but do not remove it from the list or otherwise process it. |
| 1073 | 1079 | */ |
| 1074 | 1080 | int has_option(const char *zOption){ |
| @@ -1849,10 +1855,15 @@ | ||
| 1849 | 1855 | ** not exist. |
| 1850 | 1856 | */ |
| 1851 | 1857 | zCleanRepo = file_cleanup_fullpath(zRepo); |
| 1852 | 1858 | if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){ |
| 1853 | 1859 | szFile = file_size(zCleanRepo, ExtFILE); |
| 1860 | + if( szFile>0 && !file_isfile(zCleanRepo, ExtFILE) ){ | |
| 1861 | + /* Only let szFile be non-negative if zCleanRepo really is a file | |
| 1862 | + ** and not a directory or some other filesystem object. */ | |
| 1863 | + szFile = -1; | |
| 1864 | + } | |
| 1854 | 1865 | if( g.fHttpTrace ){ |
| 1855 | 1866 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile); |
| 1856 | 1867 | @ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) --> |
| 1857 | 1868 | fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf); |
| 1858 | 1869 | } |
| @@ -2156,11 +2167,11 @@ | ||
| 2156 | 2167 | } |
| 2157 | 2168 | } |
| 2158 | 2169 | #endif |
| 2159 | 2170 | if( (pCmd->eCmdFlags & CMDFLAG_RAWCONTENT)==0 ){ |
| 2160 | 2171 | cgi_decode_post_parameters(); |
| 2161 | - if( !cgi_same_origin() ){ | |
| 2172 | + if( !cgi_same_origin(0) ){ | |
| 2162 | 2173 | isReadonly = 1; |
| 2163 | 2174 | db_protect(PROTECT_READONLY); |
| 2164 | 2175 | } |
| 2165 | 2176 | } |
| 2166 | 2177 | if( g.fCgiTrace ){ |
| 2167 | 2178 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -998,15 +998,12 @@ | |
| 998 | |
| 999 | /* |
| 1000 | ** Remove n elements from g.argv beginning with the i-th element. |
| 1001 | */ |
| 1002 | static void remove_from_argv(int i, int n){ |
| 1003 | int j; |
| 1004 | for(j=i+n; j<g.argc; i++, j++){ |
| 1005 | g.argv[i] = g.argv[j]; |
| 1006 | } |
| 1007 | g.argc = i; |
| 1008 | } |
| 1009 | |
| 1010 | |
| 1011 | /* |
| 1012 | ** Look for a command-line option. If present, remove it from the |
| @@ -1065,10 +1062,19 @@ | |
| 1065 | break; |
| 1066 | } |
| 1067 | } |
| 1068 | return zReturn; |
| 1069 | } |
| 1070 | |
| 1071 | /* Return true if zOption exists in the command-line arguments, |
| 1072 | ** but do not remove it from the list or otherwise process it. |
| 1073 | */ |
| 1074 | int has_option(const char *zOption){ |
| @@ -1849,10 +1855,15 @@ | |
| 1849 | ** not exist. |
| 1850 | */ |
| 1851 | zCleanRepo = file_cleanup_fullpath(zRepo); |
| 1852 | if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){ |
| 1853 | szFile = file_size(zCleanRepo, ExtFILE); |
| 1854 | if( g.fHttpTrace ){ |
| 1855 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile); |
| 1856 | @ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) --> |
| 1857 | fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf); |
| 1858 | } |
| @@ -2156,11 +2167,11 @@ | |
| 2156 | } |
| 2157 | } |
| 2158 | #endif |
| 2159 | if( (pCmd->eCmdFlags & CMDFLAG_RAWCONTENT)==0 ){ |
| 2160 | cgi_decode_post_parameters(); |
| 2161 | if( !cgi_same_origin() ){ |
| 2162 | isReadonly = 1; |
| 2163 | db_protect(PROTECT_READONLY); |
| 2164 | } |
| 2165 | } |
| 2166 | if( g.fCgiTrace ){ |
| 2167 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -998,15 +998,12 @@ | |
| 998 | |
| 999 | /* |
| 1000 | ** Remove n elements from g.argv beginning with the i-th element. |
| 1001 | */ |
| 1002 | static void remove_from_argv(int i, int n){ |
| 1003 | memmove(&g.argv[i], &g.argv[i+n], sizeof(g.argv[i])*(g.argc-i-n)); |
| 1004 | g.argc -= n; |
| 1005 | } |
| 1006 | |
| 1007 | |
| 1008 | /* |
| 1009 | ** Look for a command-line option. If present, remove it from the |
| @@ -1065,10 +1062,19 @@ | |
| 1062 | break; |
| 1063 | } |
| 1064 | } |
| 1065 | return zReturn; |
| 1066 | } |
| 1067 | |
| 1068 | /* |
| 1069 | ** Restore an option previously removed by find_option(). |
| 1070 | */ |
| 1071 | void restore_option(const char *zName, const char *zValue, int hasOpt){ |
| 1072 | if( zValue==0 && hasOpt ) return; |
| 1073 | g.argv[g.argc++] = (char*)zName; |
| 1074 | if( hasOpt ) g.argv[g.argc++] = (char*)zValue; |
| 1075 | } |
| 1076 | |
| 1077 | /* Return true if zOption exists in the command-line arguments, |
| 1078 | ** but do not remove it from the list or otherwise process it. |
| 1079 | */ |
| 1080 | int has_option(const char *zOption){ |
| @@ -1849,10 +1855,15 @@ | |
| 1855 | ** not exist. |
| 1856 | */ |
| 1857 | zCleanRepo = file_cleanup_fullpath(zRepo); |
| 1858 | if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){ |
| 1859 | szFile = file_size(zCleanRepo, ExtFILE); |
| 1860 | if( szFile>0 && !file_isfile(zCleanRepo, ExtFILE) ){ |
| 1861 | /* Only let szFile be non-negative if zCleanRepo really is a file |
| 1862 | ** and not a directory or some other filesystem object. */ |
| 1863 | szFile = -1; |
| 1864 | } |
| 1865 | if( g.fHttpTrace ){ |
| 1866 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile); |
| 1867 | @ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) --> |
| 1868 | fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf); |
| 1869 | } |
| @@ -2156,11 +2167,11 @@ | |
| 2167 | } |
| 2168 | } |
| 2169 | #endif |
| 2170 | if( (pCmd->eCmdFlags & CMDFLAG_RAWCONTENT)==0 ){ |
| 2171 | cgi_decode_post_parameters(); |
| 2172 | if( !cgi_same_origin(0) ){ |
| 2173 | isReadonly = 1; |
| 2174 | db_protect(PROTECT_READONLY); |
| 2175 | } |
| 2176 | } |
| 2177 | if( g.fCgiTrace ){ |
| 2178 |
+51
-17
| --- src/name.c | ||
| +++ src/name.c | ||
| @@ -58,29 +58,42 @@ | ||
| 58 | 58 | ** Check to see if the string might be a compact date/time that omits |
| 59 | 59 | ** the punctuation. Example: "20190327084549" instead of |
| 60 | 60 | ** "2019-03-27 08:45:49". If the string is of the appropriate form, |
| 61 | 61 | ** then return an alternative string (in static space) that is the same |
| 62 | 62 | ** string with punctuation inserted. |
| 63 | +** | |
| 64 | +** If the bRoundUp parameter is true, then round the resulting date-time | |
| 65 | +** up to the largest date/time that is consistent with the input value. | |
| 66 | +** This is because the result will be used for an mtime<=julianday($DATE) | |
| 67 | +** comparison. In other words: | |
| 68 | +** | |
| 69 | +** 20250317123421 -> 2025-03-17 12:34:21.999 | |
| 70 | +** ^^^^--- Added | |
| 71 | +** | |
| 72 | +** 202503171234 -> 2025-03-17 12:34:59.999 | |
| 73 | +** ^^^^^^^--- Added | |
| 74 | +** 20250317 -> 2025-03-17 23:59:59.999 | |
| 75 | +** ^^^^^^^^^^^^--- Added | |
| 63 | 76 | ** |
| 64 | 77 | ** If the bVerifyNotAHash flag is true, then a check is made to see if |
| 65 | -** the string is a hash prefix and NULL is returned if it is. If the | |
| 78 | +** the input string is a hash prefix and NULL is returned if it is. If the | |
| 66 | 79 | ** bVerifyNotAHash flag is false, then the result is determined by syntax |
| 67 | 80 | ** of the input string only, without reference to the artifact table. |
| 68 | 81 | */ |
| 69 | -char *fossil_expand_datetime(const char *zIn, int bVerifyNotAHash){ | |
| 82 | +char *fossil_expand_datetime(const char *zIn,int bVerifyNotAHash,int bRoundUp){ | |
| 70 | 83 | static char zEDate[24]; |
| 71 | 84 | static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' }; |
| 72 | 85 | int n = (int)strlen(zIn); |
| 73 | 86 | int i, j; |
| 74 | 87 | int addZulu = 0; |
| 75 | 88 | |
| 76 | 89 | /* These forms are allowed: |
| 77 | 90 | ** |
| 78 | - ** 123456789 1234 123456789 123456789 | |
| 79 | - ** (1) YYYYMMDD => YYYY-MM-DD | |
| 80 | - ** (2) YYYYMMDDHHMM => YYYY-MM-DD HH:MM | |
| 81 | - ** (3) YYYYMMDDHHMMSS => YYYY-MM-DD HH:MM:SS | |
| 91 | + ** 123456789 1234 123456789 123456789 1234 | |
| 92 | + ** (1) YYYYMMDD => YYYY-MM-DD 23:59:59.999 | |
| 93 | + ** (2) YYYYMMDDHHMM => YYYY-MM-DD HH:MM:59.999 | |
| 94 | + ** (3) YYYYMMDDHHMMSS => YYYY-MM-DD HH:MM:SS.999 | |
| 82 | 95 | ** |
| 83 | 96 | ** An optional "Z" zulu timezone designator is allowed at the end. |
| 84 | 97 | */ |
| 85 | 98 | if( n>0 && (zIn[n-1]=='Z' || zIn[n-1]=='z') ){ |
| 86 | 99 | n--; |
| @@ -99,15 +112,23 @@ | ||
| 99 | 112 | if( i>=4 && (i%2)==0 ){ |
| 100 | 113 | zEDate[j++] = aPunct[i/2]; |
| 101 | 114 | } |
| 102 | 115 | zEDate[j++] = zIn[i]; |
| 103 | 116 | } |
| 104 | - if( addZulu ){ | |
| 117 | + if( bRoundUp ){ | |
| 105 | 118 | if( j==10 ){ |
| 106 | - memcpy(&zEDate[10]," 00:00", 6); | |
| 107 | - j += 6; | |
| 119 | + memcpy(&zEDate[10], " 23:59:59.999", 13); | |
| 120 | + j += 13; | |
| 121 | + }else if( j==16 ){ | |
| 122 | + memcpy(&zEDate[16], ":59.999",7); | |
| 123 | + j += 7; | |
| 124 | + }else if( j==19 ){ | |
| 125 | + memcpy(&zEDate[19], ".999", 4); | |
| 126 | + j += 4; | |
| 108 | 127 | } |
| 128 | + } | |
| 129 | + if( addZulu ){ | |
| 109 | 130 | zEDate[j++] = 'Z'; |
| 110 | 131 | } |
| 111 | 132 | zEDate[j] = 0; |
| 112 | 133 | |
| 113 | 134 | /* Check for reasonable date values. |
| @@ -147,27 +168,40 @@ | ||
| 147 | 168 | ** comparison. So add in missing factional seconds or seconds or time. |
| 148 | 169 | ** |
| 149 | 170 | ** The returned string is held in a static buffer that is overwritten |
| 150 | 171 | ** with each call, or else is just a copy of its input if there are |
| 151 | 172 | ** no changes. |
| 173 | +** | |
| 174 | +** For reference: | |
| 175 | +** | |
| 176 | +** 0123456789 123456789 1234 | |
| 177 | +** YYYY-MM-DD HH:MM:SS.SSSz | |
| 152 | 178 | */ |
| 153 | 179 | const char *fossil_roundup_date(const char *zDate){ |
| 154 | - static char zUp[24]; | |
| 180 | + static char zUp[28]; | |
| 155 | 181 | int n = (int)strlen(zDate); |
| 182 | + int addZ = 0; | |
| 183 | + if( n>10 && (zDate[n-1]=='z' || zDate[n-1]=='Z') ){ | |
| 184 | + n--; | |
| 185 | + addZ = 1; | |
| 186 | + } | |
| 156 | 187 | if( n==19 ){ /* YYYY-MM-DD HH:MM:SS */ |
| 157 | 188 | memcpy(zUp, zDate, 19); |
| 158 | - memcpy(zUp+19, ".999", 5); | |
| 189 | + memcpy(zUp+19, ".999z", 6); | |
| 190 | + if( !addZ ) zUp[23] = 0; | |
| 159 | 191 | return zUp; |
| 160 | 192 | } |
| 161 | 193 | if( n==16 ){ /* YYYY-MM-DD HH:MM */ |
| 162 | 194 | memcpy(zUp, zDate, 16); |
| 163 | - memcpy(zUp+16, ":59.999", 8); | |
| 195 | + memcpy(zUp+16, ":59.999z", 8); | |
| 196 | + if( !addZ ) zUp[23] = 0; | |
| 164 | 197 | return zUp; |
| 165 | 198 | } |
| 166 | 199 | if( n==10 ){ /* YYYY-MM-DD */ |
| 167 | 200 | memcpy(zUp, zDate, 10); |
| 168 | - memcpy(zUp+10, " 23:59:59.999", 14); | |
| 201 | + memcpy(zUp+10, " 23:59:59.999z", 14); | |
| 202 | + if( !addZ ) zUp[23] = 0; | |
| 169 | 203 | return zUp; |
| 170 | 204 | } |
| 171 | 205 | return zDate; |
| 172 | 206 | } |
| 173 | 207 | |
| @@ -479,11 +513,11 @@ | ||
| 479 | 513 | if( rid ) return rid; |
| 480 | 514 | } |
| 481 | 515 | |
| 482 | 516 | /* Date and times */ |
| 483 | 517 | if( memcmp(zTag, "date:", 5)==0 ){ |
| 484 | - zDate = fossil_expand_datetime(&zTag[5],0); | |
| 518 | + zDate = fossil_expand_datetime(&zTag[5],0,1); | |
| 485 | 519 | if( zDate==0 ) zDate = &zTag[5]; |
| 486 | 520 | rid = db_int(0, |
| 487 | 521 | "SELECT objid FROM event" |
| 488 | 522 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 489 | 523 | " ORDER BY mtime DESC LIMIT 1", |
| @@ -545,21 +579,21 @@ | ||
| 545 | 579 | |
| 546 | 580 | /* symbolic-name ":" date-time */ |
| 547 | 581 | nTag = strlen(zTag); |
| 548 | 582 | for(i=0; i<nTag-8 && zTag[i]!=':'; i++){} |
| 549 | 583 | if( zTag[i]==':' |
| 550 | - && (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0)!=0) | |
| 584 | + && (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0,0)!=0) | |
| 551 | 585 | ){ |
| 552 | 586 | char *zDate = mprintf("%s", &zTag[i+1]); |
| 553 | 587 | char *zTagBase = mprintf("%.*s", i, zTag); |
| 554 | 588 | char *zXDate; |
| 555 | 589 | int nDate = strlen(zDate); |
| 556 | 590 | if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ |
| 557 | 591 | zDate[nDate-3] = 'z'; |
| 558 | 592 | zDate[nDate-2] = 0; |
| 559 | 593 | } |
| 560 | - zXDate = fossil_expand_datetime(zDate,0); | |
| 594 | + zXDate = fossil_expand_datetime(zDate,0,1); | |
| 561 | 595 | if( zXDate==0 ) zXDate = zDate; |
| 562 | 596 | rid = db_int(0, |
| 563 | 597 | "SELECT event.objid, max(event.mtime)" |
| 564 | 598 | " FROM tag, tagxref, event" |
| 565 | 599 | " WHERE tag.tagname='sym-%q' " |
| @@ -631,11 +665,11 @@ | ||
| 631 | 665 | if( startOfBranch ) rid = start_of_branch(rid,1); |
| 632 | 666 | return rid; |
| 633 | 667 | } |
| 634 | 668 | |
| 635 | 669 | /* Pure numeric date/time */ |
| 636 | - zDate = fossil_expand_datetime(zTag, 0); | |
| 670 | + zDate = fossil_expand_datetime(zTag, 0,1); | |
| 637 | 671 | if( zDate ){ |
| 638 | 672 | rid = db_int(0, |
| 639 | 673 | "SELECT objid FROM event" |
| 640 | 674 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 641 | 675 | " ORDER BY mtime DESC LIMIT 1", |
| 642 | 676 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -58,29 +58,42 @@ | |
| 58 | ** Check to see if the string might be a compact date/time that omits |
| 59 | ** the punctuation. Example: "20190327084549" instead of |
| 60 | ** "2019-03-27 08:45:49". If the string is of the appropriate form, |
| 61 | ** then return an alternative string (in static space) that is the same |
| 62 | ** string with punctuation inserted. |
| 63 | ** |
| 64 | ** If the bVerifyNotAHash flag is true, then a check is made to see if |
| 65 | ** the string is a hash prefix and NULL is returned if it is. If the |
| 66 | ** bVerifyNotAHash flag is false, then the result is determined by syntax |
| 67 | ** of the input string only, without reference to the artifact table. |
| 68 | */ |
| 69 | char *fossil_expand_datetime(const char *zIn, int bVerifyNotAHash){ |
| 70 | static char zEDate[24]; |
| 71 | static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' }; |
| 72 | int n = (int)strlen(zIn); |
| 73 | int i, j; |
| 74 | int addZulu = 0; |
| 75 | |
| 76 | /* These forms are allowed: |
| 77 | ** |
| 78 | ** 123456789 1234 123456789 123456789 |
| 79 | ** (1) YYYYMMDD => YYYY-MM-DD |
| 80 | ** (2) YYYYMMDDHHMM => YYYY-MM-DD HH:MM |
| 81 | ** (3) YYYYMMDDHHMMSS => YYYY-MM-DD HH:MM:SS |
| 82 | ** |
| 83 | ** An optional "Z" zulu timezone designator is allowed at the end. |
| 84 | */ |
| 85 | if( n>0 && (zIn[n-1]=='Z' || zIn[n-1]=='z') ){ |
| 86 | n--; |
| @@ -99,15 +112,23 @@ | |
| 99 | if( i>=4 && (i%2)==0 ){ |
| 100 | zEDate[j++] = aPunct[i/2]; |
| 101 | } |
| 102 | zEDate[j++] = zIn[i]; |
| 103 | } |
| 104 | if( addZulu ){ |
| 105 | if( j==10 ){ |
| 106 | memcpy(&zEDate[10]," 00:00", 6); |
| 107 | j += 6; |
| 108 | } |
| 109 | zEDate[j++] = 'Z'; |
| 110 | } |
| 111 | zEDate[j] = 0; |
| 112 | |
| 113 | /* Check for reasonable date values. |
| @@ -147,27 +168,40 @@ | |
| 147 | ** comparison. So add in missing factional seconds or seconds or time. |
| 148 | ** |
| 149 | ** The returned string is held in a static buffer that is overwritten |
| 150 | ** with each call, or else is just a copy of its input if there are |
| 151 | ** no changes. |
| 152 | */ |
| 153 | const char *fossil_roundup_date(const char *zDate){ |
| 154 | static char zUp[24]; |
| 155 | int n = (int)strlen(zDate); |
| 156 | if( n==19 ){ /* YYYY-MM-DD HH:MM:SS */ |
| 157 | memcpy(zUp, zDate, 19); |
| 158 | memcpy(zUp+19, ".999", 5); |
| 159 | return zUp; |
| 160 | } |
| 161 | if( n==16 ){ /* YYYY-MM-DD HH:MM */ |
| 162 | memcpy(zUp, zDate, 16); |
| 163 | memcpy(zUp+16, ":59.999", 8); |
| 164 | return zUp; |
| 165 | } |
| 166 | if( n==10 ){ /* YYYY-MM-DD */ |
| 167 | memcpy(zUp, zDate, 10); |
| 168 | memcpy(zUp+10, " 23:59:59.999", 14); |
| 169 | return zUp; |
| 170 | } |
| 171 | return zDate; |
| 172 | } |
| 173 | |
| @@ -479,11 +513,11 @@ | |
| 479 | if( rid ) return rid; |
| 480 | } |
| 481 | |
| 482 | /* Date and times */ |
| 483 | if( memcmp(zTag, "date:", 5)==0 ){ |
| 484 | zDate = fossil_expand_datetime(&zTag[5],0); |
| 485 | if( zDate==0 ) zDate = &zTag[5]; |
| 486 | rid = db_int(0, |
| 487 | "SELECT objid FROM event" |
| 488 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 489 | " ORDER BY mtime DESC LIMIT 1", |
| @@ -545,21 +579,21 @@ | |
| 545 | |
| 546 | /* symbolic-name ":" date-time */ |
| 547 | nTag = strlen(zTag); |
| 548 | for(i=0; i<nTag-8 && zTag[i]!=':'; i++){} |
| 549 | if( zTag[i]==':' |
| 550 | && (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0)!=0) |
| 551 | ){ |
| 552 | char *zDate = mprintf("%s", &zTag[i+1]); |
| 553 | char *zTagBase = mprintf("%.*s", i, zTag); |
| 554 | char *zXDate; |
| 555 | int nDate = strlen(zDate); |
| 556 | if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ |
| 557 | zDate[nDate-3] = 'z'; |
| 558 | zDate[nDate-2] = 0; |
| 559 | } |
| 560 | zXDate = fossil_expand_datetime(zDate,0); |
| 561 | if( zXDate==0 ) zXDate = zDate; |
| 562 | rid = db_int(0, |
| 563 | "SELECT event.objid, max(event.mtime)" |
| 564 | " FROM tag, tagxref, event" |
| 565 | " WHERE tag.tagname='sym-%q' " |
| @@ -631,11 +665,11 @@ | |
| 631 | if( startOfBranch ) rid = start_of_branch(rid,1); |
| 632 | return rid; |
| 633 | } |
| 634 | |
| 635 | /* Pure numeric date/time */ |
| 636 | zDate = fossil_expand_datetime(zTag, 0); |
| 637 | if( zDate ){ |
| 638 | rid = db_int(0, |
| 639 | "SELECT objid FROM event" |
| 640 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 641 | " ORDER BY mtime DESC LIMIT 1", |
| 642 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -58,29 +58,42 @@ | |
| 58 | ** Check to see if the string might be a compact date/time that omits |
| 59 | ** the punctuation. Example: "20190327084549" instead of |
| 60 | ** "2019-03-27 08:45:49". If the string is of the appropriate form, |
| 61 | ** then return an alternative string (in static space) that is the same |
| 62 | ** string with punctuation inserted. |
| 63 | ** |
| 64 | ** If the bRoundUp parameter is true, then round the resulting date-time |
| 65 | ** up to the largest date/time that is consistent with the input value. |
| 66 | ** This is because the result will be used for an mtime<=julianday($DATE) |
| 67 | ** comparison. In other words: |
| 68 | ** |
| 69 | ** 20250317123421 -> 2025-03-17 12:34:21.999 |
| 70 | ** ^^^^--- Added |
| 71 | ** |
| 72 | ** 202503171234 -> 2025-03-17 12:34:59.999 |
| 73 | ** ^^^^^^^--- Added |
| 74 | ** 20250317 -> 2025-03-17 23:59:59.999 |
| 75 | ** ^^^^^^^^^^^^--- Added |
| 76 | ** |
| 77 | ** If the bVerifyNotAHash flag is true, then a check is made to see if |
| 78 | ** the input string is a hash prefix and NULL is returned if it is. If the |
| 79 | ** bVerifyNotAHash flag is false, then the result is determined by syntax |
| 80 | ** of the input string only, without reference to the artifact table. |
| 81 | */ |
| 82 | char *fossil_expand_datetime(const char *zIn,int bVerifyNotAHash,int bRoundUp){ |
| 83 | static char zEDate[24]; |
| 84 | static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' }; |
| 85 | int n = (int)strlen(zIn); |
| 86 | int i, j; |
| 87 | int addZulu = 0; |
| 88 | |
| 89 | /* These forms are allowed: |
| 90 | ** |
| 91 | ** 123456789 1234 123456789 123456789 1234 |
| 92 | ** (1) YYYYMMDD => YYYY-MM-DD 23:59:59.999 |
| 93 | ** (2) YYYYMMDDHHMM => YYYY-MM-DD HH:MM:59.999 |
| 94 | ** (3) YYYYMMDDHHMMSS => YYYY-MM-DD HH:MM:SS.999 |
| 95 | ** |
| 96 | ** An optional "Z" zulu timezone designator is allowed at the end. |
| 97 | */ |
| 98 | if( n>0 && (zIn[n-1]=='Z' || zIn[n-1]=='z') ){ |
| 99 | n--; |
| @@ -99,15 +112,23 @@ | |
| 112 | if( i>=4 && (i%2)==0 ){ |
| 113 | zEDate[j++] = aPunct[i/2]; |
| 114 | } |
| 115 | zEDate[j++] = zIn[i]; |
| 116 | } |
| 117 | if( bRoundUp ){ |
| 118 | if( j==10 ){ |
| 119 | memcpy(&zEDate[10], " 23:59:59.999", 13); |
| 120 | j += 13; |
| 121 | }else if( j==16 ){ |
| 122 | memcpy(&zEDate[16], ":59.999",7); |
| 123 | j += 7; |
| 124 | }else if( j==19 ){ |
| 125 | memcpy(&zEDate[19], ".999", 4); |
| 126 | j += 4; |
| 127 | } |
| 128 | } |
| 129 | if( addZulu ){ |
| 130 | zEDate[j++] = 'Z'; |
| 131 | } |
| 132 | zEDate[j] = 0; |
| 133 | |
| 134 | /* Check for reasonable date values. |
| @@ -147,27 +168,40 @@ | |
| 168 | ** comparison. So add in missing factional seconds or seconds or time. |
| 169 | ** |
| 170 | ** The returned string is held in a static buffer that is overwritten |
| 171 | ** with each call, or else is just a copy of its input if there are |
| 172 | ** no changes. |
| 173 | ** |
| 174 | ** For reference: |
| 175 | ** |
| 176 | ** 0123456789 123456789 1234 |
| 177 | ** YYYY-MM-DD HH:MM:SS.SSSz |
| 178 | */ |
| 179 | const char *fossil_roundup_date(const char *zDate){ |
| 180 | static char zUp[28]; |
| 181 | int n = (int)strlen(zDate); |
| 182 | int addZ = 0; |
| 183 | if( n>10 && (zDate[n-1]=='z' || zDate[n-1]=='Z') ){ |
| 184 | n--; |
| 185 | addZ = 1; |
| 186 | } |
| 187 | if( n==19 ){ /* YYYY-MM-DD HH:MM:SS */ |
| 188 | memcpy(zUp, zDate, 19); |
| 189 | memcpy(zUp+19, ".999z", 6); |
| 190 | if( !addZ ) zUp[23] = 0; |
| 191 | return zUp; |
| 192 | } |
| 193 | if( n==16 ){ /* YYYY-MM-DD HH:MM */ |
| 194 | memcpy(zUp, zDate, 16); |
| 195 | memcpy(zUp+16, ":59.999z", 8); |
| 196 | if( !addZ ) zUp[23] = 0; |
| 197 | return zUp; |
| 198 | } |
| 199 | if( n==10 ){ /* YYYY-MM-DD */ |
| 200 | memcpy(zUp, zDate, 10); |
| 201 | memcpy(zUp+10, " 23:59:59.999z", 14); |
| 202 | if( !addZ ) zUp[23] = 0; |
| 203 | return zUp; |
| 204 | } |
| 205 | return zDate; |
| 206 | } |
| 207 | |
| @@ -479,11 +513,11 @@ | |
| 513 | if( rid ) return rid; |
| 514 | } |
| 515 | |
| 516 | /* Date and times */ |
| 517 | if( memcmp(zTag, "date:", 5)==0 ){ |
| 518 | zDate = fossil_expand_datetime(&zTag[5],0,1); |
| 519 | if( zDate==0 ) zDate = &zTag[5]; |
| 520 | rid = db_int(0, |
| 521 | "SELECT objid FROM event" |
| 522 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 523 | " ORDER BY mtime DESC LIMIT 1", |
| @@ -545,21 +579,21 @@ | |
| 579 | |
| 580 | /* symbolic-name ":" date-time */ |
| 581 | nTag = strlen(zTag); |
| 582 | for(i=0; i<nTag-8 && zTag[i]!=':'; i++){} |
| 583 | if( zTag[i]==':' |
| 584 | && (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0,0)!=0) |
| 585 | ){ |
| 586 | char *zDate = mprintf("%s", &zTag[i+1]); |
| 587 | char *zTagBase = mprintf("%.*s", i, zTag); |
| 588 | char *zXDate; |
| 589 | int nDate = strlen(zDate); |
| 590 | if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ |
| 591 | zDate[nDate-3] = 'z'; |
| 592 | zDate[nDate-2] = 0; |
| 593 | } |
| 594 | zXDate = fossil_expand_datetime(zDate,0,1); |
| 595 | if( zXDate==0 ) zXDate = zDate; |
| 596 | rid = db_int(0, |
| 597 | "SELECT event.objid, max(event.mtime)" |
| 598 | " FROM tag, tagxref, event" |
| 599 | " WHERE tag.tagname='sym-%q' " |
| @@ -631,11 +665,11 @@ | |
| 665 | if( startOfBranch ) rid = start_of_branch(rid,1); |
| 666 | return rid; |
| 667 | } |
| 668 | |
| 669 | /* Pure numeric date/time */ |
| 670 | zDate = fossil_expand_datetime(zTag, 0,1); |
| 671 | if( zDate ){ |
| 672 | rid = db_int(0, |
| 673 | "SELECT objid FROM event" |
| 674 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 675 | " ORDER BY mtime DESC LIMIT 1", |
| 676 |
+5
-1
| --- src/patch.c | ||
| +++ src/patch.c | ||
| @@ -1122,14 +1122,18 @@ | ||
| 1122 | 1122 | db_close(0); |
| 1123 | 1123 | diff_tk("patch diff", 3); |
| 1124 | 1124 | return; |
| 1125 | 1125 | } |
| 1126 | 1126 | db_find_and_open_repository(0, 0); |
| 1127 | + if( gdiff_using_tk(zCmd[0]=='g') ){ | |
| 1128 | + diff_tk("patch diff", 3); | |
| 1129 | + return; | |
| 1130 | + } | |
| 1127 | 1131 | if( find_option("force","f",0) ) flags |= PATCH_FORCE; |
| 1128 | 1132 | diff_options(&DCfg, zCmd[0]=='g', 0); |
| 1129 | 1133 | verify_all_options(); |
| 1130 | - zIn = patch_find_patch_filename("apply"); | |
| 1134 | + zIn = patch_find_patch_filename("diff"); | |
| 1131 | 1135 | patch_attach(zIn, stdin, 0); |
| 1132 | 1136 | patch_diff(flags, &DCfg); |
| 1133 | 1137 | fossil_free(zIn); |
| 1134 | 1138 | }else |
| 1135 | 1139 | if( strncmp(zCmd, "pull", n)==0 ){ |
| 1136 | 1140 |
| --- src/patch.c | |
| +++ src/patch.c | |
| @@ -1122,14 +1122,18 @@ | |
| 1122 | db_close(0); |
| 1123 | diff_tk("patch diff", 3); |
| 1124 | return; |
| 1125 | } |
| 1126 | db_find_and_open_repository(0, 0); |
| 1127 | if( find_option("force","f",0) ) flags |= PATCH_FORCE; |
| 1128 | diff_options(&DCfg, zCmd[0]=='g', 0); |
| 1129 | verify_all_options(); |
| 1130 | zIn = patch_find_patch_filename("apply"); |
| 1131 | patch_attach(zIn, stdin, 0); |
| 1132 | patch_diff(flags, &DCfg); |
| 1133 | fossil_free(zIn); |
| 1134 | }else |
| 1135 | if( strncmp(zCmd, "pull", n)==0 ){ |
| 1136 |
| --- src/patch.c | |
| +++ src/patch.c | |
| @@ -1122,14 +1122,18 @@ | |
| 1122 | db_close(0); |
| 1123 | diff_tk("patch diff", 3); |
| 1124 | return; |
| 1125 | } |
| 1126 | db_find_and_open_repository(0, 0); |
| 1127 | if( gdiff_using_tk(zCmd[0]=='g') ){ |
| 1128 | diff_tk("patch diff", 3); |
| 1129 | return; |
| 1130 | } |
| 1131 | if( find_option("force","f",0) ) flags |= PATCH_FORCE; |
| 1132 | diff_options(&DCfg, zCmd[0]=='g', 0); |
| 1133 | verify_all_options(); |
| 1134 | zIn = patch_find_patch_filename("diff"); |
| 1135 | patch_attach(zIn, stdin, 0); |
| 1136 | patch_diff(flags, &DCfg); |
| 1137 | fossil_free(zIn); |
| 1138 | }else |
| 1139 | if( strncmp(zCmd, "pull", n)==0 ){ |
| 1140 |
+25
| --- src/printf.c | ||
| +++ src/printf.c | ||
| @@ -239,10 +239,35 @@ | ||
| 239 | 239 | int n = 0; |
| 240 | 240 | while( (N-- != 0) && *(z++)!=0 ){ n++; } |
| 241 | 241 | return n; |
| 242 | 242 | } |
| 243 | 243 | #endif |
| 244 | + | |
| 245 | +/* | |
| 246 | +** SETTING: timeline-plaintext boolean default=off | |
| 247 | +** | |
| 248 | +** If enabled, no wiki-formatting is done for timeline comment messages. | |
| 249 | +** Hyperlinks are activated, but they show up on screen using the | |
| 250 | +** complete input text, not just the display text. No other formatting | |
| 251 | +** is done. | |
| 252 | +*/ | |
| 253 | +/* | |
| 254 | +** SETTING: timeline-block-markup boolean default=off | |
| 255 | +** | |
| 256 | +** If enabled, block markup (paragraph brakes, tables, lists, headings, etc) | |
| 257 | +** is enabled while rendering check-in comment message on the timeline. | |
| 258 | +** This is disabled by default, because the timeline works best if the | |
| 259 | +** check-in comments are short and do not take up too much vertical space. | |
| 260 | +*/ | |
| 261 | +/* | |
| 262 | +** SETTING: timeline-hard-newlines boolean default=off | |
| 263 | +** | |
| 264 | +** If enabled, the timeline honors newline characters in check-in comments. | |
| 265 | +** In other words, newlines are coverted into <br> for HTML display. | |
| 266 | +** The default behavior, when this setting is off, is that newlines are | |
| 267 | +** treated like any other whitespace character. | |
| 268 | +*/ | |
| 244 | 269 | |
| 245 | 270 | /* |
| 246 | 271 | ** Return an appropriate set of flags for wiki_convert() for displaying |
| 247 | 272 | ** comments on a timeline. These flag settings are determined by |
| 248 | 273 | ** configuration parameters. |
| 249 | 274 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -239,10 +239,35 @@ | |
| 239 | int n = 0; |
| 240 | while( (N-- != 0) && *(z++)!=0 ){ n++; } |
| 241 | return n; |
| 242 | } |
| 243 | #endif |
| 244 | |
| 245 | /* |
| 246 | ** Return an appropriate set of flags for wiki_convert() for displaying |
| 247 | ** comments on a timeline. These flag settings are determined by |
| 248 | ** configuration parameters. |
| 249 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -239,10 +239,35 @@ | |
| 239 | int n = 0; |
| 240 | while( (N-- != 0) && *(z++)!=0 ){ n++; } |
| 241 | return n; |
| 242 | } |
| 243 | #endif |
| 244 | |
| 245 | /* |
| 246 | ** SETTING: timeline-plaintext boolean default=off |
| 247 | ** |
| 248 | ** If enabled, no wiki-formatting is done for timeline comment messages. |
| 249 | ** Hyperlinks are activated, but they show up on screen using the |
| 250 | ** complete input text, not just the display text. No other formatting |
| 251 | ** is done. |
| 252 | */ |
| 253 | /* |
| 254 | ** SETTING: timeline-block-markup boolean default=off |
| 255 | ** |
| 256 | ** If enabled, block markup (paragraph brakes, tables, lists, headings, etc) |
| 257 | ** is enabled while rendering check-in comment message on the timeline. |
| 258 | ** This is disabled by default, because the timeline works best if the |
| 259 | ** check-in comments are short and do not take up too much vertical space. |
| 260 | */ |
| 261 | /* |
| 262 | ** SETTING: timeline-hard-newlines boolean default=off |
| 263 | ** |
| 264 | ** If enabled, the timeline honors newline characters in check-in comments. |
| 265 | ** In other words, newlines are coverted into <br> for HTML display. |
| 266 | ** The default behavior, when this setting is off, is that newlines are |
| 267 | ** treated like any other whitespace character. |
| 268 | */ |
| 269 | |
| 270 | /* |
| 271 | ** Return an appropriate set of flags for wiki_convert() for displaying |
| 272 | ** comments on a timeline. These flag settings are determined by |
| 273 | ** configuration parameters. |
| 274 |
+9
-9
| --- src/search.c | ||
| +++ src/search.c | ||
| @@ -1497,18 +1497,18 @@ | ||
| 1497 | 1497 | ** Search for check-in comments, documents, tickets, or wiki that |
| 1498 | 1498 | ** match a user-supplied pattern. |
| 1499 | 1499 | ** |
| 1500 | 1500 | ** s=PATTERN Specify the full-text pattern to search for |
| 1501 | 1501 | ** y=TYPE What to search. |
| 1502 | -** c -> check-ins | |
| 1503 | -** d -> documentation | |
| 1504 | -** t -> tickets | |
| 1505 | -** w -> wiki | |
| 1506 | -** e -> tech notes | |
| 1507 | -** f -> forum | |
| 1508 | -** h -> built-in help | |
| 1509 | -** all -> everything | |
| 1502 | +** c -> check-ins, | |
| 1503 | +** d -> documentation, | |
| 1504 | +** t -> tickets, | |
| 1505 | +** w -> wiki, | |
| 1506 | +** e -> tech notes, | |
| 1507 | +** f -> forum, | |
| 1508 | +** h -> built-in help, | |
| 1509 | +** all -> everything. | |
| 1510 | 1510 | */ |
| 1511 | 1511 | void search_page(void){ |
| 1512 | 1512 | const int isSearch = P("s")!=0; |
| 1513 | 1513 | login_check_credentials(); |
| 1514 | 1514 | style_header("Search%s", isSearch ? " Results" : ""); |
| @@ -2291,11 +2291,11 @@ | ||
| 2291 | 2291 | } |
| 2292 | 2292 | fossil_print(" done\n"); |
| 2293 | 2293 | } |
| 2294 | 2294 | |
| 2295 | 2295 | /* |
| 2296 | -** COMMAND: fts-config* abbreviated-subcommands | |
| 2296 | +** COMMAND: fts-config* abbrv-subcom | |
| 2297 | 2297 | ** |
| 2298 | 2298 | ** Usage: fossil fts-config ?SUBCOMMAND? ?ARGUMENT? |
| 2299 | 2299 | ** |
| 2300 | 2300 | ** The "fossil fts-config" command configures the full-text search capabilities |
| 2301 | 2301 | ** of the repository. Subcommands: |
| 2302 | 2302 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -1497,18 +1497,18 @@ | |
| 1497 | ** Search for check-in comments, documents, tickets, or wiki that |
| 1498 | ** match a user-supplied pattern. |
| 1499 | ** |
| 1500 | ** s=PATTERN Specify the full-text pattern to search for |
| 1501 | ** y=TYPE What to search. |
| 1502 | ** c -> check-ins |
| 1503 | ** d -> documentation |
| 1504 | ** t -> tickets |
| 1505 | ** w -> wiki |
| 1506 | ** e -> tech notes |
| 1507 | ** f -> forum |
| 1508 | ** h -> built-in help |
| 1509 | ** all -> everything |
| 1510 | */ |
| 1511 | void search_page(void){ |
| 1512 | const int isSearch = P("s")!=0; |
| 1513 | login_check_credentials(); |
| 1514 | style_header("Search%s", isSearch ? " Results" : ""); |
| @@ -2291,11 +2291,11 @@ | |
| 2291 | } |
| 2292 | fossil_print(" done\n"); |
| 2293 | } |
| 2294 | |
| 2295 | /* |
| 2296 | ** COMMAND: fts-config* abbreviated-subcommands |
| 2297 | ** |
| 2298 | ** Usage: fossil fts-config ?SUBCOMMAND? ?ARGUMENT? |
| 2299 | ** |
| 2300 | ** The "fossil fts-config" command configures the full-text search capabilities |
| 2301 | ** of the repository. Subcommands: |
| 2302 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -1497,18 +1497,18 @@ | |
| 1497 | ** Search for check-in comments, documents, tickets, or wiki that |
| 1498 | ** match a user-supplied pattern. |
| 1499 | ** |
| 1500 | ** s=PATTERN Specify the full-text pattern to search for |
| 1501 | ** y=TYPE What to search. |
| 1502 | ** c -> check-ins, |
| 1503 | ** d -> documentation, |
| 1504 | ** t -> tickets, |
| 1505 | ** w -> wiki, |
| 1506 | ** e -> tech notes, |
| 1507 | ** f -> forum, |
| 1508 | ** h -> built-in help, |
| 1509 | ** all -> everything. |
| 1510 | */ |
| 1511 | void search_page(void){ |
| 1512 | const int isSearch = P("s")!=0; |
| 1513 | login_check_credentials(); |
| 1514 | style_header("Search%s", isSearch ? " Results" : ""); |
| @@ -2291,11 +2291,11 @@ | |
| 2291 | } |
| 2292 | fossil_print(" done\n"); |
| 2293 | } |
| 2294 | |
| 2295 | /* |
| 2296 | ** COMMAND: fts-config* abbrv-subcom |
| 2297 | ** |
| 2298 | ** Usage: fossil fts-config ?SUBCOMMAND? ?ARGUMENT? |
| 2299 | ** |
| 2300 | ** The "fossil fts-config" command configures the full-text search capabilities |
| 2301 | ** of the repository. Subcommands: |
| 2302 |
+126
-237
| --- src/security_audit.c | ||
| +++ src/security_audit.c | ||
| @@ -553,17 +553,17 @@ | ||
| 553 | 553 | @ checkbox on the <a href="setup_access">Access Control</a> page. |
| 554 | 554 | } |
| 555 | 555 | |
| 556 | 556 | /* Logging should be turned on |
| 557 | 557 | */ |
| 558 | - if( db_get_boolean("access-log",0)==0 ){ | |
| 558 | + if( db_get_boolean("access-log",1)==0 ){ | |
| 559 | 559 | @ <li><p> |
| 560 | 560 | @ The <a href="access_log">User Log</a> is disabled. The user log |
| 561 | 561 | @ keeps a record of successful and unsuccessful login attempts and is |
| 562 | 562 | @ useful for security monitoring. |
| 563 | 563 | } |
| 564 | - if( db_get_boolean("admin-log",0)==0 ){ | |
| 564 | + if( db_get_boolean("admin-log",1)==0 ){ | |
| 565 | 565 | @ <li><p> |
| 566 | 566 | @ The <a href="admin_log">Administrative Log</a> is disabled. |
| 567 | 567 | @ The administrative log provides a record of configuration changes |
| 568 | 568 | @ and is useful for security monitoring. |
| 569 | 569 | } |
| @@ -804,37 +804,59 @@ | ||
| 804 | 804 | @ </pre></blockquote> |
| 805 | 805 | blob_reset(&fullname); |
| 806 | 806 | } |
| 807 | 807 | } |
| 808 | 808 | |
| 809 | -/* | |
| 810 | -** The maximum number of bytes of the error log to show by default. | |
| 811 | -*/ | |
| 812 | -#define MXSHOWLOG 500000 | |
| 813 | - | |
| 814 | 809 | /* |
| 815 | 810 | ** WEBPAGE: errorlog |
| 816 | 811 | ** |
| 817 | 812 | ** Show the content of the error log. Only the administrator can view |
| 818 | 813 | ** this page. |
| 814 | +** | |
| 815 | +** y=0x01 Show only hack attempts | |
| 816 | +** y=0x02 Show only panics and assertion faults | |
| 817 | +** y=0x04 Show hung backoffice processes | |
| 818 | +** y=0x08 Show POST requests from a different origin | |
| 819 | +** y=0x40 Show other uncategorized messages | |
| 820 | +** | |
| 821 | +** If y is omitted or is zero, a count of the various message types is | |
| 822 | +** shown. | |
| 819 | 823 | */ |
| 820 | 824 | void errorlog_page(void){ |
| 821 | 825 | i64 szFile; |
| 822 | 826 | FILE *in; |
| 823 | 827 | char *zLog; |
| 828 | + const char *zType = P("y"); | |
| 829 | + static const int eAllTypes = 0x4f; | |
| 830 | + long eType = 0; | |
| 831 | + int bOutput = 0; | |
| 832 | + int prevWasTime = 0; | |
| 833 | + int nHack = 0; | |
| 834 | + int nPanic = 0; | |
| 835 | + int nOther = 0; | |
| 836 | + int nHang = 0; | |
| 837 | + int nXPost = 0; | |
| 824 | 838 | char z[10000]; |
| 839 | + char zTime[10000]; | |
| 840 | + | |
| 825 | 841 | login_check_credentials(); |
| 826 | 842 | if( !g.perm.Admin ){ |
| 827 | 843 | login_needed(0); |
| 828 | 844 | return; |
| 829 | 845 | } |
| 846 | + if( zType ){ | |
| 847 | + eType = strtol(zType,0,0) & eAllTypes; | |
| 848 | + } | |
| 830 | 849 | style_header("Server Error Log"); |
| 831 | 850 | style_submenu_element("Test", "%R/test-warning"); |
| 832 | 851 | style_submenu_element("Refresh", "%R/errorlog"); |
| 852 | + style_submenu_element("Download", "%R/errorlog?download"); | |
| 853 | + style_submenu_element("Truncate", "%R/errorlog?truncate"); | |
| 833 | 854 | style_submenu_element("Log-Menu", "%R/setup-logmenu"); |
| 834 | - style_submenu_element("Panics", "%R/paniclog"); | |
| 835 | - style_submenu_element("Non-Hacks", "%R/hacklog?not"); | |
| 855 | + if( eType ){ | |
| 856 | + style_submenu_element("Summary", "%R/errorlog"); | |
| 857 | + } | |
| 836 | 858 | |
| 837 | 859 | if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ |
| 838 | 860 | no_error_log_available(); |
| 839 | 861 | style_finish_page(); |
| 840 | 862 | return; |
| @@ -861,250 +883,117 @@ | ||
| 861 | 883 | return; |
| 862 | 884 | } |
| 863 | 885 | zLog = file_canonical_name_dup(g.zErrlog); |
| 864 | 886 | @ <p>The server error log at "%h(zLog)" is %,lld(szFile) bytes in size. |
| 865 | 887 | fossil_free(zLog); |
| 866 | - style_submenu_element("Download", "%R/errorlog?download"); | |
| 867 | - style_submenu_element("Truncate", "%R/errorlog?truncate"); | |
| 868 | 888 | in = fossil_fopen(g.zErrlog, "rb"); |
| 869 | 889 | if( in==0 ){ |
| 870 | 890 | @ <p class='generalError'>Unable to open that file for reading!</p> |
| 871 | 891 | style_finish_page(); |
| 872 | 892 | return; |
| 873 | 893 | } |
| 874 | - if( szFile>MXSHOWLOG && P("all")==0 ){ | |
| 875 | - @ <form action="%R/errorlog" method="POST"> | |
| 876 | - @ <p>Only the last %,d(MXSHOWLOG) bytes are shown. | |
| 877 | - @ <input type="submit" name="all" value="Show All"> | |
| 878 | - @ </form> | |
| 879 | - fseek(in, -MXSHOWLOG, SEEK_END); | |
| 880 | - } | |
| 881 | - @ <hr> | |
| 882 | - @ <pre> | |
| 883 | - while( fgets(z, sizeof(z), in) ){ | |
| 884 | - @ %h(z)\ | |
| 885 | - } | |
| 886 | - fclose(in); | |
| 887 | - @ </pre> | |
| 888 | - style_finish_page(); | |
| 889 | -} | |
| 890 | - | |
| 891 | -/* | |
| 892 | -** WEBPAGE: paniclog | |
| 893 | -** | |
| 894 | -** Scan the error log for panics. Show all panic messages, ignoring all | |
| 895 | -** other error log entries. | |
| 896 | -*/ | |
| 897 | -void paniclog_page(void){ | |
| 898 | - i64 szFile; | |
| 899 | - char *zLog; | |
| 900 | - FILE *in; | |
| 901 | - int bOutput = 0; | |
| 902 | - int prevWasTime = 0; | |
| 903 | - char z[10000]; | |
| 904 | - char zTime[10000]; | |
| 905 | - | |
| 906 | - login_check_credentials(); | |
| 907 | - if( !g.perm.Admin ){ | |
| 908 | - login_needed(0); | |
| 909 | - return; | |
| 910 | - } | |
| 911 | - style_header("Server Panic Log"); | |
| 912 | - style_submenu_element("Log-Menu", "%R/setup-logmenu"); | |
| 913 | - | |
| 914 | - if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ | |
| 915 | - no_error_log_available(); | |
| 916 | - style_finish_page(); | |
| 917 | - return; | |
| 918 | - } | |
| 919 | - in = fossil_fopen(g.zErrlog, "rb"); | |
| 920 | - if( in==0 ){ | |
| 921 | - @ <p class='generalError'>Unable to open that file for reading!</p> | |
| 922 | - style_finish_page(); | |
| 923 | - return; | |
| 924 | - } | |
| 925 | - szFile = file_size(g.zErrlog, ExtFILE); | |
| 926 | - zLog = file_canonical_name_dup(g.zErrlog); | |
| 927 | - @ Panic messages contained within the %lld(szFile)-byte | |
| 928 | - @ <a href="%R/errorlog?all">error log</a> found at | |
| 929 | - @ "%h(zLog)". | |
| 930 | - fossil_free(zLog); | |
| 931 | - @ <hr> | |
| 932 | - @ <pre> | |
| 933 | - while( fgets(z, sizeof(z), in) ){ | |
| 934 | - if( prevWasTime | |
| 935 | - && (strncmp(z,"panic: ", 7)==0 || strstr(z," assertion fault ")!=0) | |
| 936 | - ){ | |
| 937 | - @ %h(zTime)\ | |
| 938 | - bOutput = 1; | |
| 894 | + if( eType==0 ){ | |
| 895 | + /* will do a summary */ | |
| 896 | + }else if( (eType&eAllTypes)!=eAllTypes ){ | |
| 897 | + @ Only the following types of messages displayed: | |
| 898 | + @ <ul> | |
| 899 | + if( eType & 0x01 ){ | |
| 900 | + @ <li>Hack attempts | |
| 901 | + } | |
| 902 | + if( eType & 0x02 ){ | |
| 903 | + @ <li>Panics and assertion faults | |
| 904 | + } | |
| 905 | + if( eType & 0x04 ){ | |
| 906 | + @ <li>Hung backoffice processes | |
| 907 | + } | |
| 908 | + if( eType & 0x08 ){ | |
| 909 | + @ <li>POST requests from different origin | |
| 910 | + } | |
| 911 | + if( eType & 0x40 ){ | |
| 912 | + @ <li>Other uncategorized messages | |
| 913 | + } | |
| 914 | + @ </ul> | |
| 915 | + } | |
| 916 | + @ <hr> | |
| 917 | + if( eType ){ | |
| 918 | + @ <pre> | |
| 919 | + } | |
| 920 | + while( fgets(z, sizeof(z), in) ){ | |
| 921 | + if( prevWasTime ){ | |
| 922 | + if( strncmp(z,"possible hack attempt - 418 ", 27)==0 ){ | |
| 923 | + bOutput = (eType & 0x01)!=0; | |
| 924 | + nHack++; | |
| 925 | + }else | |
| 926 | + if( (strncmp(z,"panic: ", 7)==0 || strstr(z," assertion fault ")!=0) ){ | |
| 927 | + bOutput = (eType & 0x02)!=0; | |
| 928 | + nPanic++; | |
| 929 | + }else | |
| 930 | + if( sqlite3_strglob("warning: backoffice process * still *",z)==0 ){ | |
| 931 | + bOutput = (eType & 0x04)!=0; | |
| 932 | + nHang++; | |
| 933 | + }else | |
| 934 | + if( sqlite3_strglob("warning: POST from different origin*",z)==0 ){ | |
| 935 | + bOutput = (eType & 0x08)!=0; | |
| 936 | + nXPost++; | |
| 937 | + }else | |
| 938 | + { | |
| 939 | + bOutput = (eType & 0x40)!=0; | |
| 940 | + nOther++; | |
| 941 | + } | |
| 942 | + if( bOutput ){ | |
| 943 | + @ %h(zTime)\ | |
| 944 | + } | |
| 939 | 945 | } |
| 940 | 946 | if( strncmp(z, "--------", 8)==0 ){ |
| 941 | 947 | size_t n = strlen(z); |
| 942 | 948 | memcpy(zTime, z, n+1); |
| 943 | 949 | prevWasTime = 1; |
| 944 | 950 | bOutput = 0; |
| 945 | 951 | }else{ |
| 946 | 952 | prevWasTime = 0; |
| 947 | 953 | } |
| 948 | - if( bOutput ){ | |
| 949 | - @ %h(z)\ | |
| 950 | - } | |
| 951 | - } | |
| 952 | - fclose(in); | |
| 953 | - @ </pre> | |
| 954 | - style_finish_page(); | |
| 955 | -} | |
| 956 | - | |
| 957 | -/* | |
| 958 | -** WEBPAGE: hacklog | |
| 959 | -** | |
| 960 | -** Scan the error log for "possible hack attempt" entries Show hack | |
| 961 | -** attempt messages only, omitting all others. Or if the "not" query | |
| 962 | -** parameter is present, show only messages that are not hack attempts. | |
| 963 | -*/ | |
| 964 | -void hacklog_page(void){ | |
| 965 | - i64 szFile; | |
| 966 | - char *zLog; | |
| 967 | - FILE *in; | |
| 968 | - int bOutput = 0; | |
| 969 | - int prevWasTime = 0; | |
| 970 | - int isNot = P("not")!=0; | |
| 971 | - char z[10000]; | |
| 972 | - char zTime[10000]; | |
| 973 | - | |
| 974 | - login_check_credentials(); | |
| 975 | - if( !g.perm.Admin ){ | |
| 976 | - login_needed(0); | |
| 977 | - return; | |
| 978 | - } | |
| 979 | - style_header("Server Hack Log"); | |
| 980 | - style_submenu_element("Log-Menu", "%R/setup-logmenu"); | |
| 981 | - | |
| 982 | - if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ | |
| 983 | - no_error_log_available(); | |
| 984 | - style_finish_page(); | |
| 985 | - return; | |
| 986 | - } | |
| 987 | - in = fossil_fopen(g.zErrlog, "rb"); | |
| 988 | - if( in==0 ){ | |
| 989 | - @ <p class='generalError'>Unable to open that file for reading!</p> | |
| 990 | - style_finish_page(); | |
| 991 | - return; | |
| 992 | - } | |
| 993 | - szFile = file_size(g.zErrlog, ExtFILE); | |
| 994 | - zLog = file_canonical_name_dup(g.zErrlog); | |
| 995 | - @ %s(isNot?"Non-hack":"Hack") messages contained within the %lld(szFile)-byte | |
| 996 | - @ <a href="%R/errorlog?all">error log</a> found at | |
| 997 | - @ "%h(zLog)". | |
| 998 | - fossil_free(zLog); | |
| 999 | - @ <hr> | |
| 1000 | - @ <pre> | |
| 1001 | - while( fgets(z, sizeof(z), in) ){ | |
| 1002 | - if( prevWasTime | |
| 1003 | - && ((strncmp(z,"possible hack attempt - 418 ", 27)==0) ^ isNot) | |
| 1004 | - ){ | |
| 1005 | - @ %h(zTime)\ | |
| 1006 | - bOutput = 1; | |
| 1007 | - } | |
| 1008 | - if( strncmp(z, "--------", 8)==0 ){ | |
| 1009 | - size_t n = strlen(z); | |
| 1010 | - memcpy(zTime, z, n+1); | |
| 1011 | - prevWasTime = 1; | |
| 1012 | - bOutput = 0; | |
| 1013 | - }else{ | |
| 1014 | - prevWasTime = 0; | |
| 1015 | - } | |
| 1016 | - if( bOutput ){ | |
| 1017 | - @ %h(z)\ | |
| 1018 | - } | |
| 1019 | - } | |
| 1020 | - fclose(in); | |
| 1021 | - @ </pre> | |
| 1022 | - style_finish_page(); | |
| 1023 | -} | |
| 1024 | - | |
| 1025 | -/* | |
| 1026 | -** WEBPAGE: logsummary | |
| 1027 | -** | |
| 1028 | -** Scan the error log and count the various kinds of entries. | |
| 1029 | -*/ | |
| 1030 | -void logsummary_page(void){ | |
| 1031 | - i64 szFile; | |
| 1032 | - char *zLog; | |
| 1033 | - FILE *in; | |
| 1034 | - int prevWasTime = 0; | |
| 1035 | - int nHack = 0; | |
| 1036 | - int nPanic = 0; | |
| 1037 | - int nOther = 0; | |
| 1038 | - int nTotal = 0; | |
| 1039 | - char z[10000]; | |
| 1040 | - | |
| 1041 | - login_check_credentials(); | |
| 1042 | - if( !g.perm.Admin ){ | |
| 1043 | - login_needed(0); | |
| 1044 | - return; | |
| 1045 | - } | |
| 1046 | - style_header("Server Hack Log"); | |
| 1047 | - style_submenu_element("Log-Menu", "%R/setup-logmenu"); | |
| 1048 | - | |
| 1049 | - if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ | |
| 1050 | - no_error_log_available(); | |
| 1051 | - style_finish_page(); | |
| 1052 | - return; | |
| 1053 | - } | |
| 1054 | - in = fossil_fopen(g.zErrlog, "rb"); | |
| 1055 | - if( in==0 ){ | |
| 1056 | - @ <p class='generalError'>Unable to open that file for reading!</p> | |
| 1057 | - style_finish_page(); | |
| 1058 | - return; | |
| 1059 | - } | |
| 1060 | - szFile = file_size(g.zErrlog, ExtFILE); | |
| 1061 | - zLog = file_canonical_name_dup(g.zErrlog); | |
| 1062 | - @ Summary of messages contained within the %lld(szFile)-byte | |
| 1063 | - @ <a href="%R/errorlog?all">error log</a> found at | |
| 1064 | - @ "%h(zLog)". | |
| 1065 | - fossil_free(zLog); | |
| 1066 | - @ <hr> | |
| 1067 | - while( fgets(z, sizeof(z), in) ){ | |
| 1068 | - if( prevWasTime | |
| 1069 | - && (strncmp(z,"possible hack attempt - 418 ", 27)==0) | |
| 1070 | - ){ | |
| 1071 | - nHack++; | |
| 1072 | - prevWasTime = 0; | |
| 1073 | - continue; | |
| 1074 | - } | |
| 1075 | - if( prevWasTime | |
| 1076 | - && (strncmp(z,"panic: ", 7)==0 || strstr(z," assertion fault ")!=0) | |
| 1077 | - ){ | |
| 1078 | - nPanic++; | |
| 1079 | - prevWasTime = 0; | |
| 1080 | - continue; | |
| 1081 | - } | |
| 1082 | - if( prevWasTime ) nOther++; | |
| 1083 | - if( strncmp(z, "--------", 8)==0 ){ | |
| 1084 | - nTotal++; | |
| 1085 | - prevWasTime = 1; | |
| 1086 | - continue; | |
| 1087 | - } | |
| 1088 | - prevWasTime = 0; | |
| 1089 | - } | |
| 1090 | - fclose(in); | |
| 1091 | - @ <p><table border="a" cellspacing="0" cellpadding="5"> | |
| 1092 | - @ <tr><td align="right">%d(nPanic)</td> | |
| 1093 | - if( nPanic>0 ){ | |
| 1094 | - @ <td><a href="./paniclog">Panics</a></td> | |
| 1095 | - } else { | |
| 1096 | - @ <td>Panics</td> | |
| 1097 | - } | |
| 1098 | - @ <tr><td align="right">%d(nHack)</td> | |
| 1099 | - if( nHack>0 ){ | |
| 1100 | - @ <td><a href="./hacklog">Hack Attempts</a></td> | |
| 1101 | - }else{ | |
| 1102 | - @ <td>Hack Attempts</td> | |
| 1103 | - } | |
| 1104 | - @ <tr><td align="right">%d(nOther)</td> | |
| 1105 | - @ <td>Other</td> | |
| 1106 | - @ <tr><td align="right">%d(nTotal)</td> | |
| 1107 | - @ <td>Total Messages</td> | |
| 1108 | - @ </table> | |
| 954 | + if( bOutput && eType ){ | |
| 955 | + @ %h(z)\ | |
| 956 | + } | |
| 957 | + } | |
| 958 | + fclose(in); | |
| 959 | + if( eType ){ | |
| 960 | + @ </pre> | |
| 961 | + } | |
| 962 | + if( eType==0 ){ | |
| 963 | + int nNonHack = nPanic + nHang + nOther; | |
| 964 | + int nTotal = nNonHack + nHack + nXPost; | |
| 965 | + @ <p><table border="a" cellspacing="0" cellpadding="5"> | |
| 966 | + if( nPanic>0 ){ | |
| 967 | + @ <tr><td align="right">%d(nPanic)</td> | |
| 968 | + @ <td><a href="./errorlog?y=2">Panics</a></td> | |
| 969 | + } | |
| 970 | + if( nHack>0 ){ | |
| 971 | + @ <tr><td align="right">%d(nHack)</td> | |
| 972 | + @ <td><a href="./errorlog?y=1">Hack Attempts</a></td> | |
| 973 | + } | |
| 974 | + if( nHang>0 ){ | |
| 975 | + @ <tr><td align="right">%d(nHang)</td> | |
| 976 | + @ <td><a href="./errorlog?y=4/">Hung Backoffice</a></td> | |
| 977 | + } | |
| 978 | + if( nXPost>0 ){ | |
| 979 | + @ <tr><td align="right">%d(nXPost)</td> | |
| 980 | + @ <td><a href="./errorlog?y=8/">POSTs from different origin</a></td> | |
| 981 | + } | |
| 982 | + if( nOther>0 ){ | |
| 983 | + @ <tr><td align="right">%d(nOther)</td> | |
| 984 | + @ <td><a href="./errorlog?y=64/">Other</a></td> | |
| 985 | + } | |
| 986 | + if( nHack+nXPost>0 && nNonHack>0 ){ | |
| 987 | + @ <tr><td align="right">%d(nNonHack)</td> | |
| 988 | + @ <td><a href="%R/errorlog?y=70">Other than hack attempts</a></td> | |
| 989 | + } | |
| 990 | + @ <tr><td align="right">%d(nTotal)</td> | |
| 991 | + if( nTotal>0 ){ | |
| 992 | + @ <td><a href="./errorlog?y=255">All Messages</a></td> | |
| 993 | + }else{ | |
| 994 | + @ <td>All Messages</td> | |
| 995 | + } | |
| 996 | + @ </table> | |
| 997 | + } | |
| 1109 | 998 | style_finish_page(); |
| 1110 | 999 | } |
| 1111 | 1000 |
| --- src/security_audit.c | |
| +++ src/security_audit.c | |
| @@ -553,17 +553,17 @@ | |
| 553 | @ checkbox on the <a href="setup_access">Access Control</a> page. |
| 554 | } |
| 555 | |
| 556 | /* Logging should be turned on |
| 557 | */ |
| 558 | if( db_get_boolean("access-log",0)==0 ){ |
| 559 | @ <li><p> |
| 560 | @ The <a href="access_log">User Log</a> is disabled. The user log |
| 561 | @ keeps a record of successful and unsuccessful login attempts and is |
| 562 | @ useful for security monitoring. |
| 563 | } |
| 564 | if( db_get_boolean("admin-log",0)==0 ){ |
| 565 | @ <li><p> |
| 566 | @ The <a href="admin_log">Administrative Log</a> is disabled. |
| 567 | @ The administrative log provides a record of configuration changes |
| 568 | @ and is useful for security monitoring. |
| 569 | } |
| @@ -804,37 +804,59 @@ | |
| 804 | @ </pre></blockquote> |
| 805 | blob_reset(&fullname); |
| 806 | } |
| 807 | } |
| 808 | |
| 809 | /* |
| 810 | ** The maximum number of bytes of the error log to show by default. |
| 811 | */ |
| 812 | #define MXSHOWLOG 500000 |
| 813 | |
| 814 | /* |
| 815 | ** WEBPAGE: errorlog |
| 816 | ** |
| 817 | ** Show the content of the error log. Only the administrator can view |
| 818 | ** this page. |
| 819 | */ |
| 820 | void errorlog_page(void){ |
| 821 | i64 szFile; |
| 822 | FILE *in; |
| 823 | char *zLog; |
| 824 | char z[10000]; |
| 825 | login_check_credentials(); |
| 826 | if( !g.perm.Admin ){ |
| 827 | login_needed(0); |
| 828 | return; |
| 829 | } |
| 830 | style_header("Server Error Log"); |
| 831 | style_submenu_element("Test", "%R/test-warning"); |
| 832 | style_submenu_element("Refresh", "%R/errorlog"); |
| 833 | style_submenu_element("Log-Menu", "%R/setup-logmenu"); |
| 834 | style_submenu_element("Panics", "%R/paniclog"); |
| 835 | style_submenu_element("Non-Hacks", "%R/hacklog?not"); |
| 836 | |
| 837 | if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ |
| 838 | no_error_log_available(); |
| 839 | style_finish_page(); |
| 840 | return; |
| @@ -861,250 +883,117 @@ | |
| 861 | return; |
| 862 | } |
| 863 | zLog = file_canonical_name_dup(g.zErrlog); |
| 864 | @ <p>The server error log at "%h(zLog)" is %,lld(szFile) bytes in size. |
| 865 | fossil_free(zLog); |
| 866 | style_submenu_element("Download", "%R/errorlog?download"); |
| 867 | style_submenu_element("Truncate", "%R/errorlog?truncate"); |
| 868 | in = fossil_fopen(g.zErrlog, "rb"); |
| 869 | if( in==0 ){ |
| 870 | @ <p class='generalError'>Unable to open that file for reading!</p> |
| 871 | style_finish_page(); |
| 872 | return; |
| 873 | } |
| 874 | if( szFile>MXSHOWLOG && P("all")==0 ){ |
| 875 | @ <form action="%R/errorlog" method="POST"> |
| 876 | @ <p>Only the last %,d(MXSHOWLOG) bytes are shown. |
| 877 | @ <input type="submit" name="all" value="Show All"> |
| 878 | @ </form> |
| 879 | fseek(in, -MXSHOWLOG, SEEK_END); |
| 880 | } |
| 881 | @ <hr> |
| 882 | @ <pre> |
| 883 | while( fgets(z, sizeof(z), in) ){ |
| 884 | @ %h(z)\ |
| 885 | } |
| 886 | fclose(in); |
| 887 | @ </pre> |
| 888 | style_finish_page(); |
| 889 | } |
| 890 | |
| 891 | /* |
| 892 | ** WEBPAGE: paniclog |
| 893 | ** |
| 894 | ** Scan the error log for panics. Show all panic messages, ignoring all |
| 895 | ** other error log entries. |
| 896 | */ |
| 897 | void paniclog_page(void){ |
| 898 | i64 szFile; |
| 899 | char *zLog; |
| 900 | FILE *in; |
| 901 | int bOutput = 0; |
| 902 | int prevWasTime = 0; |
| 903 | char z[10000]; |
| 904 | char zTime[10000]; |
| 905 | |
| 906 | login_check_credentials(); |
| 907 | if( !g.perm.Admin ){ |
| 908 | login_needed(0); |
| 909 | return; |
| 910 | } |
| 911 | style_header("Server Panic Log"); |
| 912 | style_submenu_element("Log-Menu", "%R/setup-logmenu"); |
| 913 | |
| 914 | if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ |
| 915 | no_error_log_available(); |
| 916 | style_finish_page(); |
| 917 | return; |
| 918 | } |
| 919 | in = fossil_fopen(g.zErrlog, "rb"); |
| 920 | if( in==0 ){ |
| 921 | @ <p class='generalError'>Unable to open that file for reading!</p> |
| 922 | style_finish_page(); |
| 923 | return; |
| 924 | } |
| 925 | szFile = file_size(g.zErrlog, ExtFILE); |
| 926 | zLog = file_canonical_name_dup(g.zErrlog); |
| 927 | @ Panic messages contained within the %lld(szFile)-byte |
| 928 | @ <a href="%R/errorlog?all">error log</a> found at |
| 929 | @ "%h(zLog)". |
| 930 | fossil_free(zLog); |
| 931 | @ <hr> |
| 932 | @ <pre> |
| 933 | while( fgets(z, sizeof(z), in) ){ |
| 934 | if( prevWasTime |
| 935 | && (strncmp(z,"panic: ", 7)==0 || strstr(z," assertion fault ")!=0) |
| 936 | ){ |
| 937 | @ %h(zTime)\ |
| 938 | bOutput = 1; |
| 939 | } |
| 940 | if( strncmp(z, "--------", 8)==0 ){ |
| 941 | size_t n = strlen(z); |
| 942 | memcpy(zTime, z, n+1); |
| 943 | prevWasTime = 1; |
| 944 | bOutput = 0; |
| 945 | }else{ |
| 946 | prevWasTime = 0; |
| 947 | } |
| 948 | if( bOutput ){ |
| 949 | @ %h(z)\ |
| 950 | } |
| 951 | } |
| 952 | fclose(in); |
| 953 | @ </pre> |
| 954 | style_finish_page(); |
| 955 | } |
| 956 | |
| 957 | /* |
| 958 | ** WEBPAGE: hacklog |
| 959 | ** |
| 960 | ** Scan the error log for "possible hack attempt" entries Show hack |
| 961 | ** attempt messages only, omitting all others. Or if the "not" query |
| 962 | ** parameter is present, show only messages that are not hack attempts. |
| 963 | */ |
| 964 | void hacklog_page(void){ |
| 965 | i64 szFile; |
| 966 | char *zLog; |
| 967 | FILE *in; |
| 968 | int bOutput = 0; |
| 969 | int prevWasTime = 0; |
| 970 | int isNot = P("not")!=0; |
| 971 | char z[10000]; |
| 972 | char zTime[10000]; |
| 973 | |
| 974 | login_check_credentials(); |
| 975 | if( !g.perm.Admin ){ |
| 976 | login_needed(0); |
| 977 | return; |
| 978 | } |
| 979 | style_header("Server Hack Log"); |
| 980 | style_submenu_element("Log-Menu", "%R/setup-logmenu"); |
| 981 | |
| 982 | if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ |
| 983 | no_error_log_available(); |
| 984 | style_finish_page(); |
| 985 | return; |
| 986 | } |
| 987 | in = fossil_fopen(g.zErrlog, "rb"); |
| 988 | if( in==0 ){ |
| 989 | @ <p class='generalError'>Unable to open that file for reading!</p> |
| 990 | style_finish_page(); |
| 991 | return; |
| 992 | } |
| 993 | szFile = file_size(g.zErrlog, ExtFILE); |
| 994 | zLog = file_canonical_name_dup(g.zErrlog); |
| 995 | @ %s(isNot?"Non-hack":"Hack") messages contained within the %lld(szFile)-byte |
| 996 | @ <a href="%R/errorlog?all">error log</a> found at |
| 997 | @ "%h(zLog)". |
| 998 | fossil_free(zLog); |
| 999 | @ <hr> |
| 1000 | @ <pre> |
| 1001 | while( fgets(z, sizeof(z), in) ){ |
| 1002 | if( prevWasTime |
| 1003 | && ((strncmp(z,"possible hack attempt - 418 ", 27)==0) ^ isNot) |
| 1004 | ){ |
| 1005 | @ %h(zTime)\ |
| 1006 | bOutput = 1; |
| 1007 | } |
| 1008 | if( strncmp(z, "--------", 8)==0 ){ |
| 1009 | size_t n = strlen(z); |
| 1010 | memcpy(zTime, z, n+1); |
| 1011 | prevWasTime = 1; |
| 1012 | bOutput = 0; |
| 1013 | }else{ |
| 1014 | prevWasTime = 0; |
| 1015 | } |
| 1016 | if( bOutput ){ |
| 1017 | @ %h(z)\ |
| 1018 | } |
| 1019 | } |
| 1020 | fclose(in); |
| 1021 | @ </pre> |
| 1022 | style_finish_page(); |
| 1023 | } |
| 1024 | |
| 1025 | /* |
| 1026 | ** WEBPAGE: logsummary |
| 1027 | ** |
| 1028 | ** Scan the error log and count the various kinds of entries. |
| 1029 | */ |
| 1030 | void logsummary_page(void){ |
| 1031 | i64 szFile; |
| 1032 | char *zLog; |
| 1033 | FILE *in; |
| 1034 | int prevWasTime = 0; |
| 1035 | int nHack = 0; |
| 1036 | int nPanic = 0; |
| 1037 | int nOther = 0; |
| 1038 | int nTotal = 0; |
| 1039 | char z[10000]; |
| 1040 | |
| 1041 | login_check_credentials(); |
| 1042 | if( !g.perm.Admin ){ |
| 1043 | login_needed(0); |
| 1044 | return; |
| 1045 | } |
| 1046 | style_header("Server Hack Log"); |
| 1047 | style_submenu_element("Log-Menu", "%R/setup-logmenu"); |
| 1048 | |
| 1049 | if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ |
| 1050 | no_error_log_available(); |
| 1051 | style_finish_page(); |
| 1052 | return; |
| 1053 | } |
| 1054 | in = fossil_fopen(g.zErrlog, "rb"); |
| 1055 | if( in==0 ){ |
| 1056 | @ <p class='generalError'>Unable to open that file for reading!</p> |
| 1057 | style_finish_page(); |
| 1058 | return; |
| 1059 | } |
| 1060 | szFile = file_size(g.zErrlog, ExtFILE); |
| 1061 | zLog = file_canonical_name_dup(g.zErrlog); |
| 1062 | @ Summary of messages contained within the %lld(szFile)-byte |
| 1063 | @ <a href="%R/errorlog?all">error log</a> found at |
| 1064 | @ "%h(zLog)". |
| 1065 | fossil_free(zLog); |
| 1066 | @ <hr> |
| 1067 | while( fgets(z, sizeof(z), in) ){ |
| 1068 | if( prevWasTime |
| 1069 | && (strncmp(z,"possible hack attempt - 418 ", 27)==0) |
| 1070 | ){ |
| 1071 | nHack++; |
| 1072 | prevWasTime = 0; |
| 1073 | continue; |
| 1074 | } |
| 1075 | if( prevWasTime |
| 1076 | && (strncmp(z,"panic: ", 7)==0 || strstr(z," assertion fault ")!=0) |
| 1077 | ){ |
| 1078 | nPanic++; |
| 1079 | prevWasTime = 0; |
| 1080 | continue; |
| 1081 | } |
| 1082 | if( prevWasTime ) nOther++; |
| 1083 | if( strncmp(z, "--------", 8)==0 ){ |
| 1084 | nTotal++; |
| 1085 | prevWasTime = 1; |
| 1086 | continue; |
| 1087 | } |
| 1088 | prevWasTime = 0; |
| 1089 | } |
| 1090 | fclose(in); |
| 1091 | @ <p><table border="a" cellspacing="0" cellpadding="5"> |
| 1092 | @ <tr><td align="right">%d(nPanic)</td> |
| 1093 | if( nPanic>0 ){ |
| 1094 | @ <td><a href="./paniclog">Panics</a></td> |
| 1095 | } else { |
| 1096 | @ <td>Panics</td> |
| 1097 | } |
| 1098 | @ <tr><td align="right">%d(nHack)</td> |
| 1099 | if( nHack>0 ){ |
| 1100 | @ <td><a href="./hacklog">Hack Attempts</a></td> |
| 1101 | }else{ |
| 1102 | @ <td>Hack Attempts</td> |
| 1103 | } |
| 1104 | @ <tr><td align="right">%d(nOther)</td> |
| 1105 | @ <td>Other</td> |
| 1106 | @ <tr><td align="right">%d(nTotal)</td> |
| 1107 | @ <td>Total Messages</td> |
| 1108 | @ </table> |
| 1109 | style_finish_page(); |
| 1110 | } |
| 1111 |
| --- src/security_audit.c | |
| +++ src/security_audit.c | |
| @@ -553,17 +553,17 @@ | |
| 553 | @ checkbox on the <a href="setup_access">Access Control</a> page. |
| 554 | } |
| 555 | |
| 556 | /* Logging should be turned on |
| 557 | */ |
| 558 | if( db_get_boolean("access-log",1)==0 ){ |
| 559 | @ <li><p> |
| 560 | @ The <a href="access_log">User Log</a> is disabled. The user log |
| 561 | @ keeps a record of successful and unsuccessful login attempts and is |
| 562 | @ useful for security monitoring. |
| 563 | } |
| 564 | if( db_get_boolean("admin-log",1)==0 ){ |
| 565 | @ <li><p> |
| 566 | @ The <a href="admin_log">Administrative Log</a> is disabled. |
| 567 | @ The administrative log provides a record of configuration changes |
| 568 | @ and is useful for security monitoring. |
| 569 | } |
| @@ -804,37 +804,59 @@ | |
| 804 | @ </pre></blockquote> |
| 805 | blob_reset(&fullname); |
| 806 | } |
| 807 | } |
| 808 | |
| 809 | /* |
| 810 | ** WEBPAGE: errorlog |
| 811 | ** |
| 812 | ** Show the content of the error log. Only the administrator can view |
| 813 | ** this page. |
| 814 | ** |
| 815 | ** y=0x01 Show only hack attempts |
| 816 | ** y=0x02 Show only panics and assertion faults |
| 817 | ** y=0x04 Show hung backoffice processes |
| 818 | ** y=0x08 Show POST requests from a different origin |
| 819 | ** y=0x40 Show other uncategorized messages |
| 820 | ** |
| 821 | ** If y is omitted or is zero, a count of the various message types is |
| 822 | ** shown. |
| 823 | */ |
| 824 | void errorlog_page(void){ |
| 825 | i64 szFile; |
| 826 | FILE *in; |
| 827 | char *zLog; |
| 828 | const char *zType = P("y"); |
| 829 | static const int eAllTypes = 0x4f; |
| 830 | long eType = 0; |
| 831 | int bOutput = 0; |
| 832 | int prevWasTime = 0; |
| 833 | int nHack = 0; |
| 834 | int nPanic = 0; |
| 835 | int nOther = 0; |
| 836 | int nHang = 0; |
| 837 | int nXPost = 0; |
| 838 | char z[10000]; |
| 839 | char zTime[10000]; |
| 840 | |
| 841 | login_check_credentials(); |
| 842 | if( !g.perm.Admin ){ |
| 843 | login_needed(0); |
| 844 | return; |
| 845 | } |
| 846 | if( zType ){ |
| 847 | eType = strtol(zType,0,0) & eAllTypes; |
| 848 | } |
| 849 | style_header("Server Error Log"); |
| 850 | style_submenu_element("Test", "%R/test-warning"); |
| 851 | style_submenu_element("Refresh", "%R/errorlog"); |
| 852 | style_submenu_element("Download", "%R/errorlog?download"); |
| 853 | style_submenu_element("Truncate", "%R/errorlog?truncate"); |
| 854 | style_submenu_element("Log-Menu", "%R/setup-logmenu"); |
| 855 | if( eType ){ |
| 856 | style_submenu_element("Summary", "%R/errorlog"); |
| 857 | } |
| 858 | |
| 859 | if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ |
| 860 | no_error_log_available(); |
| 861 | style_finish_page(); |
| 862 | return; |
| @@ -861,250 +883,117 @@ | |
| 883 | return; |
| 884 | } |
| 885 | zLog = file_canonical_name_dup(g.zErrlog); |
| 886 | @ <p>The server error log at "%h(zLog)" is %,lld(szFile) bytes in size. |
| 887 | fossil_free(zLog); |
| 888 | in = fossil_fopen(g.zErrlog, "rb"); |
| 889 | if( in==0 ){ |
| 890 | @ <p class='generalError'>Unable to open that file for reading!</p> |
| 891 | style_finish_page(); |
| 892 | return; |
| 893 | } |
| 894 | if( eType==0 ){ |
| 895 | /* will do a summary */ |
| 896 | }else if( (eType&eAllTypes)!=eAllTypes ){ |
| 897 | @ Only the following types of messages displayed: |
| 898 | @ <ul> |
| 899 | if( eType & 0x01 ){ |
| 900 | @ <li>Hack attempts |
| 901 | } |
| 902 | if( eType & 0x02 ){ |
| 903 | @ <li>Panics and assertion faults |
| 904 | } |
| 905 | if( eType & 0x04 ){ |
| 906 | @ <li>Hung backoffice processes |
| 907 | } |
| 908 | if( eType & 0x08 ){ |
| 909 | @ <li>POST requests from different origin |
| 910 | } |
| 911 | if( eType & 0x40 ){ |
| 912 | @ <li>Other uncategorized messages |
| 913 | } |
| 914 | @ </ul> |
| 915 | } |
| 916 | @ <hr> |
| 917 | if( eType ){ |
| 918 | @ <pre> |
| 919 | } |
| 920 | while( fgets(z, sizeof(z), in) ){ |
| 921 | if( prevWasTime ){ |
| 922 | if( strncmp(z,"possible hack attempt - 418 ", 27)==0 ){ |
| 923 | bOutput = (eType & 0x01)!=0; |
| 924 | nHack++; |
| 925 | }else |
| 926 | if( (strncmp(z,"panic: ", 7)==0 || strstr(z," assertion fault ")!=0) ){ |
| 927 | bOutput = (eType & 0x02)!=0; |
| 928 | nPanic++; |
| 929 | }else |
| 930 | if( sqlite3_strglob("warning: backoffice process * still *",z)==0 ){ |
| 931 | bOutput = (eType & 0x04)!=0; |
| 932 | nHang++; |
| 933 | }else |
| 934 | if( sqlite3_strglob("warning: POST from different origin*",z)==0 ){ |
| 935 | bOutput = (eType & 0x08)!=0; |
| 936 | nXPost++; |
| 937 | }else |
| 938 | { |
| 939 | bOutput = (eType & 0x40)!=0; |
| 940 | nOther++; |
| 941 | } |
| 942 | if( bOutput ){ |
| 943 | @ %h(zTime)\ |
| 944 | } |
| 945 | } |
| 946 | if( strncmp(z, "--------", 8)==0 ){ |
| 947 | size_t n = strlen(z); |
| 948 | memcpy(zTime, z, n+1); |
| 949 | prevWasTime = 1; |
| 950 | bOutput = 0; |
| 951 | }else{ |
| 952 | prevWasTime = 0; |
| 953 | } |
| 954 | if( bOutput && eType ){ |
| 955 | @ %h(z)\ |
| 956 | } |
| 957 | } |
| 958 | fclose(in); |
| 959 | if( eType ){ |
| 960 | @ </pre> |
| 961 | } |
| 962 | if( eType==0 ){ |
| 963 | int nNonHack = nPanic + nHang + nOther; |
| 964 | int nTotal = nNonHack + nHack + nXPost; |
| 965 | @ <p><table border="a" cellspacing="0" cellpadding="5"> |
| 966 | if( nPanic>0 ){ |
| 967 | @ <tr><td align="right">%d(nPanic)</td> |
| 968 | @ <td><a href="./errorlog?y=2">Panics</a></td> |
| 969 | } |
| 970 | if( nHack>0 ){ |
| 971 | @ <tr><td align="right">%d(nHack)</td> |
| 972 | @ <td><a href="./errorlog?y=1">Hack Attempts</a></td> |
| 973 | } |
| 974 | if( nHang>0 ){ |
| 975 | @ <tr><td align="right">%d(nHang)</td> |
| 976 | @ <td><a href="./errorlog?y=4/">Hung Backoffice</a></td> |
| 977 | } |
| 978 | if( nXPost>0 ){ |
| 979 | @ <tr><td align="right">%d(nXPost)</td> |
| 980 | @ <td><a href="./errorlog?y=8/">POSTs from different origin</a></td> |
| 981 | } |
| 982 | if( nOther>0 ){ |
| 983 | @ <tr><td align="right">%d(nOther)</td> |
| 984 | @ <td><a href="./errorlog?y=64/">Other</a></td> |
| 985 | } |
| 986 | if( nHack+nXPost>0 && nNonHack>0 ){ |
| 987 | @ <tr><td align="right">%d(nNonHack)</td> |
| 988 | @ <td><a href="%R/errorlog?y=70">Other than hack attempts</a></td> |
| 989 | } |
| 990 | @ <tr><td align="right">%d(nTotal)</td> |
| 991 | if( nTotal>0 ){ |
| 992 | @ <td><a href="./errorlog?y=255">All Messages</a></td> |
| 993 | }else{ |
| 994 | @ <td>All Messages</td> |
| 995 | } |
| 996 | @ </table> |
| 997 | } |
| 998 | style_finish_page(); |
| 999 | } |
| 1000 |
+26
-26
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -202,11 +202,11 @@ | ||
| 202 | 202 | return; |
| 203 | 203 | } |
| 204 | 204 | style_header("Log Menu"); |
| 205 | 205 | @ <table border="0" cellspacing="3"> |
| 206 | 206 | |
| 207 | - if( db_get_boolean("admin-log",0)==0 ){ | |
| 207 | + if( db_get_boolean("admin-log",1)==0 ){ | |
| 208 | 208 | blob_appendf(&desc, |
| 209 | 209 | "The admin log records configuration changes to the repository.\n" |
| 210 | 210 | "<b>Disabled</b>: Turn on the " |
| 211 | 211 | " <a href='%R/setup_settings'>admin-log setting</a> to enable." |
| 212 | 212 | ); |
| @@ -220,11 +220,11 @@ | ||
| 220 | 220 | } |
| 221 | 221 | setup_menu_entry("Artifact Log", "rcvfromlist", |
| 222 | 222 | "The artifact log records when new content is added in the\n" |
| 223 | 223 | "\"rcvfrom\" table.\n" |
| 224 | 224 | ); |
| 225 | - if( db_get_boolean("access-log",0) ){ | |
| 225 | + if( db_get_boolean("access-log",1) ){ | |
| 226 | 226 | setup_menu_entry("User Log", "user_log", |
| 227 | 227 | "Login attempts recorded in the \"accesslog\" table." |
| 228 | 228 | ); |
| 229 | 229 | }else{ |
| 230 | 230 | blob_appendf(&desc, |
| @@ -264,30 +264,10 @@ | ||
| 264 | 264 | bErrLog = 1; |
| 265 | 265 | } |
| 266 | 266 | setup_menu_entry("Error Log", bErrLog ? "errorlog" : 0, blob_str(&desc)); |
| 267 | 267 | blob_reset(&desc); |
| 268 | 268 | |
| 269 | - @ <tr><td><td><td> | |
| 270 | - @ —— | |
| 271 | - @ <i>The remaining links are subsets of the Error Log</i> | |
| 272 | - @ —— | |
| 273 | - @ </td> | |
| 274 | - | |
| 275 | - setup_menu_entry("Error Summary", bErrLog ? "logsummary" : 0, | |
| 276 | - "Counts of the various message types seen in the Error Log.\n" | |
| 277 | - ); | |
| 278 | - setup_menu_entry("Panic Log", bErrLog ? "paniclog" : 0, | |
| 279 | - "Only the most important messages in the Error Log:\n" | |
| 280 | - "assertion faults, segmentation faults, and similar malfunctions.\n" | |
| 281 | - ); | |
| 282 | - setup_menu_entry("Hack Log", bErrLog ? "hacklog" : 0, | |
| 283 | - "All code-418 hack attempts in the Error Log" | |
| 284 | - ); | |
| 285 | - setup_menu_entry("Non-Hack Log", bErrLog ? "hacklog?not" : 0, | |
| 286 | - "All log messages that are not code-418 hack attempts" | |
| 287 | - ); | |
| 288 | - | |
| 289 | 269 | @ </table> |
| 290 | 270 | style_finish_page(); |
| 291 | 271 | } |
| 292 | 272 | |
| 293 | 273 | /* |
| @@ -1129,10 +1109,11 @@ | ||
| 1129 | 1109 | */ |
| 1130 | 1110 | void setup_settings(void){ |
| 1131 | 1111 | int nSetting; |
| 1132 | 1112 | int i; |
| 1133 | 1113 | Setting const *pSet; |
| 1114 | + int bIfChng = P("all")==0; | |
| 1134 | 1115 | const Setting *aSetting = setting_info(&nSetting); |
| 1135 | 1116 | |
| 1136 | 1117 | login_check_credentials(); |
| 1137 | 1118 | if( !g.perm.Setup ){ |
| 1138 | 1119 | login_needed(0); |
| @@ -1145,23 +1126,36 @@ | ||
| 1145 | 1126 | /* Provide read-only access to versioned settings, |
| 1146 | 1127 | but only if no repo file was explicitly provided. */ |
| 1147 | 1128 | db_open_local(0); |
| 1148 | 1129 | } |
| 1149 | 1130 | db_begin_transaction(); |
| 1131 | + if( bIfChng ){ | |
| 1132 | + @ <p>Only settings whose value is different from the default are shown. | |
| 1133 | + @ Click the "All" button above to set all settings. | |
| 1134 | + } | |
| 1150 | 1135 | @ <p>Settings marked with (v) are "versionable" and will be overridden |
| 1151 | 1136 | @ by the contents of managed files named |
| 1152 | 1137 | @ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>". |
| 1153 | 1138 | @ If the file for a versionable setting exists, the value cannot be |
| 1154 | 1139 | @ changed on this screen.</p><hr><p> |
| 1155 | 1140 | @ |
| 1156 | 1141 | @ <form action="%R/setup_settings" method="post"><div> |
| 1142 | + if( bIfChng ){ | |
| 1143 | + style_submenu_element("All", "%R/setup_settings?all"); | |
| 1144 | + }else{ | |
| 1145 | + @ <input type="hidden" name="all" value="1"> | |
| 1146 | + style_submenu_element("Changes-Only", "%R/setup_settings"); | |
| 1147 | + } | |
| 1157 | 1148 | @ <table border="0"><tr><td valign="top"> |
| 1158 | 1149 | login_insert_csrf_secret(); |
| 1159 | 1150 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1160 | 1151 | if( pSet->width==0 ){ |
| 1161 | 1152 | int hasVersionableValue = pSet->versionable && |
| 1162 | - (db_get_versioned(pSet->name, NULL)!=0); | |
| 1153 | + (db_get_versioned(pSet->name, NULL, NULL)!=0); | |
| 1154 | + if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){ | |
| 1155 | + continue; | |
| 1156 | + } | |
| 1163 | 1157 | onoff_attribute("", pSet->name, |
| 1164 | 1158 | pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/, |
| 1165 | 1159 | is_truth(pSet->def), hasVersionableValue); |
| 1166 | 1160 | @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> |
| 1167 | 1161 | if( pSet->versionable ){ |
| @@ -1175,11 +1169,14 @@ | ||
| 1175 | 1169 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 1176 | 1170 | @ <table> |
| 1177 | 1171 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1178 | 1172 | if( pSet->width>0 && !pSet->forceTextArea ){ |
| 1179 | 1173 | int hasVersionableValue = pSet->versionable && |
| 1180 | - (db_get_versioned(pSet->name, NULL)!=0); | |
| 1174 | + (db_get_versioned(pSet->name, NULL, NULL)!=0); | |
| 1175 | + if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){ | |
| 1176 | + continue; | |
| 1177 | + } | |
| 1181 | 1178 | @ <tr><td> |
| 1182 | 1179 | @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> |
| 1183 | 1180 | if( pSet->versionable ){ |
| 1184 | 1181 | @ (v) |
| 1185 | 1182 | } else { |
| @@ -1194,11 +1191,14 @@ | ||
| 1194 | 1191 | } |
| 1195 | 1192 | @</table> |
| 1196 | 1193 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 1197 | 1194 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1198 | 1195 | if( pSet->width>0 && pSet->forceTextArea ){ |
| 1199 | - int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0; | |
| 1196 | + int hasVersionableValue = db_get_versioned(pSet->name, NULL, NULL)!=0; | |
| 1197 | + if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){ | |
| 1198 | + continue; | |
| 1199 | + } | |
| 1200 | 1200 | @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a> |
| 1201 | 1201 | if( pSet->versionable ){ |
| 1202 | 1202 | @ (v)<br> |
| 1203 | 1203 | } else { |
| 1204 | 1204 | @ <br> |
| @@ -2141,11 +2141,11 @@ | ||
| 2141 | 2141 | style_header("Admin Log"); |
| 2142 | 2142 | style_submenu_element("Log-Menu", "setup-logmenu"); |
| 2143 | 2143 | create_admin_log_table(); |
| 2144 | 2144 | limit = atoi(PD("n","200")); |
| 2145 | 2145 | ofst = atoi(PD("x","0")); |
| 2146 | - fLogEnabled = db_get_boolean("admin-log", 0); | |
| 2146 | + fLogEnabled = db_get_boolean("admin-log", 1); | |
| 2147 | 2147 | @ <div>Admin logging is %s(fLogEnabled?"on":"off"). |
| 2148 | 2148 | @ (Change this on the <a href="setup_settings">settings</a> page.)</div> |
| 2149 | 2149 | |
| 2150 | 2150 | if( ofst>0 ){ |
| 2151 | 2151 | int prevx = ofst - limit; |
| 2152 | 2152 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -202,11 +202,11 @@ | |
| 202 | return; |
| 203 | } |
| 204 | style_header("Log Menu"); |
| 205 | @ <table border="0" cellspacing="3"> |
| 206 | |
| 207 | if( db_get_boolean("admin-log",0)==0 ){ |
| 208 | blob_appendf(&desc, |
| 209 | "The admin log records configuration changes to the repository.\n" |
| 210 | "<b>Disabled</b>: Turn on the " |
| 211 | " <a href='%R/setup_settings'>admin-log setting</a> to enable." |
| 212 | ); |
| @@ -220,11 +220,11 @@ | |
| 220 | } |
| 221 | setup_menu_entry("Artifact Log", "rcvfromlist", |
| 222 | "The artifact log records when new content is added in the\n" |
| 223 | "\"rcvfrom\" table.\n" |
| 224 | ); |
| 225 | if( db_get_boolean("access-log",0) ){ |
| 226 | setup_menu_entry("User Log", "user_log", |
| 227 | "Login attempts recorded in the \"accesslog\" table." |
| 228 | ); |
| 229 | }else{ |
| 230 | blob_appendf(&desc, |
| @@ -264,30 +264,10 @@ | |
| 264 | bErrLog = 1; |
| 265 | } |
| 266 | setup_menu_entry("Error Log", bErrLog ? "errorlog" : 0, blob_str(&desc)); |
| 267 | blob_reset(&desc); |
| 268 | |
| 269 | @ <tr><td><td><td> |
| 270 | @ —— |
| 271 | @ <i>The remaining links are subsets of the Error Log</i> |
| 272 | @ —— |
| 273 | @ </td> |
| 274 | |
| 275 | setup_menu_entry("Error Summary", bErrLog ? "logsummary" : 0, |
| 276 | "Counts of the various message types seen in the Error Log.\n" |
| 277 | ); |
| 278 | setup_menu_entry("Panic Log", bErrLog ? "paniclog" : 0, |
| 279 | "Only the most important messages in the Error Log:\n" |
| 280 | "assertion faults, segmentation faults, and similar malfunctions.\n" |
| 281 | ); |
| 282 | setup_menu_entry("Hack Log", bErrLog ? "hacklog" : 0, |
| 283 | "All code-418 hack attempts in the Error Log" |
| 284 | ); |
| 285 | setup_menu_entry("Non-Hack Log", bErrLog ? "hacklog?not" : 0, |
| 286 | "All log messages that are not code-418 hack attempts" |
| 287 | ); |
| 288 | |
| 289 | @ </table> |
| 290 | style_finish_page(); |
| 291 | } |
| 292 | |
| 293 | /* |
| @@ -1129,10 +1109,11 @@ | |
| 1129 | */ |
| 1130 | void setup_settings(void){ |
| 1131 | int nSetting; |
| 1132 | int i; |
| 1133 | Setting const *pSet; |
| 1134 | const Setting *aSetting = setting_info(&nSetting); |
| 1135 | |
| 1136 | login_check_credentials(); |
| 1137 | if( !g.perm.Setup ){ |
| 1138 | login_needed(0); |
| @@ -1145,23 +1126,36 @@ | |
| 1145 | /* Provide read-only access to versioned settings, |
| 1146 | but only if no repo file was explicitly provided. */ |
| 1147 | db_open_local(0); |
| 1148 | } |
| 1149 | db_begin_transaction(); |
| 1150 | @ <p>Settings marked with (v) are "versionable" and will be overridden |
| 1151 | @ by the contents of managed files named |
| 1152 | @ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>". |
| 1153 | @ If the file for a versionable setting exists, the value cannot be |
| 1154 | @ changed on this screen.</p><hr><p> |
| 1155 | @ |
| 1156 | @ <form action="%R/setup_settings" method="post"><div> |
| 1157 | @ <table border="0"><tr><td valign="top"> |
| 1158 | login_insert_csrf_secret(); |
| 1159 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1160 | if( pSet->width==0 ){ |
| 1161 | int hasVersionableValue = pSet->versionable && |
| 1162 | (db_get_versioned(pSet->name, NULL)!=0); |
| 1163 | onoff_attribute("", pSet->name, |
| 1164 | pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/, |
| 1165 | is_truth(pSet->def), hasVersionableValue); |
| 1166 | @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> |
| 1167 | if( pSet->versionable ){ |
| @@ -1175,11 +1169,14 @@ | |
| 1175 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 1176 | @ <table> |
| 1177 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1178 | if( pSet->width>0 && !pSet->forceTextArea ){ |
| 1179 | int hasVersionableValue = pSet->versionable && |
| 1180 | (db_get_versioned(pSet->name, NULL)!=0); |
| 1181 | @ <tr><td> |
| 1182 | @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> |
| 1183 | if( pSet->versionable ){ |
| 1184 | @ (v) |
| 1185 | } else { |
| @@ -1194,11 +1191,14 @@ | |
| 1194 | } |
| 1195 | @</table> |
| 1196 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 1197 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1198 | if( pSet->width>0 && pSet->forceTextArea ){ |
| 1199 | int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0; |
| 1200 | @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a> |
| 1201 | if( pSet->versionable ){ |
| 1202 | @ (v)<br> |
| 1203 | } else { |
| 1204 | @ <br> |
| @@ -2141,11 +2141,11 @@ | |
| 2141 | style_header("Admin Log"); |
| 2142 | style_submenu_element("Log-Menu", "setup-logmenu"); |
| 2143 | create_admin_log_table(); |
| 2144 | limit = atoi(PD("n","200")); |
| 2145 | ofst = atoi(PD("x","0")); |
| 2146 | fLogEnabled = db_get_boolean("admin-log", 0); |
| 2147 | @ <div>Admin logging is %s(fLogEnabled?"on":"off"). |
| 2148 | @ (Change this on the <a href="setup_settings">settings</a> page.)</div> |
| 2149 | |
| 2150 | if( ofst>0 ){ |
| 2151 | int prevx = ofst - limit; |
| 2152 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -202,11 +202,11 @@ | |
| 202 | return; |
| 203 | } |
| 204 | style_header("Log Menu"); |
| 205 | @ <table border="0" cellspacing="3"> |
| 206 | |
| 207 | if( db_get_boolean("admin-log",1)==0 ){ |
| 208 | blob_appendf(&desc, |
| 209 | "The admin log records configuration changes to the repository.\n" |
| 210 | "<b>Disabled</b>: Turn on the " |
| 211 | " <a href='%R/setup_settings'>admin-log setting</a> to enable." |
| 212 | ); |
| @@ -220,11 +220,11 @@ | |
| 220 | } |
| 221 | setup_menu_entry("Artifact Log", "rcvfromlist", |
| 222 | "The artifact log records when new content is added in the\n" |
| 223 | "\"rcvfrom\" table.\n" |
| 224 | ); |
| 225 | if( db_get_boolean("access-log",1) ){ |
| 226 | setup_menu_entry("User Log", "user_log", |
| 227 | "Login attempts recorded in the \"accesslog\" table." |
| 228 | ); |
| 229 | }else{ |
| 230 | blob_appendf(&desc, |
| @@ -264,30 +264,10 @@ | |
| 264 | bErrLog = 1; |
| 265 | } |
| 266 | setup_menu_entry("Error Log", bErrLog ? "errorlog" : 0, blob_str(&desc)); |
| 267 | blob_reset(&desc); |
| 268 | |
| 269 | @ </table> |
| 270 | style_finish_page(); |
| 271 | } |
| 272 | |
| 273 | /* |
| @@ -1129,10 +1109,11 @@ | |
| 1109 | */ |
| 1110 | void setup_settings(void){ |
| 1111 | int nSetting; |
| 1112 | int i; |
| 1113 | Setting const *pSet; |
| 1114 | int bIfChng = P("all")==0; |
| 1115 | const Setting *aSetting = setting_info(&nSetting); |
| 1116 | |
| 1117 | login_check_credentials(); |
| 1118 | if( !g.perm.Setup ){ |
| 1119 | login_needed(0); |
| @@ -1145,23 +1126,36 @@ | |
| 1126 | /* Provide read-only access to versioned settings, |
| 1127 | but only if no repo file was explicitly provided. */ |
| 1128 | db_open_local(0); |
| 1129 | } |
| 1130 | db_begin_transaction(); |
| 1131 | if( bIfChng ){ |
| 1132 | @ <p>Only settings whose value is different from the default are shown. |
| 1133 | @ Click the "All" button above to set all settings. |
| 1134 | } |
| 1135 | @ <p>Settings marked with (v) are "versionable" and will be overridden |
| 1136 | @ by the contents of managed files named |
| 1137 | @ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>". |
| 1138 | @ If the file for a versionable setting exists, the value cannot be |
| 1139 | @ changed on this screen.</p><hr><p> |
| 1140 | @ |
| 1141 | @ <form action="%R/setup_settings" method="post"><div> |
| 1142 | if( bIfChng ){ |
| 1143 | style_submenu_element("All", "%R/setup_settings?all"); |
| 1144 | }else{ |
| 1145 | @ <input type="hidden" name="all" value="1"> |
| 1146 | style_submenu_element("Changes-Only", "%R/setup_settings"); |
| 1147 | } |
| 1148 | @ <table border="0"><tr><td valign="top"> |
| 1149 | login_insert_csrf_secret(); |
| 1150 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1151 | if( pSet->width==0 ){ |
| 1152 | int hasVersionableValue = pSet->versionable && |
| 1153 | (db_get_versioned(pSet->name, NULL, NULL)!=0); |
| 1154 | if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){ |
| 1155 | continue; |
| 1156 | } |
| 1157 | onoff_attribute("", pSet->name, |
| 1158 | pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/, |
| 1159 | is_truth(pSet->def), hasVersionableValue); |
| 1160 | @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> |
| 1161 | if( pSet->versionable ){ |
| @@ -1175,11 +1169,14 @@ | |
| 1169 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 1170 | @ <table> |
| 1171 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1172 | if( pSet->width>0 && !pSet->forceTextArea ){ |
| 1173 | int hasVersionableValue = pSet->versionable && |
| 1174 | (db_get_versioned(pSet->name, NULL, NULL)!=0); |
| 1175 | if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){ |
| 1176 | continue; |
| 1177 | } |
| 1178 | @ <tr><td> |
| 1179 | @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> |
| 1180 | if( pSet->versionable ){ |
| 1181 | @ (v) |
| 1182 | } else { |
| @@ -1194,11 +1191,14 @@ | |
| 1191 | } |
| 1192 | @</table> |
| 1193 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 1194 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1195 | if( pSet->width>0 && pSet->forceTextArea ){ |
| 1196 | int hasVersionableValue = db_get_versioned(pSet->name, NULL, NULL)!=0; |
| 1197 | if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){ |
| 1198 | continue; |
| 1199 | } |
| 1200 | @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a> |
| 1201 | if( pSet->versionable ){ |
| 1202 | @ (v)<br> |
| 1203 | } else { |
| 1204 | @ <br> |
| @@ -2141,11 +2141,11 @@ | |
| 2141 | style_header("Admin Log"); |
| 2142 | style_submenu_element("Log-Menu", "setup-logmenu"); |
| 2143 | create_admin_log_table(); |
| 2144 | limit = atoi(PD("n","200")); |
| 2145 | ofst = atoi(PD("x","0")); |
| 2146 | fLogEnabled = db_get_boolean("admin-log", 1); |
| 2147 | @ <div>Admin logging is %s(fLogEnabled?"on":"off"). |
| 2148 | @ (Change this on the <a href="setup_settings">settings</a> page.)</div> |
| 2149 | |
| 2150 | if( ofst>0 ){ |
| 2151 | int prevx = ofst - limit; |
| 2152 |
+116
-12
| --- src/setupuser.c | ||
| +++ src/setupuser.c | ||
| @@ -302,10 +302,101 @@ | ||
| 302 | 302 | if( zPw==0 ) return 0; |
| 303 | 303 | if( zPw[0]==0 ) return 1; |
| 304 | 304 | while( zPw[0]=='*' ){ zPw++; } |
| 305 | 305 | return zPw[0]!=0; |
| 306 | 306 | } |
| 307 | + | |
| 308 | +/* | |
| 309 | +** Return true if user capability string zNew contains any capability | |
| 310 | +** letter which is not in user capability string zOrig, else 0. This | |
| 311 | +** does not take inherited permissions into account. Either argument | |
| 312 | +** may be NULL. | |
| 313 | +*/ | |
| 314 | +static int userHasNewCaps(const char *zOrig, const char *zNew){ | |
| 315 | + for( ; zNew && *zNew; ++zNew ){ | |
| 316 | + if( !zOrig || strchr(zOrig,*zNew)==0 ){ | |
| 317 | + return *zNew; | |
| 318 | + } | |
| 319 | + } | |
| 320 | + return 0; | |
| 321 | +} | |
| 322 | + | |
| 323 | +/* | |
| 324 | +** Sends notification of user permission elevation changes to all | |
| 325 | +** subscribers with a "u" subscription. This is a no-op if alerts are | |
| 326 | +** not enabled. | |
| 327 | +** | |
| 328 | +** These subscriptions differ from most, in that: | |
| 329 | +** | |
| 330 | +** - They currently lack an "unsubscribe" link. | |
| 331 | +** | |
| 332 | +** - Only an admin can assign this subscription, but if a non-admin | |
| 333 | +** edits their subscriptions after an admin assigns them this one, | |
| 334 | +** this particular one will be lost. "Feature or bug?" is unclear, | |
| 335 | +** but it would be odd for a non-admin to be assigned this | |
| 336 | +** capability. | |
| 337 | +*/ | |
| 338 | +static void alert_user_elevation(const char *zLogin, /*Affected user*/ | |
| 339 | + int uid, /*[user].uid*/ | |
| 340 | + int bIsNew, /*true if new user*/ | |
| 341 | + const char *zOrigCaps,/*Old caps*/ | |
| 342 | + const char *zNewCaps /*New caps*/){ | |
| 343 | + Blob hdr, body; | |
| 344 | + Stmt q; | |
| 345 | + int nBody; | |
| 346 | + AlertSender *pSender; | |
| 347 | + char *zSubname; | |
| 348 | + char *zURL; | |
| 349 | + char * zSubject; | |
| 350 | + | |
| 351 | + if( !alert_enabled() ) return; | |
| 352 | + zSubject = bIsNew | |
| 353 | + ? mprintf("New user created: [%q]", zLogin) | |
| 354 | + : mprintf("User [%q] permissions elevated", zLogin); | |
| 355 | + zURL = db_get("email-url",0); | |
| 356 | + zSubname = db_get("email-subname", "[Fossil Repo]"); | |
| 357 | + blob_init(&body, 0, 0); | |
| 358 | + blob_init(&hdr, 0, 0); | |
| 359 | + if( bIsNew ){ | |
| 360 | + blob_appendf(&body, "User [%q] was created by with " | |
| 361 | + "permissions [%q] by user [%q].\n", | |
| 362 | + zLogin, zNewCaps, g.zLogin); | |
| 363 | + } else { | |
| 364 | + blob_appendf(&body, "Permissions for user [%q] where elevated " | |
| 365 | + "from [%q] to [%q] by user [%q].\n", | |
| 366 | + zLogin, zOrigCaps, zNewCaps, g.zLogin); | |
| 367 | + } | |
| 368 | + if( zURL ){ | |
| 369 | + blob_appendf(&body, "\nUser editor: %s/setup_uedit?uid=%d\n", zURL, uid); | |
| 370 | + } | |
| 371 | + nBody = blob_size(&body); | |
| 372 | + pSender = alert_sender_new(0, 0); | |
| 373 | + db_prepare(&q, | |
| 374 | + "SELECT semail, hex(subscriberCode)" | |
| 375 | + " FROM subscriber, user " | |
| 376 | + " WHERE sverified AND NOT sdonotcall" | |
| 377 | + " AND suname=login" | |
| 378 | + " AND ssub GLOB '*u*'"); | |
| 379 | + while( !pSender->zErr && db_step(&q)==SQLITE_ROW ){ | |
| 380 | + const char *zTo = db_column_text(&q, 0); | |
| 381 | + blob_truncate(&hdr, 0); | |
| 382 | + blob_appendf(&hdr, "To: <%s>\r\nSubject: %s %s\r\n", | |
| 383 | + zTo, zSubname, zSubject); | |
| 384 | + if( zURL ){ | |
| 385 | + const char *zCode = db_column_text(&q, 1); | |
| 386 | + blob_truncate(&body, nBody); | |
| 387 | + blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n", | |
| 388 | + zURL, zCode); | |
| 389 | + } | |
| 390 | + alert_send(pSender, &hdr, &body, 0); | |
| 391 | + } | |
| 392 | + db_finalize(&q); | |
| 393 | + alert_sender_free(pSender); | |
| 394 | + fossil_free(zURL); | |
| 395 | + fossil_free(zSubname); | |
| 396 | + fossil_free(zSubject); | |
| 397 | +} | |
| 307 | 398 | |
| 308 | 399 | /* |
| 309 | 400 | ** WEBPAGE: setup_uedit |
| 310 | 401 | ** |
| 311 | 402 | ** Edit information about a user or create a new user. |
| @@ -314,10 +405,11 @@ | ||
| 314 | 405 | void user_edit(void){ |
| 315 | 406 | const char *zId, *zLogin, *zInfo, *zCap, *zPw; |
| 316 | 407 | const char *zGroup; |
| 317 | 408 | const char *zOldLogin; |
| 318 | 409 | int uid, i; |
| 410 | + char *zOldCaps = 0; /* Capabilities before edit */ | |
| 319 | 411 | char *zDeleteVerify = 0; /* Delete user verification text */ |
| 320 | 412 | int higherUser = 0; /* True if user being edited is SETUP and the */ |
| 321 | 413 | /* user doing the editing is ADMIN. Disallow editing */ |
| 322 | 414 | const char *inherit[128]; |
| 323 | 415 | int a[128]; |
| @@ -331,14 +423,15 @@ | ||
| 331 | 423 | /* Check to see if an ADMIN user is trying to edit a SETUP account. |
| 332 | 424 | ** Don't allow that. |
| 333 | 425 | */ |
| 334 | 426 | zId = PD("id", "0"); |
| 335 | 427 | uid = atoi(zId); |
| 336 | - if( zId && !g.perm.Setup && uid>0 ){ | |
| 337 | - char *zOldCaps; | |
| 338 | - zOldCaps = db_text(0, "SELECT cap FROM user WHERE uid=%d",uid); | |
| 339 | - higherUser = zOldCaps && strchr(zOldCaps,'s'); | |
| 428 | + if( uid>0 ){ | |
| 429 | + zOldCaps = db_text("", "SELECT cap FROM user WHERE uid=%d",uid); | |
| 430 | + if( zId && !g.perm.Setup ){ | |
| 431 | + higherUser = zOldCaps && strchr(zOldCaps,'s'); | |
| 432 | + } | |
| 340 | 433 | } |
| 341 | 434 | |
| 342 | 435 | if( P("can") ){ |
| 343 | 436 | /* User pressed the cancel button */ |
| 344 | 437 | cgi_redirect(cgi_referer("setup_ulist")); |
| @@ -393,10 +486,12 @@ | ||
| 393 | 486 | }else if( !cgi_csrf_safe(2) ){ |
| 394 | 487 | /* This might be a cross-site request forgery, so ignore it */ |
| 395 | 488 | }else{ |
| 396 | 489 | /* We have all the information we need to make the change to the user */ |
| 397 | 490 | char c; |
| 491 | + int bHasNewCaps = 0 /* 1 if user's permissions are increased */; | |
| 492 | + const int bIsNew = uid<=0; | |
| 398 | 493 | char aCap[70], zNm[4]; |
| 399 | 494 | zNm[0] = 'a'; |
| 400 | 495 | zNm[2] = 0; |
| 401 | 496 | for(i=0, c='a'; c<='z'; c++){ |
| 402 | 497 | zNm[1] = c; |
| @@ -413,10 +508,11 @@ | ||
| 413 | 508 | a[c&0x7f] = P(zNm)!=0; |
| 414 | 509 | if( a[c&0x7f] ) aCap[i++] = c; |
| 415 | 510 | } |
| 416 | 511 | |
| 417 | 512 | aCap[i] = 0; |
| 513 | + bHasNewCaps = bIsNew || userHasNewCaps(zOldCaps, &aCap[0]); | |
| 418 | 514 | zPw = P("pw"); |
| 419 | 515 | zLogin = P("login"); |
| 420 | 516 | if( strlen(zLogin)==0 ){ |
| 421 | 517 | const char *zRef = cgi_referer("setup_ulist"); |
| 422 | 518 | style_header("User Creation Error"); |
| @@ -444,15 +540,16 @@ | ||
| 444 | 540 | style_finish_page(); |
| 445 | 541 | return; |
| 446 | 542 | } |
| 447 | 543 | cgi_csrf_verify(); |
| 448 | 544 | db_unprotect(PROTECT_USER); |
| 449 | - db_multi_exec( | |
| 450 | - "REPLACE INTO user(uid,login,info,pw,cap,mtime) " | |
| 451 | - "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now())", | |
| 452 | - uid, zLogin, P("info"), zPw, &aCap[0] | |
| 453 | - ); | |
| 545 | + uid = db_int(0, | |
| 546 | + "REPLACE INTO user(uid,login,info,pw,cap,mtime) " | |
| 547 | + "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now()) " | |
| 548 | + "RETURNING uid", | |
| 549 | + uid, zLogin, P("info"), zPw, &aCap[0]); | |
| 550 | + assert( uid>0 ); | |
| 454 | 551 | if( zOldLogin && fossil_strcmp(zLogin, zOldLogin)!=0 ){ |
| 455 | 552 | if( alert_tables_exist() ){ |
| 456 | 553 | /* Rename matching subscriber entry, else the user cannot |
| 457 | 554 | re-subscribe with their same email address. */ |
| 458 | 555 | db_multi_exec("UPDATE subscriber SET suname=%Q WHERE suname=%Q", |
| @@ -460,11 +557,12 @@ | ||
| 460 | 557 | } |
| 461 | 558 | admin_log( "Renamed user [%q] to [%q].", zOldLogin, zLogin ); |
| 462 | 559 | } |
| 463 | 560 | db_protect_pop(); |
| 464 | 561 | setup_incr_cfgcnt(); |
| 465 | - admin_log( "Updated user [%q] with capabilities [%q].", | |
| 562 | + admin_log( "%s user [%q] with capabilities [%q].", | |
| 563 | + bIsNew ? "Added" : "Updated", | |
| 466 | 564 | zLogin, &aCap[0] ); |
| 467 | 565 | if( atoi(PD("all","0"))>0 ){ |
| 468 | 566 | Blob sql; |
| 469 | 567 | char *zErr = 0; |
| 470 | 568 | blob_zero(&sql); |
| @@ -515,30 +613,36 @@ | ||
| 515 | 613 | @ <span class="loginError">%h(zErr)</span> |
| 516 | 614 | @ |
| 517 | 615 | @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)"> |
| 518 | 616 | @ [Bummer]</a></p> |
| 519 | 617 | style_finish_page(); |
| 618 | + if( bHasNewCaps ){ | |
| 619 | + alert_user_elevation(zLogin, uid, bIsNew, zOldCaps, &aCap[0]); | |
| 620 | + } | |
| 520 | 621 | return; |
| 521 | 622 | } |
| 522 | 623 | } |
| 624 | + if( bHasNewCaps ){ | |
| 625 | + alert_user_elevation(zLogin, uid, bIsNew, zOldCaps, &aCap[0]); | |
| 626 | + } | |
| 523 | 627 | cgi_redirect(cgi_referer("setup_ulist")); |
| 524 | 628 | return; |
| 525 | 629 | } |
| 526 | 630 | |
| 527 | 631 | /* Load the existing information about the user, if any |
| 528 | 632 | */ |
| 529 | 633 | zLogin = ""; |
| 530 | 634 | zInfo = ""; |
| 531 | - zCap = ""; | |
| 635 | + zCap = zOldCaps; | |
| 532 | 636 | zPw = ""; |
| 533 | 637 | for(i='a'; i<='z'; i++) oa[i] = ""; |
| 534 | 638 | for(i='0'; i<='9'; i++) oa[i] = ""; |
| 535 | 639 | for(i='A'; i<='Z'; i++) oa[i] = ""; |
| 536 | 640 | if( uid ){ |
| 641 | + assert( zCap ); | |
| 537 | 642 | zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid); |
| 538 | 643 | zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid); |
| 539 | - zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid); | |
| 540 | 644 | zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid); |
| 541 | 645 | for(i=0; zCap[i]; i++){ |
| 542 | 646 | char c = zCap[i]; |
| 543 | 647 | if( (c>='a' && c<='z') || (c>='0' && c<='9') || (c>='A' && c<='Z') ){ |
| 544 | 648 | oa[c&0x7f] = " checked=\"checked\""; |
| 545 | 649 |
| --- src/setupuser.c | |
| +++ src/setupuser.c | |
| @@ -302,10 +302,101 @@ | |
| 302 | if( zPw==0 ) return 0; |
| 303 | if( zPw[0]==0 ) return 1; |
| 304 | while( zPw[0]=='*' ){ zPw++; } |
| 305 | return zPw[0]!=0; |
| 306 | } |
| 307 | |
| 308 | /* |
| 309 | ** WEBPAGE: setup_uedit |
| 310 | ** |
| 311 | ** Edit information about a user or create a new user. |
| @@ -314,10 +405,11 @@ | |
| 314 | void user_edit(void){ |
| 315 | const char *zId, *zLogin, *zInfo, *zCap, *zPw; |
| 316 | const char *zGroup; |
| 317 | const char *zOldLogin; |
| 318 | int uid, i; |
| 319 | char *zDeleteVerify = 0; /* Delete user verification text */ |
| 320 | int higherUser = 0; /* True if user being edited is SETUP and the */ |
| 321 | /* user doing the editing is ADMIN. Disallow editing */ |
| 322 | const char *inherit[128]; |
| 323 | int a[128]; |
| @@ -331,14 +423,15 @@ | |
| 331 | /* Check to see if an ADMIN user is trying to edit a SETUP account. |
| 332 | ** Don't allow that. |
| 333 | */ |
| 334 | zId = PD("id", "0"); |
| 335 | uid = atoi(zId); |
| 336 | if( zId && !g.perm.Setup && uid>0 ){ |
| 337 | char *zOldCaps; |
| 338 | zOldCaps = db_text(0, "SELECT cap FROM user WHERE uid=%d",uid); |
| 339 | higherUser = zOldCaps && strchr(zOldCaps,'s'); |
| 340 | } |
| 341 | |
| 342 | if( P("can") ){ |
| 343 | /* User pressed the cancel button */ |
| 344 | cgi_redirect(cgi_referer("setup_ulist")); |
| @@ -393,10 +486,12 @@ | |
| 393 | }else if( !cgi_csrf_safe(2) ){ |
| 394 | /* This might be a cross-site request forgery, so ignore it */ |
| 395 | }else{ |
| 396 | /* We have all the information we need to make the change to the user */ |
| 397 | char c; |
| 398 | char aCap[70], zNm[4]; |
| 399 | zNm[0] = 'a'; |
| 400 | zNm[2] = 0; |
| 401 | for(i=0, c='a'; c<='z'; c++){ |
| 402 | zNm[1] = c; |
| @@ -413,10 +508,11 @@ | |
| 413 | a[c&0x7f] = P(zNm)!=0; |
| 414 | if( a[c&0x7f] ) aCap[i++] = c; |
| 415 | } |
| 416 | |
| 417 | aCap[i] = 0; |
| 418 | zPw = P("pw"); |
| 419 | zLogin = P("login"); |
| 420 | if( strlen(zLogin)==0 ){ |
| 421 | const char *zRef = cgi_referer("setup_ulist"); |
| 422 | style_header("User Creation Error"); |
| @@ -444,15 +540,16 @@ | |
| 444 | style_finish_page(); |
| 445 | return; |
| 446 | } |
| 447 | cgi_csrf_verify(); |
| 448 | db_unprotect(PROTECT_USER); |
| 449 | db_multi_exec( |
| 450 | "REPLACE INTO user(uid,login,info,pw,cap,mtime) " |
| 451 | "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now())", |
| 452 | uid, zLogin, P("info"), zPw, &aCap[0] |
| 453 | ); |
| 454 | if( zOldLogin && fossil_strcmp(zLogin, zOldLogin)!=0 ){ |
| 455 | if( alert_tables_exist() ){ |
| 456 | /* Rename matching subscriber entry, else the user cannot |
| 457 | re-subscribe with their same email address. */ |
| 458 | db_multi_exec("UPDATE subscriber SET suname=%Q WHERE suname=%Q", |
| @@ -460,11 +557,12 @@ | |
| 460 | } |
| 461 | admin_log( "Renamed user [%q] to [%q].", zOldLogin, zLogin ); |
| 462 | } |
| 463 | db_protect_pop(); |
| 464 | setup_incr_cfgcnt(); |
| 465 | admin_log( "Updated user [%q] with capabilities [%q].", |
| 466 | zLogin, &aCap[0] ); |
| 467 | if( atoi(PD("all","0"))>0 ){ |
| 468 | Blob sql; |
| 469 | char *zErr = 0; |
| 470 | blob_zero(&sql); |
| @@ -515,30 +613,36 @@ | |
| 515 | @ <span class="loginError">%h(zErr)</span> |
| 516 | @ |
| 517 | @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)"> |
| 518 | @ [Bummer]</a></p> |
| 519 | style_finish_page(); |
| 520 | return; |
| 521 | } |
| 522 | } |
| 523 | cgi_redirect(cgi_referer("setup_ulist")); |
| 524 | return; |
| 525 | } |
| 526 | |
| 527 | /* Load the existing information about the user, if any |
| 528 | */ |
| 529 | zLogin = ""; |
| 530 | zInfo = ""; |
| 531 | zCap = ""; |
| 532 | zPw = ""; |
| 533 | for(i='a'; i<='z'; i++) oa[i] = ""; |
| 534 | for(i='0'; i<='9'; i++) oa[i] = ""; |
| 535 | for(i='A'; i<='Z'; i++) oa[i] = ""; |
| 536 | if( uid ){ |
| 537 | zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid); |
| 538 | zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid); |
| 539 | zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid); |
| 540 | zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid); |
| 541 | for(i=0; zCap[i]; i++){ |
| 542 | char c = zCap[i]; |
| 543 | if( (c>='a' && c<='z') || (c>='0' && c<='9') || (c>='A' && c<='Z') ){ |
| 544 | oa[c&0x7f] = " checked=\"checked\""; |
| 545 |
| --- src/setupuser.c | |
| +++ src/setupuser.c | |
| @@ -302,10 +302,101 @@ | |
| 302 | if( zPw==0 ) return 0; |
| 303 | if( zPw[0]==0 ) return 1; |
| 304 | while( zPw[0]=='*' ){ zPw++; } |
| 305 | return zPw[0]!=0; |
| 306 | } |
| 307 | |
| 308 | /* |
| 309 | ** Return true if user capability string zNew contains any capability |
| 310 | ** letter which is not in user capability string zOrig, else 0. This |
| 311 | ** does not take inherited permissions into account. Either argument |
| 312 | ** may be NULL. |
| 313 | */ |
| 314 | static int userHasNewCaps(const char *zOrig, const char *zNew){ |
| 315 | for( ; zNew && *zNew; ++zNew ){ |
| 316 | if( !zOrig || strchr(zOrig,*zNew)==0 ){ |
| 317 | return *zNew; |
| 318 | } |
| 319 | } |
| 320 | return 0; |
| 321 | } |
| 322 | |
| 323 | /* |
| 324 | ** Sends notification of user permission elevation changes to all |
| 325 | ** subscribers with a "u" subscription. This is a no-op if alerts are |
| 326 | ** not enabled. |
| 327 | ** |
| 328 | ** These subscriptions differ from most, in that: |
| 329 | ** |
| 330 | ** - They currently lack an "unsubscribe" link. |
| 331 | ** |
| 332 | ** - Only an admin can assign this subscription, but if a non-admin |
| 333 | ** edits their subscriptions after an admin assigns them this one, |
| 334 | ** this particular one will be lost. "Feature or bug?" is unclear, |
| 335 | ** but it would be odd for a non-admin to be assigned this |
| 336 | ** capability. |
| 337 | */ |
| 338 | static void alert_user_elevation(const char *zLogin, /*Affected user*/ |
| 339 | int uid, /*[user].uid*/ |
| 340 | int bIsNew, /*true if new user*/ |
| 341 | const char *zOrigCaps,/*Old caps*/ |
| 342 | const char *zNewCaps /*New caps*/){ |
| 343 | Blob hdr, body; |
| 344 | Stmt q; |
| 345 | int nBody; |
| 346 | AlertSender *pSender; |
| 347 | char *zSubname; |
| 348 | char *zURL; |
| 349 | char * zSubject; |
| 350 | |
| 351 | if( !alert_enabled() ) return; |
| 352 | zSubject = bIsNew |
| 353 | ? mprintf("New user created: [%q]", zLogin) |
| 354 | : mprintf("User [%q] permissions elevated", zLogin); |
| 355 | zURL = db_get("email-url",0); |
| 356 | zSubname = db_get("email-subname", "[Fossil Repo]"); |
| 357 | blob_init(&body, 0, 0); |
| 358 | blob_init(&hdr, 0, 0); |
| 359 | if( bIsNew ){ |
| 360 | blob_appendf(&body, "User [%q] was created by with " |
| 361 | "permissions [%q] by user [%q].\n", |
| 362 | zLogin, zNewCaps, g.zLogin); |
| 363 | } else { |
| 364 | blob_appendf(&body, "Permissions for user [%q] where elevated " |
| 365 | "from [%q] to [%q] by user [%q].\n", |
| 366 | zLogin, zOrigCaps, zNewCaps, g.zLogin); |
| 367 | } |
| 368 | if( zURL ){ |
| 369 | blob_appendf(&body, "\nUser editor: %s/setup_uedit?uid=%d\n", zURL, uid); |
| 370 | } |
| 371 | nBody = blob_size(&body); |
| 372 | pSender = alert_sender_new(0, 0); |
| 373 | db_prepare(&q, |
| 374 | "SELECT semail, hex(subscriberCode)" |
| 375 | " FROM subscriber, user " |
| 376 | " WHERE sverified AND NOT sdonotcall" |
| 377 | " AND suname=login" |
| 378 | " AND ssub GLOB '*u*'"); |
| 379 | while( !pSender->zErr && db_step(&q)==SQLITE_ROW ){ |
| 380 | const char *zTo = db_column_text(&q, 0); |
| 381 | blob_truncate(&hdr, 0); |
| 382 | blob_appendf(&hdr, "To: <%s>\r\nSubject: %s %s\r\n", |
| 383 | zTo, zSubname, zSubject); |
| 384 | if( zURL ){ |
| 385 | const char *zCode = db_column_text(&q, 1); |
| 386 | blob_truncate(&body, nBody); |
| 387 | blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n", |
| 388 | zURL, zCode); |
| 389 | } |
| 390 | alert_send(pSender, &hdr, &body, 0); |
| 391 | } |
| 392 | db_finalize(&q); |
| 393 | alert_sender_free(pSender); |
| 394 | fossil_free(zURL); |
| 395 | fossil_free(zSubname); |
| 396 | fossil_free(zSubject); |
| 397 | } |
| 398 | |
| 399 | /* |
| 400 | ** WEBPAGE: setup_uedit |
| 401 | ** |
| 402 | ** Edit information about a user or create a new user. |
| @@ -314,10 +405,11 @@ | |
| 405 | void user_edit(void){ |
| 406 | const char *zId, *zLogin, *zInfo, *zCap, *zPw; |
| 407 | const char *zGroup; |
| 408 | const char *zOldLogin; |
| 409 | int uid, i; |
| 410 | char *zOldCaps = 0; /* Capabilities before edit */ |
| 411 | char *zDeleteVerify = 0; /* Delete user verification text */ |
| 412 | int higherUser = 0; /* True if user being edited is SETUP and the */ |
| 413 | /* user doing the editing is ADMIN. Disallow editing */ |
| 414 | const char *inherit[128]; |
| 415 | int a[128]; |
| @@ -331,14 +423,15 @@ | |
| 423 | /* Check to see if an ADMIN user is trying to edit a SETUP account. |
| 424 | ** Don't allow that. |
| 425 | */ |
| 426 | zId = PD("id", "0"); |
| 427 | uid = atoi(zId); |
| 428 | if( uid>0 ){ |
| 429 | zOldCaps = db_text("", "SELECT cap FROM user WHERE uid=%d",uid); |
| 430 | if( zId && !g.perm.Setup ){ |
| 431 | higherUser = zOldCaps && strchr(zOldCaps,'s'); |
| 432 | } |
| 433 | } |
| 434 | |
| 435 | if( P("can") ){ |
| 436 | /* User pressed the cancel button */ |
| 437 | cgi_redirect(cgi_referer("setup_ulist")); |
| @@ -393,10 +486,12 @@ | |
| 486 | }else if( !cgi_csrf_safe(2) ){ |
| 487 | /* This might be a cross-site request forgery, so ignore it */ |
| 488 | }else{ |
| 489 | /* We have all the information we need to make the change to the user */ |
| 490 | char c; |
| 491 | int bHasNewCaps = 0 /* 1 if user's permissions are increased */; |
| 492 | const int bIsNew = uid<=0; |
| 493 | char aCap[70], zNm[4]; |
| 494 | zNm[0] = 'a'; |
| 495 | zNm[2] = 0; |
| 496 | for(i=0, c='a'; c<='z'; c++){ |
| 497 | zNm[1] = c; |
| @@ -413,10 +508,11 @@ | |
| 508 | a[c&0x7f] = P(zNm)!=0; |
| 509 | if( a[c&0x7f] ) aCap[i++] = c; |
| 510 | } |
| 511 | |
| 512 | aCap[i] = 0; |
| 513 | bHasNewCaps = bIsNew || userHasNewCaps(zOldCaps, &aCap[0]); |
| 514 | zPw = P("pw"); |
| 515 | zLogin = P("login"); |
| 516 | if( strlen(zLogin)==0 ){ |
| 517 | const char *zRef = cgi_referer("setup_ulist"); |
| 518 | style_header("User Creation Error"); |
| @@ -444,15 +540,16 @@ | |
| 540 | style_finish_page(); |
| 541 | return; |
| 542 | } |
| 543 | cgi_csrf_verify(); |
| 544 | db_unprotect(PROTECT_USER); |
| 545 | uid = db_int(0, |
| 546 | "REPLACE INTO user(uid,login,info,pw,cap,mtime) " |
| 547 | "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now()) " |
| 548 | "RETURNING uid", |
| 549 | uid, zLogin, P("info"), zPw, &aCap[0]); |
| 550 | assert( uid>0 ); |
| 551 | if( zOldLogin && fossil_strcmp(zLogin, zOldLogin)!=0 ){ |
| 552 | if( alert_tables_exist() ){ |
| 553 | /* Rename matching subscriber entry, else the user cannot |
| 554 | re-subscribe with their same email address. */ |
| 555 | db_multi_exec("UPDATE subscriber SET suname=%Q WHERE suname=%Q", |
| @@ -460,11 +557,12 @@ | |
| 557 | } |
| 558 | admin_log( "Renamed user [%q] to [%q].", zOldLogin, zLogin ); |
| 559 | } |
| 560 | db_protect_pop(); |
| 561 | setup_incr_cfgcnt(); |
| 562 | admin_log( "%s user [%q] with capabilities [%q].", |
| 563 | bIsNew ? "Added" : "Updated", |
| 564 | zLogin, &aCap[0] ); |
| 565 | if( atoi(PD("all","0"))>0 ){ |
| 566 | Blob sql; |
| 567 | char *zErr = 0; |
| 568 | blob_zero(&sql); |
| @@ -515,30 +613,36 @@ | |
| 613 | @ <span class="loginError">%h(zErr)</span> |
| 614 | @ |
| 615 | @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)"> |
| 616 | @ [Bummer]</a></p> |
| 617 | style_finish_page(); |
| 618 | if( bHasNewCaps ){ |
| 619 | alert_user_elevation(zLogin, uid, bIsNew, zOldCaps, &aCap[0]); |
| 620 | } |
| 621 | return; |
| 622 | } |
| 623 | } |
| 624 | if( bHasNewCaps ){ |
| 625 | alert_user_elevation(zLogin, uid, bIsNew, zOldCaps, &aCap[0]); |
| 626 | } |
| 627 | cgi_redirect(cgi_referer("setup_ulist")); |
| 628 | return; |
| 629 | } |
| 630 | |
| 631 | /* Load the existing information about the user, if any |
| 632 | */ |
| 633 | zLogin = ""; |
| 634 | zInfo = ""; |
| 635 | zCap = zOldCaps; |
| 636 | zPw = ""; |
| 637 | for(i='a'; i<='z'; i++) oa[i] = ""; |
| 638 | for(i='0'; i<='9'; i++) oa[i] = ""; |
| 639 | for(i='A'; i<='Z'; i++) oa[i] = ""; |
| 640 | if( uid ){ |
| 641 | assert( zCap ); |
| 642 | zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid); |
| 643 | zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid); |
| 644 | zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid); |
| 645 | for(i=0; zCap[i]; i++){ |
| 646 | char c = zCap[i]; |
| 647 | if( (c>='a' && c<='z') || (c>='0' && c<='9') || (c>='A' && c<='Z') ){ |
| 648 | oa[c&0x7f] = " checked=\"checked\""; |
| 649 |
+1
| --- src/sqlcmd.c | ||
| +++ src/sqlcmd.c | ||
| @@ -235,10 +235,11 @@ | ||
| 235 | 235 | char *zSql = sqlite3_mprintf("ATTACH %Q AS 'configdb' KEY ''", |
| 236 | 236 | g.zConfigDbName); |
| 237 | 237 | sqlite3_exec(db, zSql, 0, 0, 0); |
| 238 | 238 | sqlite3_free(zSql); |
| 239 | 239 | } |
| 240 | + (void)timeline_query_for_tty(); /* Registers wiki_to_text() as side-effect */ | |
| 240 | 241 | /* Arrange to trace close operations so that static prepared statements |
| 241 | 242 | ** will get cleaned up when the shell closes the database connection */ |
| 242 | 243 | if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE; |
| 243 | 244 | sqlite3_trace_v2(db, mTrace, db_sql_trace, 0); |
| 244 | 245 | db_protect_only(PROTECT_NONE); |
| 245 | 246 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -235,10 +235,11 @@ | |
| 235 | char *zSql = sqlite3_mprintf("ATTACH %Q AS 'configdb' KEY ''", |
| 236 | g.zConfigDbName); |
| 237 | sqlite3_exec(db, zSql, 0, 0, 0); |
| 238 | sqlite3_free(zSql); |
| 239 | } |
| 240 | /* Arrange to trace close operations so that static prepared statements |
| 241 | ** will get cleaned up when the shell closes the database connection */ |
| 242 | if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE; |
| 243 | sqlite3_trace_v2(db, mTrace, db_sql_trace, 0); |
| 244 | db_protect_only(PROTECT_NONE); |
| 245 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -235,10 +235,11 @@ | |
| 235 | char *zSql = sqlite3_mprintf("ATTACH %Q AS 'configdb' KEY ''", |
| 236 | g.zConfigDbName); |
| 237 | sqlite3_exec(db, zSql, 0, 0, 0); |
| 238 | sqlite3_free(zSql); |
| 239 | } |
| 240 | (void)timeline_query_for_tty(); /* Registers wiki_to_text() as side-effect */ |
| 241 | /* Arrange to trace close operations so that static prepared statements |
| 242 | ** will get cleaned up when the shell closes the database connection */ |
| 243 | if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE; |
| 244 | sqlite3_trace_v2(db, mTrace, db_sql_trace, 0); |
| 245 | db_protect_only(PROTECT_NONE); |
| 246 |
+1
-1
| --- src/stash.c | ||
| +++ src/stash.c | ||
| @@ -756,11 +756,11 @@ | ||
| 756 | 756 | DiffConfig DCfg; |
| 757 | 757 | |
| 758 | 758 | if( strstr(zCmd,"show")!=0 || strstr(zCmd,"cat")!=0 ){ |
| 759 | 759 | fBaseline = 1; |
| 760 | 760 | } |
| 761 | - if( find_option("tk",0,0)!=0 ){ | |
| 761 | + if( find_option("tk",0,0)!=0 || gdiff_using_tk(zCmd[0]=='g') ){ | |
| 762 | 762 | db_close(0); |
| 763 | 763 | diff_tk(fBaseline ? "stash show" : "stash diff", 3); |
| 764 | 764 | return; |
| 765 | 765 | } |
| 766 | 766 | diff_options(&DCfg, zCmd[0]=='g', 0); |
| 767 | 767 |
| --- src/stash.c | |
| +++ src/stash.c | |
| @@ -756,11 +756,11 @@ | |
| 756 | DiffConfig DCfg; |
| 757 | |
| 758 | if( strstr(zCmd,"show")!=0 || strstr(zCmd,"cat")!=0 ){ |
| 759 | fBaseline = 1; |
| 760 | } |
| 761 | if( find_option("tk",0,0)!=0 ){ |
| 762 | db_close(0); |
| 763 | diff_tk(fBaseline ? "stash show" : "stash diff", 3); |
| 764 | return; |
| 765 | } |
| 766 | diff_options(&DCfg, zCmd[0]=='g', 0); |
| 767 |
| --- src/stash.c | |
| +++ src/stash.c | |
| @@ -756,11 +756,11 @@ | |
| 756 | DiffConfig DCfg; |
| 757 | |
| 758 | if( strstr(zCmd,"show")!=0 || strstr(zCmd,"cat")!=0 ){ |
| 759 | fBaseline = 1; |
| 760 | } |
| 761 | if( find_option("tk",0,0)!=0 || gdiff_using_tk(zCmd[0]=='g') ){ |
| 762 | db_close(0); |
| 763 | diff_tk(fBaseline ? "stash show" : "stash diff", 3); |
| 764 | return; |
| 765 | } |
| 766 | diff_options(&DCfg, zCmd[0]=='g', 0); |
| 767 |
+1
-1
| --- src/tar.c | ||
| +++ src/tar.c | ||
| @@ -499,11 +499,11 @@ | ||
| 499 | 499 | pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 500 | 500 | if( pManifest ){ |
| 501 | 501 | int flg, eflg = 0; |
| 502 | 502 | mTime = (unsigned)((pManifest->rDate - 2440587.5)*86400.0); |
| 503 | 503 | if( pTar ) tar_begin(mTime); |
| 504 | - flg = db_get_manifest_setting(); | |
| 504 | + flg = db_get_manifest_setting(blob_str(&hash)); | |
| 505 | 505 | if( flg ){ |
| 506 | 506 | /* eflg is the effective flags, taking include/exclude into account */ |
| 507 | 507 | if( (pInclude==0 || glob_match(pInclude, "manifest")) |
| 508 | 508 | && !glob_match(pExclude, "manifest") |
| 509 | 509 | && (flg & MFESTFLG_RAW) ){ |
| 510 | 510 |
| --- src/tar.c | |
| +++ src/tar.c | |
| @@ -499,11 +499,11 @@ | |
| 499 | pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 500 | if( pManifest ){ |
| 501 | int flg, eflg = 0; |
| 502 | mTime = (unsigned)((pManifest->rDate - 2440587.5)*86400.0); |
| 503 | if( pTar ) tar_begin(mTime); |
| 504 | flg = db_get_manifest_setting(); |
| 505 | if( flg ){ |
| 506 | /* eflg is the effective flags, taking include/exclude into account */ |
| 507 | if( (pInclude==0 || glob_match(pInclude, "manifest")) |
| 508 | && !glob_match(pExclude, "manifest") |
| 509 | && (flg & MFESTFLG_RAW) ){ |
| 510 |
| --- src/tar.c | |
| +++ src/tar.c | |
| @@ -499,11 +499,11 @@ | |
| 499 | pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 500 | if( pManifest ){ |
| 501 | int flg, eflg = 0; |
| 502 | mTime = (unsigned)((pManifest->rDate - 2440587.5)*86400.0); |
| 503 | if( pTar ) tar_begin(mTime); |
| 504 | flg = db_get_manifest_setting(blob_str(&hash)); |
| 505 | if( flg ){ |
| 506 | /* eflg is the effective flags, taking include/exclude into account */ |
| 507 | if( (pInclude==0 || glob_match(pInclude, "manifest")) |
| 508 | && !glob_match(pExclude, "manifest") |
| 509 | && (flg & MFESTFLG_RAW) ){ |
| 510 |
+12
-3
| --- src/terminal.c | ||
| +++ src/terminal.c | ||
| @@ -60,31 +60,40 @@ | ||
| 60 | 60 | memset(t, 0, sizeof(*t)); |
| 61 | 61 | |
| 62 | 62 | #if defined(TIOCGSIZE) |
| 63 | 63 | { |
| 64 | 64 | struct ttysize ts; |
| 65 | - if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)!=-1 ){ | |
| 65 | + if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)>=0 | |
| 66 | + || ioctl(STDOUT_FILENO, TIOCGSIZE, &ts)>=0 | |
| 67 | + || ioctl(STDERR_FILENO, TIOCGSIZE, &ts)>=0 | |
| 68 | + ){ | |
| 66 | 69 | t->nColumns = ts.ts_cols; |
| 67 | 70 | t->nLines = ts.ts_lines; |
| 68 | 71 | return 1; |
| 69 | 72 | } |
| 70 | 73 | return 0; |
| 71 | 74 | } |
| 72 | 75 | #elif defined(TIOCGWINSZ) |
| 73 | 76 | { |
| 74 | 77 | struct winsize ws; |
| 75 | - if( ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)!=-1 ){ | |
| 78 | + if( ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)>=0 | |
| 79 | + || ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws)>=0 | |
| 80 | + || ioctl(STDERR_FILENO, TIOCGWINSZ, &ws)>=0 | |
| 81 | + ){ | |
| 76 | 82 | t->nColumns = ws.ws_col; |
| 77 | 83 | t->nLines = ws.ws_row; |
| 78 | 84 | return 1; |
| 79 | 85 | } |
| 80 | 86 | return 0; |
| 81 | 87 | } |
| 82 | 88 | #elif defined(_WIN32) |
| 83 | 89 | { |
| 84 | 90 | CONSOLE_SCREEN_BUFFER_INFO csbi; |
| 85 | - if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) ){ | |
| 91 | + if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) | |
| 92 | + || GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &csbi) | |
| 93 | + || GetConsoleScreenBufferInfo(GetStdHandle(STD_INPUT_HANDLE), &csbi) | |
| 94 | + ){ | |
| 86 | 95 | t->nColumns = csbi.srWindow.Right - csbi.srWindow.Left + 1; |
| 87 | 96 | t->nLines = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; |
| 88 | 97 | return 1; |
| 89 | 98 | } |
| 90 | 99 | return 0; |
| 91 | 100 |
| --- src/terminal.c | |
| +++ src/terminal.c | |
| @@ -60,31 +60,40 @@ | |
| 60 | memset(t, 0, sizeof(*t)); |
| 61 | |
| 62 | #if defined(TIOCGSIZE) |
| 63 | { |
| 64 | struct ttysize ts; |
| 65 | if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)!=-1 ){ |
| 66 | t->nColumns = ts.ts_cols; |
| 67 | t->nLines = ts.ts_lines; |
| 68 | return 1; |
| 69 | } |
| 70 | return 0; |
| 71 | } |
| 72 | #elif defined(TIOCGWINSZ) |
| 73 | { |
| 74 | struct winsize ws; |
| 75 | if( ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)!=-1 ){ |
| 76 | t->nColumns = ws.ws_col; |
| 77 | t->nLines = ws.ws_row; |
| 78 | return 1; |
| 79 | } |
| 80 | return 0; |
| 81 | } |
| 82 | #elif defined(_WIN32) |
| 83 | { |
| 84 | CONSOLE_SCREEN_BUFFER_INFO csbi; |
| 85 | if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) ){ |
| 86 | t->nColumns = csbi.srWindow.Right - csbi.srWindow.Left + 1; |
| 87 | t->nLines = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; |
| 88 | return 1; |
| 89 | } |
| 90 | return 0; |
| 91 |
| --- src/terminal.c | |
| +++ src/terminal.c | |
| @@ -60,31 +60,40 @@ | |
| 60 | memset(t, 0, sizeof(*t)); |
| 61 | |
| 62 | #if defined(TIOCGSIZE) |
| 63 | { |
| 64 | struct ttysize ts; |
| 65 | if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)>=0 |
| 66 | || ioctl(STDOUT_FILENO, TIOCGSIZE, &ts)>=0 |
| 67 | || ioctl(STDERR_FILENO, TIOCGSIZE, &ts)>=0 |
| 68 | ){ |
| 69 | t->nColumns = ts.ts_cols; |
| 70 | t->nLines = ts.ts_lines; |
| 71 | return 1; |
| 72 | } |
| 73 | return 0; |
| 74 | } |
| 75 | #elif defined(TIOCGWINSZ) |
| 76 | { |
| 77 | struct winsize ws; |
| 78 | if( ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)>=0 |
| 79 | || ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws)>=0 |
| 80 | || ioctl(STDERR_FILENO, TIOCGWINSZ, &ws)>=0 |
| 81 | ){ |
| 82 | t->nColumns = ws.ws_col; |
| 83 | t->nLines = ws.ws_row; |
| 84 | return 1; |
| 85 | } |
| 86 | return 0; |
| 87 | } |
| 88 | #elif defined(_WIN32) |
| 89 | { |
| 90 | CONSOLE_SCREEN_BUFFER_INFO csbi; |
| 91 | if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) |
| 92 | || GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &csbi) |
| 93 | || GetConsoleScreenBufferInfo(GetStdHandle(STD_INPUT_HANDLE), &csbi) |
| 94 | ){ |
| 95 | t->nColumns = csbi.srWindow.Right - csbi.srWindow.Left + 1; |
| 96 | t->nLines = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; |
| 97 | return 1; |
| 98 | } |
| 99 | return 0; |
| 100 |
+183
-60
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -221,11 +221,28 @@ | ||
| 221 | 221 | vid = db_lget_int("checkout", 0); |
| 222 | 222 | } |
| 223 | 223 | zPrevDate[0] = 0; |
| 224 | 224 | mxWikiLen = db_get_int("timeline-max-comment", 0); |
| 225 | 225 | dateFormat = db_get_int("timeline-date-format", 0); |
| 226 | + /* | |
| 227 | + ** SETTING: timeline-truncate-at-blank boolean default=off | |
| 228 | + ** | |
| 229 | + ** If enabled, check-in comments displayed on the timeline are truncated | |
| 230 | + ** at the first blank line of the comment text. The comment text after | |
| 231 | + ** the first blank line is only seen in the /info or similar pages that | |
| 232 | + ** show details about the check-in. | |
| 233 | + */ | |
| 226 | 234 | bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0); |
| 235 | + /* | |
| 236 | + ** SETTING: timeline-tslink-info boolean default=off | |
| 237 | + ** | |
| 238 | + ** The hyperlink on the timestamp associated with each timeline entry, | |
| 239 | + ** on the far left-hand side of the screen, normally targets another | |
| 240 | + ** /timeline page that shows the entry in context. However, if this | |
| 241 | + ** option is turned on, that hyperlink targets the /info page showing | |
| 242 | + ** the details of the entry. | |
| 243 | + */ | |
| 227 | 244 | bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0); |
| 228 | 245 | if( (tmFlags & TIMELINE_VIEWS)==0 ){ |
| 229 | 246 | tmFlags |= timeline_ss_cookie(); |
| 230 | 247 | } |
| 231 | 248 | if( tmFlags & TIMELINE_COLUMNAR ){ |
| @@ -1092,24 +1109,39 @@ | ||
| 1092 | 1109 | } |
| 1093 | 1110 | |
| 1094 | 1111 | /* |
| 1095 | 1112 | ** Convert a symbolic name used as an argument to the a=, b=, or c= |
| 1096 | 1113 | ** query parameters of timeline into a julianday mtime value. |
| 1114 | +** | |
| 1115 | +** If pzDisplay is not null, then display text for the symbolic name might | |
| 1116 | +** be written into *pzDisplay. But that is not guaranteed. | |
| 1117 | +** | |
| 1118 | +** If bRoundUp is true and the symbolic name is a timestamp with less | |
| 1119 | +** than millisecond resolution, then the timestamp is rounding up to the | |
| 1120 | +** largest millisecond consistent with that timestamp. If bRoundUp is | |
| 1121 | +** false, then the resulting time is obtained by extending the timestamp | |
| 1122 | +** with zeros (hence rounding down). Use bRoundUp==1 if the result | |
| 1123 | +** will be used in mtime<=$RESULT and use bRoundUp==0 if the result | |
| 1124 | +** will be used in mtime>=$RESULT. | |
| 1097 | 1125 | */ |
| 1098 | -double symbolic_name_to_mtime(const char *z, const char **pzDisplay){ | |
| 1126 | +double symbolic_name_to_mtime( | |
| 1127 | + const char *z, /* Input symbolic name */ | |
| 1128 | + const char **pzDisplay, /* Perhaps write display text here, if not NULL */ | |
| 1129 | + int bRoundUp /* Round up if true */ | |
| 1130 | +){ | |
| 1099 | 1131 | double mtime; |
| 1100 | 1132 | int rid; |
| 1101 | 1133 | const char *zDate; |
| 1102 | 1134 | if( z==0 ) return -1.0; |
| 1103 | 1135 | if( fossil_isdate(z) ){ |
| 1104 | 1136 | mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z); |
| 1105 | 1137 | if( mtime>0.0 ) return mtime; |
| 1106 | 1138 | } |
| 1107 | - zDate = fossil_expand_datetime(z, 1); | |
| 1139 | + zDate = fossil_expand_datetime(z, 1, bRoundUp); | |
| 1108 | 1140 | if( zDate!=0 ){ |
| 1109 | 1141 | mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", |
| 1110 | - fossil_roundup_date(zDate)); | |
| 1142 | + bRoundUp ? fossil_roundup_date(zDate) : zDate); | |
| 1111 | 1143 | if( mtime>0.0 ){ |
| 1112 | 1144 | if( pzDisplay ) *pzDisplay = fossil_strdup(zDate); |
| 1113 | 1145 | return mtime; |
| 1114 | 1146 | } |
| 1115 | 1147 | } |
| @@ -1381,11 +1413,11 @@ | ||
| 1381 | 1413 | ** return 0. |
| 1382 | 1414 | */ |
| 1383 | 1415 | static int timeline_endpoint( |
| 1384 | 1416 | int iFrom, /* Starting point */ |
| 1385 | 1417 | const char *zEnd, /* Tag we are searching for */ |
| 1386 | - int bForward /* 1: forwards in time (descendents) 0: backwards */ | |
| 1418 | + int bForward /* 1: forwards in time (descendants) 0: backwards */ | |
| 1387 | 1419 | ){ |
| 1388 | 1420 | int tagId; |
| 1389 | 1421 | int endId = 0; |
| 1390 | 1422 | Stmt q; |
| 1391 | 1423 | int ans = 0; |
| @@ -1513,17 +1545,17 @@ | ||
| 1513 | 1545 | /* |
| 1514 | 1546 | ** COMMAND: test-endpoint |
| 1515 | 1547 | ** |
| 1516 | 1548 | ** Usage: fossil test-endpoint BASE TAG ?OPTIONS? |
| 1517 | 1549 | ** |
| 1518 | -** Show the first check-in with TAG that is a descendent or ancestor | |
| 1519 | -** of BASE. The first descendent checkin is shown by default. Use | |
| 1550 | +** Show the first check-in with TAG that is a descendant or ancestor | |
| 1551 | +** of BASE. The first descendant checkin is shown by default. Use | |
| 1520 | 1552 | ** the --backto to see the first ancestor checkin. |
| 1521 | 1553 | ** |
| 1522 | 1554 | ** Options: |
| 1523 | 1555 | ** |
| 1524 | -** --backto Show ancestor. Others defaults to descendents. | |
| 1556 | +** --backto Show ancestor. Others defaults to descendants. | |
| 1525 | 1557 | */ |
| 1526 | 1558 | void timeline_test_endpoint(void){ |
| 1527 | 1559 | int bForward = find_option("backto",0,0)==0; |
| 1528 | 1560 | int from_rid; |
| 1529 | 1561 | int ans; |
| @@ -1703,11 +1735,11 @@ | ||
| 1703 | 1735 | int to_rid = name_choice("to","to2",&zTo2); /* to= for path timelines */ |
| 1704 | 1736 | int bShort = P("shortest")!=0; /* shortest possible path */ |
| 1705 | 1737 | int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */ |
| 1706 | 1738 | int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */ |
| 1707 | 1739 | int pd_rid; |
| 1708 | - const char *zDPName; /* Value of p=, d=, or dp= params */ | |
| 1740 | + const char *zDPNameP, *zDPNameD; /* Value of p=, d=, or dp= params */ | |
| 1709 | 1741 | double rBefore, rAfter, rCirca; /* Boundary times */ |
| 1710 | 1742 | const char *z; |
| 1711 | 1743 | char *zOlderButton = 0; /* URL for Older button at the bottom */ |
| 1712 | 1744 | char *zOlderButtonLabel = 0; /* Label for the Older Button */ |
| 1713 | 1745 | char *zNewerButton = 0; /* URL for Newer button at the top */ |
| @@ -1770,20 +1802,20 @@ | ||
| 1770 | 1802 | }else{ |
| 1771 | 1803 | nEntry = 50; |
| 1772 | 1804 | } |
| 1773 | 1805 | |
| 1774 | 1806 | /* Query parameters d=, p=, and f= and variants */ |
| 1775 | - p_rid = name_choice("p","p2", &zDPName); | |
| 1776 | - d_rid = name_choice("d","d2", &zDPName); | |
| 1807 | + p_rid = name_choice("p","p2", &zDPNameP); | |
| 1808 | + d_rid = name_choice("d","d2", &zDPNameD); | |
| 1777 | 1809 | z = P("f"); |
| 1778 | 1810 | f_rid = z ? name_to_typed_rid(z,"ci") : 0; |
| 1779 | 1811 | z = P("df"); |
| 1780 | 1812 | if( z && (d_rid = name_to_typed_rid(z,"ci"))!=0 ){ |
| 1781 | 1813 | nEntry = 0; |
| 1782 | 1814 | useDividers = 0; |
| 1783 | 1815 | cgi_replace_query_parameter("d",fossil_strdup(z)); |
| 1784 | - zDPName = z; | |
| 1816 | + zDPNameD = zDPNameP = z; | |
| 1785 | 1817 | } |
| 1786 | 1818 | |
| 1787 | 1819 | /* Undocumented query parameter to set JS mode */ |
| 1788 | 1820 | builtin_set_js_delivery_mode(P("jsmode"),1); |
| 1789 | 1821 | |
| @@ -1803,13 +1835,14 @@ | ||
| 1803 | 1835 | showCherrypicks = 0; |
| 1804 | 1836 | } |
| 1805 | 1837 | |
| 1806 | 1838 | /* To view the timeline, must have permission to read project data. |
| 1807 | 1839 | */ |
| 1808 | - pd_rid = name_choice("dp","dp2",&zDPName); | |
| 1840 | + pd_rid = name_choice("dp","dp2",&zDPNameP); | |
| 1809 | 1841 | if( pd_rid ){ |
| 1810 | 1842 | p_rid = d_rid = pd_rid; |
| 1843 | + zDPNameD = zDPNameP; | |
| 1811 | 1844 | } |
| 1812 | 1845 | if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum) |
| 1813 | 1846 | || (bisectLocal && !g.perm.Setup) |
| 1814 | 1847 | ){ |
| 1815 | 1848 | login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); |
| @@ -2226,37 +2259,47 @@ | ||
| 2226 | 2259 | } |
| 2227 | 2260 | } |
| 2228 | 2261 | } |
| 2229 | 2262 | addFileGlobDescription(zChng, &desc); |
| 2230 | 2263 | }else if( (p_rid || d_rid) && g.perm.Read && zTagSql==0 ){ |
| 2231 | - /* If p= or d= is present, ignore all other parameters other than n= */ | |
| 2232 | - char *zUuid; | |
| 2233 | - const char *zCiName; | |
| 2264 | + /* If either p= or d= or both are present, ignore all other parameters | |
| 2265 | + ** other than n=, ft=, and bt= */ | |
| 2266 | + const char *zBaseName = 0; | |
| 2234 | 2267 | int np = 0, nd; |
| 2235 | 2268 | const char *zBackTo = 0; |
| 2236 | 2269 | const char *zFwdTo = 0; |
| 2237 | 2270 | int ridBackTo = 0; |
| 2238 | 2271 | int ridFwdTo = 0; |
| 2272 | + int bBackAdded = 0; /* True if the zBackTo node was added */ | |
| 2273 | + int bFwdAdded = 0; /* True if the zBackTo node was added */ | |
| 2274 | + int bSeparateDandP = 0; /* p_rid & d_rid both exist and are distinct */ | |
| 2239 | 2275 | |
| 2240 | 2276 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| 2241 | - if( p_rid && d_rid ){ | |
| 2242 | - if( p_rid!=d_rid ) p_rid = d_rid; | |
| 2243 | - if( !haveParameterN ) nEntry = 10; | |
| 2277 | + if( p_rid && d_rid && p_rid!=d_rid ){ | |
| 2278 | + bSeparateDandP = 1; | |
| 2279 | + db_multi_exec( | |
| 2280 | + "CREATE TEMP TABLE IF NOT EXISTS ok_d(rid INTEGER PRIMARY KEY)" | |
| 2281 | + ); | |
| 2282 | + }else{ | |
| 2283 | + zBaseName = p_rid ? zDPNameP : zDPNameD; | |
| 2244 | 2284 | } |
| 2245 | 2285 | db_multi_exec( |
| 2246 | - "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)" | |
| 2286 | + "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)" | |
| 2247 | 2287 | ); |
| 2248 | 2288 | add_extra_rids("ok", P("x")); |
| 2249 | - zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", | |
| 2250 | - p_rid ? p_rid : d_rid); | |
| 2251 | - zCiName = zDPName; | |
| 2252 | - if( zCiName==0 ) zCiName = zUuid; | |
| 2253 | 2289 | blob_append_sql(&sql, " AND event.objid IN ok"); |
| 2254 | 2290 | nd = 0; |
| 2255 | 2291 | if( d_rid ){ |
| 2256 | 2292 | double rStopTime = 9e99; |
| 2257 | 2293 | zFwdTo = P("ft"); |
| 2294 | + if( zFwdTo && bSeparateDandP ){ | |
| 2295 | + if( zError==0 ){ | |
| 2296 | + zError = "Cannot use the ft= query parameter when both p= and d= " | |
| 2297 | + "are used and have distinct values."; | |
| 2298 | + } | |
| 2299 | + zFwdTo = 0; | |
| 2300 | + } | |
| 2258 | 2301 | if( zFwdTo ){ |
| 2259 | 2302 | double rStartDate = mtime_of_rid(d_rid, 0.0); |
| 2260 | 2303 | ridFwdTo = first_checkin_with_tag_after_date(zFwdTo, rStartDate); |
| 2261 | 2304 | if( ridFwdTo==0 ){ |
| 2262 | 2305 | ridFwdTo = name_to_typed_rid(zBackTo,"ci"); |
| @@ -2263,10 +2306,13 @@ | ||
| 2263 | 2306 | } |
| 2264 | 2307 | if( ridFwdTo ){ |
| 2265 | 2308 | if( !haveParameterN ) nEntry = 0; |
| 2266 | 2309 | rStopTime = mtime_of_rid(ridFwdTo, 9e99); |
| 2267 | 2310 | } |
| 2311 | + }else if( bSeparateDandP ){ | |
| 2312 | + rStopTime = mtime_of_rid(p_rid, 9e99); | |
| 2313 | + nEntry = 0; | |
| 2268 | 2314 | } |
| 2269 | 2315 | if( rStopTime<9e99 ){ |
| 2270 | 2316 | rStopTime += 5.8e-6; /* Round up by 1/2 second */ |
| 2271 | 2317 | } |
| 2272 | 2318 | db_multi_exec( |
| @@ -2279,71 +2325,111 @@ | ||
| 2279 | 2325 | " ORDER BY 2\n" |
| 2280 | 2326 | ")\n" |
| 2281 | 2327 | "INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d", |
| 2282 | 2328 | d_rid, rStopTime<8e99 ? 17 : 2, rStopTime, nEntry<=0 ? -1 : nEntry+1 |
| 2283 | 2329 | ); |
| 2284 | - nd = db_int(0, "SELECT count(*)-1 FROM ok"); | |
| 2285 | - if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql)); | |
| 2286 | - if( nd>0 || p_rid==0 ){ | |
| 2287 | - blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s"); | |
| 2330 | + if( ridFwdTo && !db_exists("SELECT 1 FROM ok WHERE rid=%d",ridFwdTo) ){ | |
| 2331 | + db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridFwdTo); | |
| 2332 | + bFwdAdded = 1; | |
| 2288 | 2333 | } |
| 2289 | - if( useDividers && !selectedRid ) selectedRid = d_rid; | |
| 2290 | - db_multi_exec("DELETE FROM ok"); | |
| 2334 | + if( bSeparateDandP ){ | |
| 2335 | + db_multi_exec( | |
| 2336 | + "INSERT INTO ok_d SELECT rid FROM ok;" | |
| 2337 | + "DELETE FROM ok;" | |
| 2338 | + ); | |
| 2339 | + }else{ | |
| 2340 | + nd = db_int(0, "SELECT count(*)-1 FROM ok"); | |
| 2341 | + if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql)); | |
| 2342 | + if( nd>0 || p_rid==0 ){ | |
| 2343 | + blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s"); | |
| 2344 | + } | |
| 2345 | + if( useDividers && !selectedRid ) selectedRid = d_rid; | |
| 2346 | + db_multi_exec("DELETE FROM ok"); | |
| 2347 | + } | |
| 2291 | 2348 | } |
| 2292 | 2349 | if( p_rid ){ |
| 2293 | 2350 | zBackTo = P("bt"); |
| 2351 | + if( zBackTo && bSeparateDandP ){ | |
| 2352 | + if( zError==0 ){ | |
| 2353 | + zError = "Cannot use the bt= query parameter when both p= and d= " | |
| 2354 | + "are used and have distinct values."; | |
| 2355 | + } | |
| 2356 | + zBackTo = 0; | |
| 2357 | + } | |
| 2294 | 2358 | if( zBackTo ){ |
| 2295 | 2359 | double rDateLimit = mtime_of_rid(p_rid, 0.0); |
| 2296 | 2360 | ridBackTo = last_checkin_with_tag_before_date(zBackTo, rDateLimit); |
| 2297 | 2361 | if( ridBackTo==0 ){ |
| 2298 | 2362 | ridBackTo = name_to_typed_rid(zBackTo,"ci"); |
| 2299 | 2363 | } |
| 2300 | 2364 | if( ridBackTo && !haveParameterN ) nEntry = 0; |
| 2365 | + }else if( bSeparateDandP ){ | |
| 2366 | + ridBackTo = d_rid; | |
| 2367 | + nEntry = 0; | |
| 2301 | 2368 | } |
| 2302 | 2369 | compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo); |
| 2303 | - np = db_int(0, "SELECT count(*)-1 FROM ok"); | |
| 2304 | - if( np>0 || nd==0 ){ | |
| 2305 | - if( nd>0 ) blob_appendf(&desc, " and "); | |
| 2306 | - blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s"); | |
| 2370 | + if( ridBackTo && !db_exists("SELECT 1 FROM ok WHERE rid=%d",ridBackTo) ){ | |
| 2371 | + db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridBackTo); | |
| 2372 | + bBackAdded = 1; | |
| 2373 | + } | |
| 2374 | + if( bSeparateDandP ){ | |
| 2375 | + db_multi_exec("DELETE FROM ok WHERE rid NOT IN ok_d;"); | |
| 2307 | 2376 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 2377 | + }else{ | |
| 2378 | + np = db_int(0, "SELECT count(*)-1 FROM ok"); | |
| 2379 | + if( np>0 || nd==0 ){ | |
| 2380 | + if( nd>0 ) blob_appendf(&desc, " and "); | |
| 2381 | + blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s"); | |
| 2382 | + db_multi_exec("%s", blob_sql_text(&sql)); | |
| 2383 | + } | |
| 2384 | + if( useDividers && !selectedRid ) selectedRid = p_rid; | |
| 2308 | 2385 | } |
| 2309 | - if( useDividers && !selectedRid ) selectedRid = p_rid; | |
| 2310 | 2386 | } |
| 2311 | - | |
| 2312 | - blob_appendf(&desc, " of %z%h</a>", | |
| 2313 | - href("%R/info?name=%h", zCiName), zCiName); | |
| 2387 | + if( bSeparateDandP ){ | |
| 2388 | + int n = db_int(0, "SELECT count(*) FROM ok"); | |
| 2389 | + blob_reset(&desc); | |
| 2390 | + blob_appendf(&desc, | |
| 2391 | + "%d check-ins that are both ancestors of %z%h</a>" | |
| 2392 | + " and descendants of %z%h</a>", | |
| 2393 | + n, | |
| 2394 | + href("%R/info?name=%h",zDPNameP),zDPNameP, | |
| 2395 | + href("%R/info?name=%h",zDPNameD),zDPNameD | |
| 2396 | + ); | |
| 2397 | + ridBackTo = 0; | |
| 2398 | + ridFwdTo = 0; | |
| 2399 | + }else{ | |
| 2400 | + blob_appendf(&desc, " of %z%h</a>", | |
| 2401 | + href("%R/info?name=%h", zBaseName), zBaseName); | |
| 2402 | + } | |
| 2314 | 2403 | if( ridBackTo ){ |
| 2315 | 2404 | if( np==0 ){ |
| 2316 | 2405 | blob_reset(&desc); |
| 2317 | 2406 | blob_appendf(&desc, |
| 2318 | - "Check-in %z%h</a> only (%z%h</a> is not an ancestor)", | |
| 2319 | - href("%R/info?name=%h",zCiName), zCiName, | |
| 2407 | + "Check-in %z%h</a> only (%z%h</a> does not precede it)", | |
| 2408 | + href("%R/info?name=%h",zBaseName), zBaseName, | |
| 2320 | 2409 | href("%R/info?name=%h",zBackTo), zBackTo); |
| 2321 | 2410 | }else{ |
| 2322 | - blob_appendf(&desc, " back to %z%h</a>", | |
| 2323 | - href("%R/info?name=%h",zBackTo), zBackTo); | |
| 2411 | + blob_appendf(&desc, " back to %z%h</a>%s", | |
| 2412 | + href("%R/info?name=%h",zBackTo), zBackTo, | |
| 2413 | + bBackAdded ? " (not a direct anscestor)" : ""); | |
| 2324 | 2414 | if( ridFwdTo && zFwdTo ){ |
| 2325 | - blob_appendf(&desc, " and up to %z%h</a>", | |
| 2326 | - href("%R/info?name=%h",zFwdTo), zFwdTo); | |
| 2415 | + blob_appendf(&desc, " and up to %z%h</a>%s", | |
| 2416 | + href("%R/info?name=%h",zFwdTo), zFwdTo, | |
| 2417 | + bFwdAdded ? " (not a direct descendant)" : ""); | |
| 2327 | 2418 | } |
| 2328 | 2419 | } |
| 2329 | 2420 | }else if( ridFwdTo ){ |
| 2330 | 2421 | if( nd==0 ){ |
| 2331 | 2422 | blob_reset(&desc); |
| 2332 | 2423 | blob_appendf(&desc, |
| 2333 | - "Check-in %z%h</a> only (%z%h</a> is not an descendant)", | |
| 2334 | - href("%R/info?name=%h",zCiName), zCiName, | |
| 2424 | + "Check-in %z%h</a> only (%z%h</a> does not follow it)", | |
| 2425 | + href("%R/info?name=%h",zBaseName), zBaseName, | |
| 2335 | 2426 | href("%R/info?name=%h",zFwdTo), zFwdTo); |
| 2336 | 2427 | }else{ |
| 2337 | - blob_appendf(&desc, " up to %z%h</a>", | |
| 2338 | - href("%R/info?name=%h",zFwdTo), zFwdTo); | |
| 2339 | - } | |
| 2340 | - } | |
| 2341 | - if( d_rid ){ | |
| 2342 | - if( p_rid ){ | |
| 2343 | - /* If both p= and d= are set, we don't have the uuid of d yet. */ | |
| 2344 | - zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", d_rid); | |
| 2428 | + blob_appendf(&desc, " up to %z%h</a>%s", | |
| 2429 | + href("%R/info?name=%h",zFwdTo), zFwdTo, | |
| 2430 | + bFwdAdded ? " (not a direct descendant)":""); | |
| 2345 | 2431 | } |
| 2346 | 2432 | } |
| 2347 | 2433 | if( advancedMenu ){ |
| 2348 | 2434 | style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0); |
| 2349 | 2435 | } |
| @@ -2813,13 +2899,13 @@ | ||
| 2813 | 2899 | blob_append_sql(&cond, |
| 2814 | 2900 | " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')", |
| 2815 | 2901 | zSearch, zSearch); |
| 2816 | 2902 | } |
| 2817 | 2903 | } |
| 2818 | - rBefore = symbolic_name_to_mtime(zBefore, &zBefore); | |
| 2819 | - rAfter = symbolic_name_to_mtime(zAfter, &zAfter); | |
| 2820 | - rCirca = symbolic_name_to_mtime(zCirca, &zCirca); | |
| 2904 | + rBefore = symbolic_name_to_mtime(zBefore, &zBefore, 1); | |
| 2905 | + rAfter = symbolic_name_to_mtime(zAfter, &zAfter, 0); | |
| 2906 | + rCirca = symbolic_name_to_mtime(zCirca, &zCirca, 0); | |
| 2821 | 2907 | blob_append_sql(&sql, "%s", blob_sql_text(&cond)); |
| 2822 | 2908 | if( rAfter>0.0 ){ |
| 2823 | 2909 | if( rBefore>0.0 ){ |
| 2824 | 2910 | blob_append_sql(&sql, |
| 2825 | 2911 | " AND event.mtime>=%.17g AND event.mtime<=%.17g\n" |
| @@ -2956,11 +3042,11 @@ | ||
| 2956 | 3042 | zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/"); |
| 2957 | 3043 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 2958 | 3044 | zDate = mprintf("%s", (zAfter ? zAfter : zBefore)); |
| 2959 | 3045 | } |
| 2960 | 3046 | if( zDate ){ |
| 2961 | - rDate = symbolic_name_to_mtime(zDate, 0); | |
| 3047 | + rDate = symbolic_name_to_mtime(zDate, 0, 0); | |
| 2962 | 3048 | if( db_int(0, |
| 2963 | 3049 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2964 | 3050 | " WHERE blob.rid=event.objid AND mtime<=%.17g%s)", |
| 2965 | 3051 | rDate-ONE_SECOND, blob_sql_text(&cond)) |
| 2966 | 3052 | ){ |
| @@ -2972,11 +3058,11 @@ | ||
| 2972 | 3058 | zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/"); |
| 2973 | 3059 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 2974 | 3060 | zDate = mprintf("%s", (zBefore ? zBefore : zAfter)); |
| 2975 | 3061 | } |
| 2976 | 3062 | if( zDate ){ |
| 2977 | - rDate = symbolic_name_to_mtime(zDate, 0); | |
| 3063 | + rDate = symbolic_name_to_mtime(zDate, 0, 0); | |
| 2978 | 3064 | if( db_int(0, |
| 2979 | 3065 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2980 | 3066 | " WHERE blob.rid=event.objid AND mtime>=%.17g%s)", |
| 2981 | 3067 | rDate+ONE_SECOND, blob_sql_text(&cond)) |
| 2982 | 3068 | ){ |
| @@ -3015,11 +3101,11 @@ | ||
| 3015 | 3101 | style_submenu_element("Advanced", "%s", |
| 3016 | 3102 | url_render(&url, "advm", "1", "udc", "1")); |
| 3017 | 3103 | } |
| 3018 | 3104 | if( PB("showid") ) tmFlags |= TIMELINE_SHOWRID; |
| 3019 | 3105 | if( useDividers && zMark && zMark[0] ){ |
| 3020 | - double r = symbolic_name_to_mtime(zMark, 0); | |
| 3106 | + double r = symbolic_name_to_mtime(zMark, 0, 0); | |
| 3021 | 3107 | if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r); |
| 3022 | 3108 | } |
| 3023 | 3109 | blob_zero(&sql); |
| 3024 | 3110 | if( PB("oldestfirst") ){ |
| 3025 | 3111 | db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby ASC /*scan*/"); |
| @@ -3381,22 +3467,54 @@ | ||
| 3381 | 3467 | fossil_print("+++ no more data (%d) +++\n", nEntry); |
| 3382 | 3468 | } |
| 3383 | 3469 | } |
| 3384 | 3470 | if( fchngQueryInit ) db_finalize(&fchngQuery); |
| 3385 | 3471 | } |
| 3472 | + | |
| 3473 | +/* | |
| 3474 | +** wiki_to_text(TEXT) | |
| 3475 | +** | |
| 3476 | +** Return a plain-text rendering of Fossil-Wiki TEXT. | |
| 3477 | +*/ | |
| 3478 | +static void wiki_to_text_sqlfunc( | |
| 3479 | + sqlite3_context *context, | |
| 3480 | + int argc, | |
| 3481 | + sqlite3_value **argv | |
| 3482 | +){ | |
| 3483 | + const char *zIn, *zOut; | |
| 3484 | + int nIn, nOut; | |
| 3485 | + Blob in, html, txt; | |
| 3486 | + zIn = (const char*)sqlite3_value_text(argv[0]); | |
| 3487 | + if( zIn==0 ) return; | |
| 3488 | + nIn = sqlite3_value_bytes(argv[0]); | |
| 3489 | + blob_init(&in, zIn, nIn); | |
| 3490 | + blob_init(&html, 0, 0); | |
| 3491 | + wiki_convert(&in, &html, WIKI_INLINE); | |
| 3492 | + blob_reset(&in); | |
| 3493 | + blob_init(&txt, 0, 0); | |
| 3494 | + html_to_plaintext(blob_str(&html), &txt); | |
| 3495 | + blob_reset(&html); | |
| 3496 | + nOut = blob_size(&txt); | |
| 3497 | + zOut = blob_str(&txt); | |
| 3498 | + while( fossil_isspace(zOut[0]) ){ zOut++; nOut--; } | |
| 3499 | + while( nOut>0 && fossil_isspace(zOut[nOut-1]) ){ nOut--; } | |
| 3500 | + sqlite3_result_text(context, zOut, nOut, SQLITE_TRANSIENT); | |
| 3501 | + blob_reset(&txt); | |
| 3502 | +} | |
| 3386 | 3503 | |
| 3387 | 3504 | /* |
| 3388 | 3505 | ** Return a pointer to a static string that forms the basis for |
| 3389 | 3506 | ** a timeline query for display on a TTY. |
| 3390 | 3507 | */ |
| 3391 | 3508 | const char *timeline_query_for_tty(void){ |
| 3509 | + static int once = 0; | |
| 3392 | 3510 | static const char zBaseSql[] = |
| 3393 | 3511 | @ SELECT |
| 3394 | 3512 | @ blob.rid AS rid, |
| 3395 | 3513 | @ uuid, |
| 3396 | 3514 | @ datetime(event.mtime,toLocal()) AS mDateTime, |
| 3397 | - @ coalesce(ecomment,comment) | |
| 3515 | + @ wiki_to_text(coalesce(ecomment,comment)) | |
| 3398 | 3516 | @ || ' (user: ' || coalesce(euser,user,'?') |
| 3399 | 3517 | @ || (SELECT case when length(x)>0 then ' tags: ' || x else '' end |
| 3400 | 3518 | @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x |
| 3401 | 3519 | @ FROM tag, tagxref |
| 3402 | 3520 | @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid |
| @@ -3420,10 +3538,15 @@ | ||
| 3420 | 3538 | @ AND tagxref.tagtype>0 |
| 3421 | 3539 | @ AND tagxref.rid=blob.rid |
| 3422 | 3540 | @ WHERE blob.rid=event.objid |
| 3423 | 3541 | @ AND tag.tagname='branch' |
| 3424 | 3542 | ; |
| 3543 | + if( !once && g.db ){ | |
| 3544 | + once = 1; | |
| 3545 | + sqlite3_create_function(g.db, "wiki_to_text", 1, SQLITE_UTF8, 0, | |
| 3546 | + wiki_to_text_sqlfunc, 0, 0); | |
| 3547 | + } | |
| 3425 | 3548 | return zBaseSql; |
| 3426 | 3549 | } |
| 3427 | 3550 | |
| 3428 | 3551 | /* |
| 3429 | 3552 | ** Return true if the input string is a date in the ISO 8601 format: |
| 3430 | 3553 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -221,11 +221,28 @@ | |
| 221 | vid = db_lget_int("checkout", 0); |
| 222 | } |
| 223 | zPrevDate[0] = 0; |
| 224 | mxWikiLen = db_get_int("timeline-max-comment", 0); |
| 225 | dateFormat = db_get_int("timeline-date-format", 0); |
| 226 | bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0); |
| 227 | bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0); |
| 228 | if( (tmFlags & TIMELINE_VIEWS)==0 ){ |
| 229 | tmFlags |= timeline_ss_cookie(); |
| 230 | } |
| 231 | if( tmFlags & TIMELINE_COLUMNAR ){ |
| @@ -1092,24 +1109,39 @@ | |
| 1092 | } |
| 1093 | |
| 1094 | /* |
| 1095 | ** Convert a symbolic name used as an argument to the a=, b=, or c= |
| 1096 | ** query parameters of timeline into a julianday mtime value. |
| 1097 | */ |
| 1098 | double symbolic_name_to_mtime(const char *z, const char **pzDisplay){ |
| 1099 | double mtime; |
| 1100 | int rid; |
| 1101 | const char *zDate; |
| 1102 | if( z==0 ) return -1.0; |
| 1103 | if( fossil_isdate(z) ){ |
| 1104 | mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z); |
| 1105 | if( mtime>0.0 ) return mtime; |
| 1106 | } |
| 1107 | zDate = fossil_expand_datetime(z, 1); |
| 1108 | if( zDate!=0 ){ |
| 1109 | mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", |
| 1110 | fossil_roundup_date(zDate)); |
| 1111 | if( mtime>0.0 ){ |
| 1112 | if( pzDisplay ) *pzDisplay = fossil_strdup(zDate); |
| 1113 | return mtime; |
| 1114 | } |
| 1115 | } |
| @@ -1381,11 +1413,11 @@ | |
| 1381 | ** return 0. |
| 1382 | */ |
| 1383 | static int timeline_endpoint( |
| 1384 | int iFrom, /* Starting point */ |
| 1385 | const char *zEnd, /* Tag we are searching for */ |
| 1386 | int bForward /* 1: forwards in time (descendents) 0: backwards */ |
| 1387 | ){ |
| 1388 | int tagId; |
| 1389 | int endId = 0; |
| 1390 | Stmt q; |
| 1391 | int ans = 0; |
| @@ -1513,17 +1545,17 @@ | |
| 1513 | /* |
| 1514 | ** COMMAND: test-endpoint |
| 1515 | ** |
| 1516 | ** Usage: fossil test-endpoint BASE TAG ?OPTIONS? |
| 1517 | ** |
| 1518 | ** Show the first check-in with TAG that is a descendent or ancestor |
| 1519 | ** of BASE. The first descendent checkin is shown by default. Use |
| 1520 | ** the --backto to see the first ancestor checkin. |
| 1521 | ** |
| 1522 | ** Options: |
| 1523 | ** |
| 1524 | ** --backto Show ancestor. Others defaults to descendents. |
| 1525 | */ |
| 1526 | void timeline_test_endpoint(void){ |
| 1527 | int bForward = find_option("backto",0,0)==0; |
| 1528 | int from_rid; |
| 1529 | int ans; |
| @@ -1703,11 +1735,11 @@ | |
| 1703 | int to_rid = name_choice("to","to2",&zTo2); /* to= for path timelines */ |
| 1704 | int bShort = P("shortest")!=0; /* shortest possible path */ |
| 1705 | int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */ |
| 1706 | int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */ |
| 1707 | int pd_rid; |
| 1708 | const char *zDPName; /* Value of p=, d=, or dp= params */ |
| 1709 | double rBefore, rAfter, rCirca; /* Boundary times */ |
| 1710 | const char *z; |
| 1711 | char *zOlderButton = 0; /* URL for Older button at the bottom */ |
| 1712 | char *zOlderButtonLabel = 0; /* Label for the Older Button */ |
| 1713 | char *zNewerButton = 0; /* URL for Newer button at the top */ |
| @@ -1770,20 +1802,20 @@ | |
| 1770 | }else{ |
| 1771 | nEntry = 50; |
| 1772 | } |
| 1773 | |
| 1774 | /* Query parameters d=, p=, and f= and variants */ |
| 1775 | p_rid = name_choice("p","p2", &zDPName); |
| 1776 | d_rid = name_choice("d","d2", &zDPName); |
| 1777 | z = P("f"); |
| 1778 | f_rid = z ? name_to_typed_rid(z,"ci") : 0; |
| 1779 | z = P("df"); |
| 1780 | if( z && (d_rid = name_to_typed_rid(z,"ci"))!=0 ){ |
| 1781 | nEntry = 0; |
| 1782 | useDividers = 0; |
| 1783 | cgi_replace_query_parameter("d",fossil_strdup(z)); |
| 1784 | zDPName = z; |
| 1785 | } |
| 1786 | |
| 1787 | /* Undocumented query parameter to set JS mode */ |
| 1788 | builtin_set_js_delivery_mode(P("jsmode"),1); |
| 1789 | |
| @@ -1803,13 +1835,14 @@ | |
| 1803 | showCherrypicks = 0; |
| 1804 | } |
| 1805 | |
| 1806 | /* To view the timeline, must have permission to read project data. |
| 1807 | */ |
| 1808 | pd_rid = name_choice("dp","dp2",&zDPName); |
| 1809 | if( pd_rid ){ |
| 1810 | p_rid = d_rid = pd_rid; |
| 1811 | } |
| 1812 | if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum) |
| 1813 | || (bisectLocal && !g.perm.Setup) |
| 1814 | ){ |
| 1815 | login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); |
| @@ -2226,37 +2259,47 @@ | |
| 2226 | } |
| 2227 | } |
| 2228 | } |
| 2229 | addFileGlobDescription(zChng, &desc); |
| 2230 | }else if( (p_rid || d_rid) && g.perm.Read && zTagSql==0 ){ |
| 2231 | /* If p= or d= is present, ignore all other parameters other than n= */ |
| 2232 | char *zUuid; |
| 2233 | const char *zCiName; |
| 2234 | int np = 0, nd; |
| 2235 | const char *zBackTo = 0; |
| 2236 | const char *zFwdTo = 0; |
| 2237 | int ridBackTo = 0; |
| 2238 | int ridFwdTo = 0; |
| 2239 | |
| 2240 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| 2241 | if( p_rid && d_rid ){ |
| 2242 | if( p_rid!=d_rid ) p_rid = d_rid; |
| 2243 | if( !haveParameterN ) nEntry = 10; |
| 2244 | } |
| 2245 | db_multi_exec( |
| 2246 | "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)" |
| 2247 | ); |
| 2248 | add_extra_rids("ok", P("x")); |
| 2249 | zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", |
| 2250 | p_rid ? p_rid : d_rid); |
| 2251 | zCiName = zDPName; |
| 2252 | if( zCiName==0 ) zCiName = zUuid; |
| 2253 | blob_append_sql(&sql, " AND event.objid IN ok"); |
| 2254 | nd = 0; |
| 2255 | if( d_rid ){ |
| 2256 | double rStopTime = 9e99; |
| 2257 | zFwdTo = P("ft"); |
| 2258 | if( zFwdTo ){ |
| 2259 | double rStartDate = mtime_of_rid(d_rid, 0.0); |
| 2260 | ridFwdTo = first_checkin_with_tag_after_date(zFwdTo, rStartDate); |
| 2261 | if( ridFwdTo==0 ){ |
| 2262 | ridFwdTo = name_to_typed_rid(zBackTo,"ci"); |
| @@ -2263,10 +2306,13 @@ | |
| 2263 | } |
| 2264 | if( ridFwdTo ){ |
| 2265 | if( !haveParameterN ) nEntry = 0; |
| 2266 | rStopTime = mtime_of_rid(ridFwdTo, 9e99); |
| 2267 | } |
| 2268 | } |
| 2269 | if( rStopTime<9e99 ){ |
| 2270 | rStopTime += 5.8e-6; /* Round up by 1/2 second */ |
| 2271 | } |
| 2272 | db_multi_exec( |
| @@ -2279,71 +2325,111 @@ | |
| 2279 | " ORDER BY 2\n" |
| 2280 | ")\n" |
| 2281 | "INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d", |
| 2282 | d_rid, rStopTime<8e99 ? 17 : 2, rStopTime, nEntry<=0 ? -1 : nEntry+1 |
| 2283 | ); |
| 2284 | nd = db_int(0, "SELECT count(*)-1 FROM ok"); |
| 2285 | if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql)); |
| 2286 | if( nd>0 || p_rid==0 ){ |
| 2287 | blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s"); |
| 2288 | } |
| 2289 | if( useDividers && !selectedRid ) selectedRid = d_rid; |
| 2290 | db_multi_exec("DELETE FROM ok"); |
| 2291 | } |
| 2292 | if( p_rid ){ |
| 2293 | zBackTo = P("bt"); |
| 2294 | if( zBackTo ){ |
| 2295 | double rDateLimit = mtime_of_rid(p_rid, 0.0); |
| 2296 | ridBackTo = last_checkin_with_tag_before_date(zBackTo, rDateLimit); |
| 2297 | if( ridBackTo==0 ){ |
| 2298 | ridBackTo = name_to_typed_rid(zBackTo,"ci"); |
| 2299 | } |
| 2300 | if( ridBackTo && !haveParameterN ) nEntry = 0; |
| 2301 | } |
| 2302 | compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo); |
| 2303 | np = db_int(0, "SELECT count(*)-1 FROM ok"); |
| 2304 | if( np>0 || nd==0 ){ |
| 2305 | if( nd>0 ) blob_appendf(&desc, " and "); |
| 2306 | blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s"); |
| 2307 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 2308 | } |
| 2309 | if( useDividers && !selectedRid ) selectedRid = p_rid; |
| 2310 | } |
| 2311 | |
| 2312 | blob_appendf(&desc, " of %z%h</a>", |
| 2313 | href("%R/info?name=%h", zCiName), zCiName); |
| 2314 | if( ridBackTo ){ |
| 2315 | if( np==0 ){ |
| 2316 | blob_reset(&desc); |
| 2317 | blob_appendf(&desc, |
| 2318 | "Check-in %z%h</a> only (%z%h</a> is not an ancestor)", |
| 2319 | href("%R/info?name=%h",zCiName), zCiName, |
| 2320 | href("%R/info?name=%h",zBackTo), zBackTo); |
| 2321 | }else{ |
| 2322 | blob_appendf(&desc, " back to %z%h</a>", |
| 2323 | href("%R/info?name=%h",zBackTo), zBackTo); |
| 2324 | if( ridFwdTo && zFwdTo ){ |
| 2325 | blob_appendf(&desc, " and up to %z%h</a>", |
| 2326 | href("%R/info?name=%h",zFwdTo), zFwdTo); |
| 2327 | } |
| 2328 | } |
| 2329 | }else if( ridFwdTo ){ |
| 2330 | if( nd==0 ){ |
| 2331 | blob_reset(&desc); |
| 2332 | blob_appendf(&desc, |
| 2333 | "Check-in %z%h</a> only (%z%h</a> is not an descendant)", |
| 2334 | href("%R/info?name=%h",zCiName), zCiName, |
| 2335 | href("%R/info?name=%h",zFwdTo), zFwdTo); |
| 2336 | }else{ |
| 2337 | blob_appendf(&desc, " up to %z%h</a>", |
| 2338 | href("%R/info?name=%h",zFwdTo), zFwdTo); |
| 2339 | } |
| 2340 | } |
| 2341 | if( d_rid ){ |
| 2342 | if( p_rid ){ |
| 2343 | /* If both p= and d= are set, we don't have the uuid of d yet. */ |
| 2344 | zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", d_rid); |
| 2345 | } |
| 2346 | } |
| 2347 | if( advancedMenu ){ |
| 2348 | style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0); |
| 2349 | } |
| @@ -2813,13 +2899,13 @@ | |
| 2813 | blob_append_sql(&cond, |
| 2814 | " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')", |
| 2815 | zSearch, zSearch); |
| 2816 | } |
| 2817 | } |
| 2818 | rBefore = symbolic_name_to_mtime(zBefore, &zBefore); |
| 2819 | rAfter = symbolic_name_to_mtime(zAfter, &zAfter); |
| 2820 | rCirca = symbolic_name_to_mtime(zCirca, &zCirca); |
| 2821 | blob_append_sql(&sql, "%s", blob_sql_text(&cond)); |
| 2822 | if( rAfter>0.0 ){ |
| 2823 | if( rBefore>0.0 ){ |
| 2824 | blob_append_sql(&sql, |
| 2825 | " AND event.mtime>=%.17g AND event.mtime<=%.17g\n" |
| @@ -2956,11 +3042,11 @@ | |
| 2956 | zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/"); |
| 2957 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 2958 | zDate = mprintf("%s", (zAfter ? zAfter : zBefore)); |
| 2959 | } |
| 2960 | if( zDate ){ |
| 2961 | rDate = symbolic_name_to_mtime(zDate, 0); |
| 2962 | if( db_int(0, |
| 2963 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2964 | " WHERE blob.rid=event.objid AND mtime<=%.17g%s)", |
| 2965 | rDate-ONE_SECOND, blob_sql_text(&cond)) |
| 2966 | ){ |
| @@ -2972,11 +3058,11 @@ | |
| 2972 | zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/"); |
| 2973 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 2974 | zDate = mprintf("%s", (zBefore ? zBefore : zAfter)); |
| 2975 | } |
| 2976 | if( zDate ){ |
| 2977 | rDate = symbolic_name_to_mtime(zDate, 0); |
| 2978 | if( db_int(0, |
| 2979 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2980 | " WHERE blob.rid=event.objid AND mtime>=%.17g%s)", |
| 2981 | rDate+ONE_SECOND, blob_sql_text(&cond)) |
| 2982 | ){ |
| @@ -3015,11 +3101,11 @@ | |
| 3015 | style_submenu_element("Advanced", "%s", |
| 3016 | url_render(&url, "advm", "1", "udc", "1")); |
| 3017 | } |
| 3018 | if( PB("showid") ) tmFlags |= TIMELINE_SHOWRID; |
| 3019 | if( useDividers && zMark && zMark[0] ){ |
| 3020 | double r = symbolic_name_to_mtime(zMark, 0); |
| 3021 | if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r); |
| 3022 | } |
| 3023 | blob_zero(&sql); |
| 3024 | if( PB("oldestfirst") ){ |
| 3025 | db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby ASC /*scan*/"); |
| @@ -3381,22 +3467,54 @@ | |
| 3381 | fossil_print("+++ no more data (%d) +++\n", nEntry); |
| 3382 | } |
| 3383 | } |
| 3384 | if( fchngQueryInit ) db_finalize(&fchngQuery); |
| 3385 | } |
| 3386 | |
| 3387 | /* |
| 3388 | ** Return a pointer to a static string that forms the basis for |
| 3389 | ** a timeline query for display on a TTY. |
| 3390 | */ |
| 3391 | const char *timeline_query_for_tty(void){ |
| 3392 | static const char zBaseSql[] = |
| 3393 | @ SELECT |
| 3394 | @ blob.rid AS rid, |
| 3395 | @ uuid, |
| 3396 | @ datetime(event.mtime,toLocal()) AS mDateTime, |
| 3397 | @ coalesce(ecomment,comment) |
| 3398 | @ || ' (user: ' || coalesce(euser,user,'?') |
| 3399 | @ || (SELECT case when length(x)>0 then ' tags: ' || x else '' end |
| 3400 | @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x |
| 3401 | @ FROM tag, tagxref |
| 3402 | @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid |
| @@ -3420,10 +3538,15 @@ | |
| 3420 | @ AND tagxref.tagtype>0 |
| 3421 | @ AND tagxref.rid=blob.rid |
| 3422 | @ WHERE blob.rid=event.objid |
| 3423 | @ AND tag.tagname='branch' |
| 3424 | ; |
| 3425 | return zBaseSql; |
| 3426 | } |
| 3427 | |
| 3428 | /* |
| 3429 | ** Return true if the input string is a date in the ISO 8601 format: |
| 3430 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -221,11 +221,28 @@ | |
| 221 | vid = db_lget_int("checkout", 0); |
| 222 | } |
| 223 | zPrevDate[0] = 0; |
| 224 | mxWikiLen = db_get_int("timeline-max-comment", 0); |
| 225 | dateFormat = db_get_int("timeline-date-format", 0); |
| 226 | /* |
| 227 | ** SETTING: timeline-truncate-at-blank boolean default=off |
| 228 | ** |
| 229 | ** If enabled, check-in comments displayed on the timeline are truncated |
| 230 | ** at the first blank line of the comment text. The comment text after |
| 231 | ** the first blank line is only seen in the /info or similar pages that |
| 232 | ** show details about the check-in. |
| 233 | */ |
| 234 | bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0); |
| 235 | /* |
| 236 | ** SETTING: timeline-tslink-info boolean default=off |
| 237 | ** |
| 238 | ** The hyperlink on the timestamp associated with each timeline entry, |
| 239 | ** on the far left-hand side of the screen, normally targets another |
| 240 | ** /timeline page that shows the entry in context. However, if this |
| 241 | ** option is turned on, that hyperlink targets the /info page showing |
| 242 | ** the details of the entry. |
| 243 | */ |
| 244 | bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0); |
| 245 | if( (tmFlags & TIMELINE_VIEWS)==0 ){ |
| 246 | tmFlags |= timeline_ss_cookie(); |
| 247 | } |
| 248 | if( tmFlags & TIMELINE_COLUMNAR ){ |
| @@ -1092,24 +1109,39 @@ | |
| 1109 | } |
| 1110 | |
| 1111 | /* |
| 1112 | ** Convert a symbolic name used as an argument to the a=, b=, or c= |
| 1113 | ** query parameters of timeline into a julianday mtime value. |
| 1114 | ** |
| 1115 | ** If pzDisplay is not null, then display text for the symbolic name might |
| 1116 | ** be written into *pzDisplay. But that is not guaranteed. |
| 1117 | ** |
| 1118 | ** If bRoundUp is true and the symbolic name is a timestamp with less |
| 1119 | ** than millisecond resolution, then the timestamp is rounding up to the |
| 1120 | ** largest millisecond consistent with that timestamp. If bRoundUp is |
| 1121 | ** false, then the resulting time is obtained by extending the timestamp |
| 1122 | ** with zeros (hence rounding down). Use bRoundUp==1 if the result |
| 1123 | ** will be used in mtime<=$RESULT and use bRoundUp==0 if the result |
| 1124 | ** will be used in mtime>=$RESULT. |
| 1125 | */ |
| 1126 | double symbolic_name_to_mtime( |
| 1127 | const char *z, /* Input symbolic name */ |
| 1128 | const char **pzDisplay, /* Perhaps write display text here, if not NULL */ |
| 1129 | int bRoundUp /* Round up if true */ |
| 1130 | ){ |
| 1131 | double mtime; |
| 1132 | int rid; |
| 1133 | const char *zDate; |
| 1134 | if( z==0 ) return -1.0; |
| 1135 | if( fossil_isdate(z) ){ |
| 1136 | mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z); |
| 1137 | if( mtime>0.0 ) return mtime; |
| 1138 | } |
| 1139 | zDate = fossil_expand_datetime(z, 1, bRoundUp); |
| 1140 | if( zDate!=0 ){ |
| 1141 | mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", |
| 1142 | bRoundUp ? fossil_roundup_date(zDate) : zDate); |
| 1143 | if( mtime>0.0 ){ |
| 1144 | if( pzDisplay ) *pzDisplay = fossil_strdup(zDate); |
| 1145 | return mtime; |
| 1146 | } |
| 1147 | } |
| @@ -1381,11 +1413,11 @@ | |
| 1413 | ** return 0. |
| 1414 | */ |
| 1415 | static int timeline_endpoint( |
| 1416 | int iFrom, /* Starting point */ |
| 1417 | const char *zEnd, /* Tag we are searching for */ |
| 1418 | int bForward /* 1: forwards in time (descendants) 0: backwards */ |
| 1419 | ){ |
| 1420 | int tagId; |
| 1421 | int endId = 0; |
| 1422 | Stmt q; |
| 1423 | int ans = 0; |
| @@ -1513,17 +1545,17 @@ | |
| 1545 | /* |
| 1546 | ** COMMAND: test-endpoint |
| 1547 | ** |
| 1548 | ** Usage: fossil test-endpoint BASE TAG ?OPTIONS? |
| 1549 | ** |
| 1550 | ** Show the first check-in with TAG that is a descendant or ancestor |
| 1551 | ** of BASE. The first descendant checkin is shown by default. Use |
| 1552 | ** the --backto to see the first ancestor checkin. |
| 1553 | ** |
| 1554 | ** Options: |
| 1555 | ** |
| 1556 | ** --backto Show ancestor. Others defaults to descendants. |
| 1557 | */ |
| 1558 | void timeline_test_endpoint(void){ |
| 1559 | int bForward = find_option("backto",0,0)==0; |
| 1560 | int from_rid; |
| 1561 | int ans; |
| @@ -1703,11 +1735,11 @@ | |
| 1735 | int to_rid = name_choice("to","to2",&zTo2); /* to= for path timelines */ |
| 1736 | int bShort = P("shortest")!=0; /* shortest possible path */ |
| 1737 | int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */ |
| 1738 | int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */ |
| 1739 | int pd_rid; |
| 1740 | const char *zDPNameP, *zDPNameD; /* Value of p=, d=, or dp= params */ |
| 1741 | double rBefore, rAfter, rCirca; /* Boundary times */ |
| 1742 | const char *z; |
| 1743 | char *zOlderButton = 0; /* URL for Older button at the bottom */ |
| 1744 | char *zOlderButtonLabel = 0; /* Label for the Older Button */ |
| 1745 | char *zNewerButton = 0; /* URL for Newer button at the top */ |
| @@ -1770,20 +1802,20 @@ | |
| 1802 | }else{ |
| 1803 | nEntry = 50; |
| 1804 | } |
| 1805 | |
| 1806 | /* Query parameters d=, p=, and f= and variants */ |
| 1807 | p_rid = name_choice("p","p2", &zDPNameP); |
| 1808 | d_rid = name_choice("d","d2", &zDPNameD); |
| 1809 | z = P("f"); |
| 1810 | f_rid = z ? name_to_typed_rid(z,"ci") : 0; |
| 1811 | z = P("df"); |
| 1812 | if( z && (d_rid = name_to_typed_rid(z,"ci"))!=0 ){ |
| 1813 | nEntry = 0; |
| 1814 | useDividers = 0; |
| 1815 | cgi_replace_query_parameter("d",fossil_strdup(z)); |
| 1816 | zDPNameD = zDPNameP = z; |
| 1817 | } |
| 1818 | |
| 1819 | /* Undocumented query parameter to set JS mode */ |
| 1820 | builtin_set_js_delivery_mode(P("jsmode"),1); |
| 1821 | |
| @@ -1803,13 +1835,14 @@ | |
| 1835 | showCherrypicks = 0; |
| 1836 | } |
| 1837 | |
| 1838 | /* To view the timeline, must have permission to read project data. |
| 1839 | */ |
| 1840 | pd_rid = name_choice("dp","dp2",&zDPNameP); |
| 1841 | if( pd_rid ){ |
| 1842 | p_rid = d_rid = pd_rid; |
| 1843 | zDPNameD = zDPNameP; |
| 1844 | } |
| 1845 | if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum) |
| 1846 | || (bisectLocal && !g.perm.Setup) |
| 1847 | ){ |
| 1848 | login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); |
| @@ -2226,37 +2259,47 @@ | |
| 2259 | } |
| 2260 | } |
| 2261 | } |
| 2262 | addFileGlobDescription(zChng, &desc); |
| 2263 | }else if( (p_rid || d_rid) && g.perm.Read && zTagSql==0 ){ |
| 2264 | /* If either p= or d= or both are present, ignore all other parameters |
| 2265 | ** other than n=, ft=, and bt= */ |
| 2266 | const char *zBaseName = 0; |
| 2267 | int np = 0, nd; |
| 2268 | const char *zBackTo = 0; |
| 2269 | const char *zFwdTo = 0; |
| 2270 | int ridBackTo = 0; |
| 2271 | int ridFwdTo = 0; |
| 2272 | int bBackAdded = 0; /* True if the zBackTo node was added */ |
| 2273 | int bFwdAdded = 0; /* True if the zBackTo node was added */ |
| 2274 | int bSeparateDandP = 0; /* p_rid & d_rid both exist and are distinct */ |
| 2275 | |
| 2276 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| 2277 | if( p_rid && d_rid && p_rid!=d_rid ){ |
| 2278 | bSeparateDandP = 1; |
| 2279 | db_multi_exec( |
| 2280 | "CREATE TEMP TABLE IF NOT EXISTS ok_d(rid INTEGER PRIMARY KEY)" |
| 2281 | ); |
| 2282 | }else{ |
| 2283 | zBaseName = p_rid ? zDPNameP : zDPNameD; |
| 2284 | } |
| 2285 | db_multi_exec( |
| 2286 | "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)" |
| 2287 | ); |
| 2288 | add_extra_rids("ok", P("x")); |
| 2289 | blob_append_sql(&sql, " AND event.objid IN ok"); |
| 2290 | nd = 0; |
| 2291 | if( d_rid ){ |
| 2292 | double rStopTime = 9e99; |
| 2293 | zFwdTo = P("ft"); |
| 2294 | if( zFwdTo && bSeparateDandP ){ |
| 2295 | if( zError==0 ){ |
| 2296 | zError = "Cannot use the ft= query parameter when both p= and d= " |
| 2297 | "are used and have distinct values."; |
| 2298 | } |
| 2299 | zFwdTo = 0; |
| 2300 | } |
| 2301 | if( zFwdTo ){ |
| 2302 | double rStartDate = mtime_of_rid(d_rid, 0.0); |
| 2303 | ridFwdTo = first_checkin_with_tag_after_date(zFwdTo, rStartDate); |
| 2304 | if( ridFwdTo==0 ){ |
| 2305 | ridFwdTo = name_to_typed_rid(zBackTo,"ci"); |
| @@ -2263,10 +2306,13 @@ | |
| 2306 | } |
| 2307 | if( ridFwdTo ){ |
| 2308 | if( !haveParameterN ) nEntry = 0; |
| 2309 | rStopTime = mtime_of_rid(ridFwdTo, 9e99); |
| 2310 | } |
| 2311 | }else if( bSeparateDandP ){ |
| 2312 | rStopTime = mtime_of_rid(p_rid, 9e99); |
| 2313 | nEntry = 0; |
| 2314 | } |
| 2315 | if( rStopTime<9e99 ){ |
| 2316 | rStopTime += 5.8e-6; /* Round up by 1/2 second */ |
| 2317 | } |
| 2318 | db_multi_exec( |
| @@ -2279,71 +2325,111 @@ | |
| 2325 | " ORDER BY 2\n" |
| 2326 | ")\n" |
| 2327 | "INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d", |
| 2328 | d_rid, rStopTime<8e99 ? 17 : 2, rStopTime, nEntry<=0 ? -1 : nEntry+1 |
| 2329 | ); |
| 2330 | if( ridFwdTo && !db_exists("SELECT 1 FROM ok WHERE rid=%d",ridFwdTo) ){ |
| 2331 | db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridFwdTo); |
| 2332 | bFwdAdded = 1; |
| 2333 | } |
| 2334 | if( bSeparateDandP ){ |
| 2335 | db_multi_exec( |
| 2336 | "INSERT INTO ok_d SELECT rid FROM ok;" |
| 2337 | "DELETE FROM ok;" |
| 2338 | ); |
| 2339 | }else{ |
| 2340 | nd = db_int(0, "SELECT count(*)-1 FROM ok"); |
| 2341 | if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql)); |
| 2342 | if( nd>0 || p_rid==0 ){ |
| 2343 | blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s"); |
| 2344 | } |
| 2345 | if( useDividers && !selectedRid ) selectedRid = d_rid; |
| 2346 | db_multi_exec("DELETE FROM ok"); |
| 2347 | } |
| 2348 | } |
| 2349 | if( p_rid ){ |
| 2350 | zBackTo = P("bt"); |
| 2351 | if( zBackTo && bSeparateDandP ){ |
| 2352 | if( zError==0 ){ |
| 2353 | zError = "Cannot use the bt= query parameter when both p= and d= " |
| 2354 | "are used and have distinct values."; |
| 2355 | } |
| 2356 | zBackTo = 0; |
| 2357 | } |
| 2358 | if( zBackTo ){ |
| 2359 | double rDateLimit = mtime_of_rid(p_rid, 0.0); |
| 2360 | ridBackTo = last_checkin_with_tag_before_date(zBackTo, rDateLimit); |
| 2361 | if( ridBackTo==0 ){ |
| 2362 | ridBackTo = name_to_typed_rid(zBackTo,"ci"); |
| 2363 | } |
| 2364 | if( ridBackTo && !haveParameterN ) nEntry = 0; |
| 2365 | }else if( bSeparateDandP ){ |
| 2366 | ridBackTo = d_rid; |
| 2367 | nEntry = 0; |
| 2368 | } |
| 2369 | compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo); |
| 2370 | if( ridBackTo && !db_exists("SELECT 1 FROM ok WHERE rid=%d",ridBackTo) ){ |
| 2371 | db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridBackTo); |
| 2372 | bBackAdded = 1; |
| 2373 | } |
| 2374 | if( bSeparateDandP ){ |
| 2375 | db_multi_exec("DELETE FROM ok WHERE rid NOT IN ok_d;"); |
| 2376 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 2377 | }else{ |
| 2378 | np = db_int(0, "SELECT count(*)-1 FROM ok"); |
| 2379 | if( np>0 || nd==0 ){ |
| 2380 | if( nd>0 ) blob_appendf(&desc, " and "); |
| 2381 | blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s"); |
| 2382 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 2383 | } |
| 2384 | if( useDividers && !selectedRid ) selectedRid = p_rid; |
| 2385 | } |
| 2386 | } |
| 2387 | if( bSeparateDandP ){ |
| 2388 | int n = db_int(0, "SELECT count(*) FROM ok"); |
| 2389 | blob_reset(&desc); |
| 2390 | blob_appendf(&desc, |
| 2391 | "%d check-ins that are both ancestors of %z%h</a>" |
| 2392 | " and descendants of %z%h</a>", |
| 2393 | n, |
| 2394 | href("%R/info?name=%h",zDPNameP),zDPNameP, |
| 2395 | href("%R/info?name=%h",zDPNameD),zDPNameD |
| 2396 | ); |
| 2397 | ridBackTo = 0; |
| 2398 | ridFwdTo = 0; |
| 2399 | }else{ |
| 2400 | blob_appendf(&desc, " of %z%h</a>", |
| 2401 | href("%R/info?name=%h", zBaseName), zBaseName); |
| 2402 | } |
| 2403 | if( ridBackTo ){ |
| 2404 | if( np==0 ){ |
| 2405 | blob_reset(&desc); |
| 2406 | blob_appendf(&desc, |
| 2407 | "Check-in %z%h</a> only (%z%h</a> does not precede it)", |
| 2408 | href("%R/info?name=%h",zBaseName), zBaseName, |
| 2409 | href("%R/info?name=%h",zBackTo), zBackTo); |
| 2410 | }else{ |
| 2411 | blob_appendf(&desc, " back to %z%h</a>%s", |
| 2412 | href("%R/info?name=%h",zBackTo), zBackTo, |
| 2413 | bBackAdded ? " (not a direct anscestor)" : ""); |
| 2414 | if( ridFwdTo && zFwdTo ){ |
| 2415 | blob_appendf(&desc, " and up to %z%h</a>%s", |
| 2416 | href("%R/info?name=%h",zFwdTo), zFwdTo, |
| 2417 | bFwdAdded ? " (not a direct descendant)" : ""); |
| 2418 | } |
| 2419 | } |
| 2420 | }else if( ridFwdTo ){ |
| 2421 | if( nd==0 ){ |
| 2422 | blob_reset(&desc); |
| 2423 | blob_appendf(&desc, |
| 2424 | "Check-in %z%h</a> only (%z%h</a> does not follow it)", |
| 2425 | href("%R/info?name=%h",zBaseName), zBaseName, |
| 2426 | href("%R/info?name=%h",zFwdTo), zFwdTo); |
| 2427 | }else{ |
| 2428 | blob_appendf(&desc, " up to %z%h</a>%s", |
| 2429 | href("%R/info?name=%h",zFwdTo), zFwdTo, |
| 2430 | bFwdAdded ? " (not a direct descendant)":""); |
| 2431 | } |
| 2432 | } |
| 2433 | if( advancedMenu ){ |
| 2434 | style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0); |
| 2435 | } |
| @@ -2813,13 +2899,13 @@ | |
| 2899 | blob_append_sql(&cond, |
| 2900 | " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')", |
| 2901 | zSearch, zSearch); |
| 2902 | } |
| 2903 | } |
| 2904 | rBefore = symbolic_name_to_mtime(zBefore, &zBefore, 1); |
| 2905 | rAfter = symbolic_name_to_mtime(zAfter, &zAfter, 0); |
| 2906 | rCirca = symbolic_name_to_mtime(zCirca, &zCirca, 0); |
| 2907 | blob_append_sql(&sql, "%s", blob_sql_text(&cond)); |
| 2908 | if( rAfter>0.0 ){ |
| 2909 | if( rBefore>0.0 ){ |
| 2910 | blob_append_sql(&sql, |
| 2911 | " AND event.mtime>=%.17g AND event.mtime<=%.17g\n" |
| @@ -2956,11 +3042,11 @@ | |
| 3042 | zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/"); |
| 3043 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 3044 | zDate = mprintf("%s", (zAfter ? zAfter : zBefore)); |
| 3045 | } |
| 3046 | if( zDate ){ |
| 3047 | rDate = symbolic_name_to_mtime(zDate, 0, 0); |
| 3048 | if( db_int(0, |
| 3049 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 3050 | " WHERE blob.rid=event.objid AND mtime<=%.17g%s)", |
| 3051 | rDate-ONE_SECOND, blob_sql_text(&cond)) |
| 3052 | ){ |
| @@ -2972,11 +3058,11 @@ | |
| 3058 | zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/"); |
| 3059 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 3060 | zDate = mprintf("%s", (zBefore ? zBefore : zAfter)); |
| 3061 | } |
| 3062 | if( zDate ){ |
| 3063 | rDate = symbolic_name_to_mtime(zDate, 0, 0); |
| 3064 | if( db_int(0, |
| 3065 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 3066 | " WHERE blob.rid=event.objid AND mtime>=%.17g%s)", |
| 3067 | rDate+ONE_SECOND, blob_sql_text(&cond)) |
| 3068 | ){ |
| @@ -3015,11 +3101,11 @@ | |
| 3101 | style_submenu_element("Advanced", "%s", |
| 3102 | url_render(&url, "advm", "1", "udc", "1")); |
| 3103 | } |
| 3104 | if( PB("showid") ) tmFlags |= TIMELINE_SHOWRID; |
| 3105 | if( useDividers && zMark && zMark[0] ){ |
| 3106 | double r = symbolic_name_to_mtime(zMark, 0, 0); |
| 3107 | if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r); |
| 3108 | } |
| 3109 | blob_zero(&sql); |
| 3110 | if( PB("oldestfirst") ){ |
| 3111 | db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby ASC /*scan*/"); |
| @@ -3381,22 +3467,54 @@ | |
| 3467 | fossil_print("+++ no more data (%d) +++\n", nEntry); |
| 3468 | } |
| 3469 | } |
| 3470 | if( fchngQueryInit ) db_finalize(&fchngQuery); |
| 3471 | } |
| 3472 | |
| 3473 | /* |
| 3474 | ** wiki_to_text(TEXT) |
| 3475 | ** |
| 3476 | ** Return a plain-text rendering of Fossil-Wiki TEXT. |
| 3477 | */ |
| 3478 | static void wiki_to_text_sqlfunc( |
| 3479 | sqlite3_context *context, |
| 3480 | int argc, |
| 3481 | sqlite3_value **argv |
| 3482 | ){ |
| 3483 | const char *zIn, *zOut; |
| 3484 | int nIn, nOut; |
| 3485 | Blob in, html, txt; |
| 3486 | zIn = (const char*)sqlite3_value_text(argv[0]); |
| 3487 | if( zIn==0 ) return; |
| 3488 | nIn = sqlite3_value_bytes(argv[0]); |
| 3489 | blob_init(&in, zIn, nIn); |
| 3490 | blob_init(&html, 0, 0); |
| 3491 | wiki_convert(&in, &html, WIKI_INLINE); |
| 3492 | blob_reset(&in); |
| 3493 | blob_init(&txt, 0, 0); |
| 3494 | html_to_plaintext(blob_str(&html), &txt); |
| 3495 | blob_reset(&html); |
| 3496 | nOut = blob_size(&txt); |
| 3497 | zOut = blob_str(&txt); |
| 3498 | while( fossil_isspace(zOut[0]) ){ zOut++; nOut--; } |
| 3499 | while( nOut>0 && fossil_isspace(zOut[nOut-1]) ){ nOut--; } |
| 3500 | sqlite3_result_text(context, zOut, nOut, SQLITE_TRANSIENT); |
| 3501 | blob_reset(&txt); |
| 3502 | } |
| 3503 | |
| 3504 | /* |
| 3505 | ** Return a pointer to a static string that forms the basis for |
| 3506 | ** a timeline query for display on a TTY. |
| 3507 | */ |
| 3508 | const char *timeline_query_for_tty(void){ |
| 3509 | static int once = 0; |
| 3510 | static const char zBaseSql[] = |
| 3511 | @ SELECT |
| 3512 | @ blob.rid AS rid, |
| 3513 | @ uuid, |
| 3514 | @ datetime(event.mtime,toLocal()) AS mDateTime, |
| 3515 | @ wiki_to_text(coalesce(ecomment,comment)) |
| 3516 | @ || ' (user: ' || coalesce(euser,user,'?') |
| 3517 | @ || (SELECT case when length(x)>0 then ' tags: ' || x else '' end |
| 3518 | @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x |
| 3519 | @ FROM tag, tagxref |
| 3520 | @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid |
| @@ -3420,10 +3538,15 @@ | |
| 3538 | @ AND tagxref.tagtype>0 |
| 3539 | @ AND tagxref.rid=blob.rid |
| 3540 | @ WHERE blob.rid=event.objid |
| 3541 | @ AND tag.tagname='branch' |
| 3542 | ; |
| 3543 | if( !once && g.db ){ |
| 3544 | once = 1; |
| 3545 | sqlite3_create_function(g.db, "wiki_to_text", 1, SQLITE_UTF8, 0, |
| 3546 | wiki_to_text_sqlfunc, 0, 0); |
| 3547 | } |
| 3548 | return zBaseSql; |
| 3549 | } |
| 3550 | |
| 3551 | /* |
| 3552 | ** Return true if the input string is a date in the ISO 8601 format: |
| 3553 |
+2
-2
| --- src/unversioned.c | ||
| +++ src/unversioned.c | ||
| @@ -218,12 +218,12 @@ | ||
| 218 | 218 | } |
| 219 | 219 | return 0; |
| 220 | 220 | } |
| 221 | 221 | |
| 222 | 222 | /* |
| 223 | -** COMMAND: uv# abbreviated-subcommands | |
| 224 | -** COMMAND: unversioned abbreviated-subcommands | |
| 223 | +** COMMAND: uv# abbrv-subcom | |
| 224 | +** COMMAND: unversioned abbrv-subcom | |
| 225 | 225 | ** |
| 226 | 226 | ** Usage: %fossil unversioned SUBCOMMAND ARGS... |
| 227 | 227 | ** or: %fossil uv SUBCOMMAND ARGS.. |
| 228 | 228 | ** |
| 229 | 229 | ** Unversioned files (UV-files) are artifacts that are synced and are available |
| 230 | 230 |
| --- src/unversioned.c | |
| +++ src/unversioned.c | |
| @@ -218,12 +218,12 @@ | |
| 218 | } |
| 219 | return 0; |
| 220 | } |
| 221 | |
| 222 | /* |
| 223 | ** COMMAND: uv# abbreviated-subcommands |
| 224 | ** COMMAND: unversioned abbreviated-subcommands |
| 225 | ** |
| 226 | ** Usage: %fossil unversioned SUBCOMMAND ARGS... |
| 227 | ** or: %fossil uv SUBCOMMAND ARGS.. |
| 228 | ** |
| 229 | ** Unversioned files (UV-files) are artifacts that are synced and are available |
| 230 |
| --- src/unversioned.c | |
| +++ src/unversioned.c | |
| @@ -218,12 +218,12 @@ | |
| 218 | } |
| 219 | return 0; |
| 220 | } |
| 221 | |
| 222 | /* |
| 223 | ** COMMAND: uv# abbrv-subcom |
| 224 | ** COMMAND: unversioned abbrv-subcom |
| 225 | ** |
| 226 | ** Usage: %fossil unversioned SUBCOMMAND ARGS... |
| 227 | ** or: %fossil uv SUBCOMMAND ARGS.. |
| 228 | ** |
| 229 | ** Unversioned files (UV-files) are artifacts that are synced and are available |
| 230 |
+1
-1
| --- src/update.c | ||
| +++ src/update.c | ||
| @@ -199,11 +199,11 @@ | ||
| 199 | 199 | } |
| 200 | 200 | } |
| 201 | 201 | } |
| 202 | 202 | |
| 203 | 203 | /* If no VERSION is specified on the command-line, then look for a |
| 204 | - ** descendent of the current version. If there are multiple descendants, | |
| 204 | + ** descendant of the current version. If there are multiple descendants, | |
| 205 | 205 | ** look for one from the same branch as the current version. If there |
| 206 | 206 | ** are still multiple descendants, show them all and refuse to update |
| 207 | 207 | ** until the user selects one. |
| 208 | 208 | */ |
| 209 | 209 | if( tid==0 ){ |
| 210 | 210 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -199,11 +199,11 @@ | |
| 199 | } |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | /* If no VERSION is specified on the command-line, then look for a |
| 204 | ** descendent of the current version. If there are multiple descendants, |
| 205 | ** look for one from the same branch as the current version. If there |
| 206 | ** are still multiple descendants, show them all and refuse to update |
| 207 | ** until the user selects one. |
| 208 | */ |
| 209 | if( tid==0 ){ |
| 210 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -199,11 +199,11 @@ | |
| 199 | } |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | /* If no VERSION is specified on the command-line, then look for a |
| 204 | ** descendant of the current version. If there are multiple descendants, |
| 205 | ** look for one from the same branch as the current version. If there |
| 206 | ** are still multiple descendants, show them all and refuse to update |
| 207 | ** until the user selects one. |
| 208 | */ |
| 209 | if( tid==0 ){ |
| 210 |
+1
-1
| --- src/user.c | ||
| +++ src/user.c | ||
| @@ -298,11 +298,11 @@ | ||
| 298 | 298 | ** Prompt the user to enter a single line of text. |
| 299 | 299 | */ |
| 300 | 300 | void prompt_user(const char *zPrompt, Blob *pIn){ |
| 301 | 301 | char *z; |
| 302 | 302 | char zLine[1000]; |
| 303 | - blob_zero(pIn); | |
| 303 | + blob_init(pIn, 0, 0); | |
| 304 | 304 | fossil_force_newline(); |
| 305 | 305 | fossil_print("%s", zPrompt); |
| 306 | 306 | fflush(stdout); |
| 307 | 307 | z = fgets(zLine, sizeof(zLine), stdin); |
| 308 | 308 | if( z ){ |
| 309 | 309 |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -298,11 +298,11 @@ | |
| 298 | ** Prompt the user to enter a single line of text. |
| 299 | */ |
| 300 | void prompt_user(const char *zPrompt, Blob *pIn){ |
| 301 | char *z; |
| 302 | char zLine[1000]; |
| 303 | blob_zero(pIn); |
| 304 | fossil_force_newline(); |
| 305 | fossil_print("%s", zPrompt); |
| 306 | fflush(stdout); |
| 307 | z = fgets(zLine, sizeof(zLine), stdin); |
| 308 | if( z ){ |
| 309 |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -298,11 +298,11 @@ | |
| 298 | ** Prompt the user to enter a single line of text. |
| 299 | */ |
| 300 | void prompt_user(const char *zPrompt, Blob *pIn){ |
| 301 | char *z; |
| 302 | char zLine[1000]; |
| 303 | blob_init(pIn, 0, 0); |
| 304 | fossil_force_newline(); |
| 305 | fossil_print("%s", zPrompt); |
| 306 | fflush(stdout); |
| 307 | z = fgets(zLine, sizeof(zLine), stdin); |
| 308 | if( z ){ |
| 309 |
+63
-17
| --- src/util.c | ||
| +++ src/util.c | ||
| @@ -670,28 +670,33 @@ | ||
| 670 | 670 | ** Search algorithm: |
| 671 | 671 | ** (1) The local "editor" setting |
| 672 | 672 | ** (2) The global "editor" setting |
| 673 | 673 | ** (3) The VISUAL environment variable |
| 674 | 674 | ** (4) The EDITOR environment variable |
| 675 | -** (5) (Windows only:) "notepad.exe" | |
| 675 | +** (5) Any of the following programs that are available: | |
| 676 | +** notepad, nano, pico, jove, edit, vi, vim, ed, | |
| 676 | 677 | */ |
| 677 | 678 | const char *fossil_text_editor(void){ |
| 678 | 679 | const char *zEditor = db_get("editor", 0); |
| 680 | + const char *azStdEd[] = { | |
| 681 | + "notepad", "nano", "pico", "jove", "edit", "vi", "vim", "ed" | |
| 682 | + }; | |
| 683 | + int i = 0; | |
| 679 | 684 | if( zEditor==0 ){ |
| 680 | 685 | zEditor = fossil_getenv("VISUAL"); |
| 681 | 686 | } |
| 682 | 687 | if( zEditor==0 ){ |
| 683 | 688 | zEditor = fossil_getenv("EDITOR"); |
| 684 | 689 | } |
| 685 | -#if defined(_WIN32) || defined(__CYGWIN__) | |
| 686 | - if( zEditor==0 ){ | |
| 687 | - zEditor = mprintf("%s\\notepad.exe", fossil_getenv("SYSTEMROOT")); | |
| 688 | -#if defined(__CYGWIN__) | |
| 689 | - zEditor = fossil_utf8_to_path(zEditor, 0); | |
| 690 | -#endif | |
| 691 | - } | |
| 692 | -#endif | |
| 690 | + while( zEditor==0 && i<count(azStdEd) ){ | |
| 691 | + if( fossil_app_on_path(azStdEd[i],0) ){ | |
| 692 | + zEditor = azStdEd[i]; | |
| 693 | + }else{ | |
| 694 | + i++; | |
| 695 | + } | |
| 696 | + } | |
| 697 | + if( zEditor && is_false(zEditor) ) zEditor = 0; | |
| 693 | 698 | return zEditor; |
| 694 | 699 | } |
| 695 | 700 | |
| 696 | 701 | /* |
| 697 | 702 | ** Construct a temporary filename. |
| @@ -895,35 +900,76 @@ | ||
| 895 | 900 | return n< 10 ? 1 : n< 100 ? 2 : n< 1000 ? 3 |
| 896 | 901 | : n< 10000 ? 4 : n< 100000 ? 5 : n< 1000000 ? 6 |
| 897 | 902 | : n<10000000 ? 7 : n<100000000 ? 8 : n<1000000000 ? 9 : 10; |
| 898 | 903 | } |
| 899 | 904 | |
| 900 | -#if !defined(_WIN32) | |
| 901 | -#if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__) | |
| 902 | 905 | /* |
| 903 | 906 | ** Search for an executable on the PATH environment variable. |
| 904 | 907 | ** Return true (1) if found and false (0) if not found. |
| 908 | +** | |
| 909 | +** Print the full pathname of the first location if ePrint==1. Print | |
| 910 | +** all pathnames for the executable if ePrint==2 or more. | |
| 905 | 911 | */ |
| 906 | -static int binaryOnPath(const char *zBinary){ | |
| 912 | +int fossil_app_on_path(const char *zBinary, int ePrint){ | |
| 907 | 913 | const char *zPath = fossil_getenv("PATH"); |
| 908 | 914 | char *zFull; |
| 909 | 915 | int i; |
| 910 | 916 | int bExists; |
| 917 | + int bFound = 0; | |
| 911 | 918 | while( zPath && zPath[0] ){ |
| 919 | +#ifdef _WIN32 | |
| 920 | + while( zPath[0]==';' ) zPath++; | |
| 921 | + for(i=0; zPath[i] && zPath[i]!=';'; i++){} | |
| 922 | + zFull = mprintf("%.*s\\%s.exe", i, zPath, zBinary); | |
| 923 | + bExists = file_access(zFull, R_OK); | |
| 924 | + if( bExists!=0 ){ | |
| 925 | + fossil_free(zFull); | |
| 926 | + zFull = mprintf("%.*s\\%s.bat", i, zPath, zBinary); | |
| 927 | + bExists = file_access(zFull, R_OK); | |
| 928 | + } | |
| 929 | +#else | |
| 912 | 930 | while( zPath[0]==':' ) zPath++; |
| 913 | 931 | for(i=0; zPath[i] && zPath[i]!=':'; i++){} |
| 914 | 932 | zFull = mprintf("%.*s/%s", i, zPath, zBinary); |
| 915 | 933 | bExists = file_access(zFull, X_OK); |
| 934 | +#endif | |
| 935 | + if( bExists==0 && ePrint ){ | |
| 936 | + fossil_print("%s\n", zFull); | |
| 937 | + } | |
| 916 | 938 | fossil_free(zFull); |
| 917 | - if( bExists==0 ) return 1; | |
| 939 | + if( bExists==0 ){ | |
| 940 | + if( ePrint<2 ) return 1; | |
| 941 | + bFound = 1; | |
| 942 | + } | |
| 918 | 943 | zPath += i; |
| 919 | 944 | } |
| 920 | - return 0; | |
| 945 | + return bFound; | |
| 921 | 946 | } |
| 922 | -#endif | |
| 923 | -#endif | |
| 924 | 947 | |
| 948 | +/* | |
| 949 | +** COMMAND: which* | |
| 950 | +** | |
| 951 | +** Usage: fossil which [-a] NAME ... | |
| 952 | +** | |
| 953 | +** For each NAME mentioned as an argument, print the first location on the | |
| 954 | +** on PATH of the executable with that name. Or, show all locations on PATH | |
| 955 | +** for each argument if the -a option is used. | |
| 956 | +** | |
| 957 | +** This command is a substitute for the unix "which" command, which is not | |
| 958 | +** always available, especially on Windows. | |
| 959 | +*/ | |
| 960 | +void test_app_on_path(void){ | |
| 961 | + int i; | |
| 962 | + int ePrint = 1; | |
| 963 | + if( find_option("all","a",0)!=0 ) ePrint = 2; | |
| 964 | + verify_all_options(); | |
| 965 | + for(i=2; i<g.argc; i++){ | |
| 966 | + if( fossil_app_on_path(g.argv[i], ePrint)==0 ){ | |
| 967 | + fossil_print("NOT FOUND: %s\n", g.argv[i]); | |
| 968 | + } | |
| 969 | + } | |
| 970 | +} | |
| 925 | 971 | |
| 926 | 972 | /* |
| 927 | 973 | ** Return the name of a command that will launch a web-browser. |
| 928 | 974 | */ |
| 929 | 975 | const char *fossil_web_browser(void){ |
| @@ -938,11 +984,11 @@ | ||
| 938 | 984 | static const char *const azBrowserProg[] = |
| 939 | 985 | { "xdg-open", "gnome-open", "firefox", "google-chrome" }; |
| 940 | 986 | int i; |
| 941 | 987 | zBrowser = "echo"; |
| 942 | 988 | for(i=0; i<count(azBrowserProg); i++){ |
| 943 | - if( binaryOnPath(azBrowserProg[i]) ){ | |
| 989 | + if( fossil_app_on_path(azBrowserProg[i],0) ){ | |
| 944 | 990 | zBrowser = azBrowserProg[i]; |
| 945 | 991 | break; |
| 946 | 992 | } |
| 947 | 993 | } |
| 948 | 994 | zBrowser = mprintf("%s 2>/dev/null", zBrowser); |
| 949 | 995 |
| --- src/util.c | |
| +++ src/util.c | |
| @@ -670,28 +670,33 @@ | |
| 670 | ** Search algorithm: |
| 671 | ** (1) The local "editor" setting |
| 672 | ** (2) The global "editor" setting |
| 673 | ** (3) The VISUAL environment variable |
| 674 | ** (4) The EDITOR environment variable |
| 675 | ** (5) (Windows only:) "notepad.exe" |
| 676 | */ |
| 677 | const char *fossil_text_editor(void){ |
| 678 | const char *zEditor = db_get("editor", 0); |
| 679 | if( zEditor==0 ){ |
| 680 | zEditor = fossil_getenv("VISUAL"); |
| 681 | } |
| 682 | if( zEditor==0 ){ |
| 683 | zEditor = fossil_getenv("EDITOR"); |
| 684 | } |
| 685 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 686 | if( zEditor==0 ){ |
| 687 | zEditor = mprintf("%s\\notepad.exe", fossil_getenv("SYSTEMROOT")); |
| 688 | #if defined(__CYGWIN__) |
| 689 | zEditor = fossil_utf8_to_path(zEditor, 0); |
| 690 | #endif |
| 691 | } |
| 692 | #endif |
| 693 | return zEditor; |
| 694 | } |
| 695 | |
| 696 | /* |
| 697 | ** Construct a temporary filename. |
| @@ -895,35 +900,76 @@ | |
| 895 | return n< 10 ? 1 : n< 100 ? 2 : n< 1000 ? 3 |
| 896 | : n< 10000 ? 4 : n< 100000 ? 5 : n< 1000000 ? 6 |
| 897 | : n<10000000 ? 7 : n<100000000 ? 8 : n<1000000000 ? 9 : 10; |
| 898 | } |
| 899 | |
| 900 | #if !defined(_WIN32) |
| 901 | #if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__) |
| 902 | /* |
| 903 | ** Search for an executable on the PATH environment variable. |
| 904 | ** Return true (1) if found and false (0) if not found. |
| 905 | */ |
| 906 | static int binaryOnPath(const char *zBinary){ |
| 907 | const char *zPath = fossil_getenv("PATH"); |
| 908 | char *zFull; |
| 909 | int i; |
| 910 | int bExists; |
| 911 | while( zPath && zPath[0] ){ |
| 912 | while( zPath[0]==':' ) zPath++; |
| 913 | for(i=0; zPath[i] && zPath[i]!=':'; i++){} |
| 914 | zFull = mprintf("%.*s/%s", i, zPath, zBinary); |
| 915 | bExists = file_access(zFull, X_OK); |
| 916 | fossil_free(zFull); |
| 917 | if( bExists==0 ) return 1; |
| 918 | zPath += i; |
| 919 | } |
| 920 | return 0; |
| 921 | } |
| 922 | #endif |
| 923 | #endif |
| 924 | |
| 925 | |
| 926 | /* |
| 927 | ** Return the name of a command that will launch a web-browser. |
| 928 | */ |
| 929 | const char *fossil_web_browser(void){ |
| @@ -938,11 +984,11 @@ | |
| 938 | static const char *const azBrowserProg[] = |
| 939 | { "xdg-open", "gnome-open", "firefox", "google-chrome" }; |
| 940 | int i; |
| 941 | zBrowser = "echo"; |
| 942 | for(i=0; i<count(azBrowserProg); i++){ |
| 943 | if( binaryOnPath(azBrowserProg[i]) ){ |
| 944 | zBrowser = azBrowserProg[i]; |
| 945 | break; |
| 946 | } |
| 947 | } |
| 948 | zBrowser = mprintf("%s 2>/dev/null", zBrowser); |
| 949 |
| --- src/util.c | |
| +++ src/util.c | |
| @@ -670,28 +670,33 @@ | |
| 670 | ** Search algorithm: |
| 671 | ** (1) The local "editor" setting |
| 672 | ** (2) The global "editor" setting |
| 673 | ** (3) The VISUAL environment variable |
| 674 | ** (4) The EDITOR environment variable |
| 675 | ** (5) Any of the following programs that are available: |
| 676 | ** notepad, nano, pico, jove, edit, vi, vim, ed, |
| 677 | */ |
| 678 | const char *fossil_text_editor(void){ |
| 679 | const char *zEditor = db_get("editor", 0); |
| 680 | const char *azStdEd[] = { |
| 681 | "notepad", "nano", "pico", "jove", "edit", "vi", "vim", "ed" |
| 682 | }; |
| 683 | int i = 0; |
| 684 | if( zEditor==0 ){ |
| 685 | zEditor = fossil_getenv("VISUAL"); |
| 686 | } |
| 687 | if( zEditor==0 ){ |
| 688 | zEditor = fossil_getenv("EDITOR"); |
| 689 | } |
| 690 | while( zEditor==0 && i<count(azStdEd) ){ |
| 691 | if( fossil_app_on_path(azStdEd[i],0) ){ |
| 692 | zEditor = azStdEd[i]; |
| 693 | }else{ |
| 694 | i++; |
| 695 | } |
| 696 | } |
| 697 | if( zEditor && is_false(zEditor) ) zEditor = 0; |
| 698 | return zEditor; |
| 699 | } |
| 700 | |
| 701 | /* |
| 702 | ** Construct a temporary filename. |
| @@ -895,35 +900,76 @@ | |
| 900 | return n< 10 ? 1 : n< 100 ? 2 : n< 1000 ? 3 |
| 901 | : n< 10000 ? 4 : n< 100000 ? 5 : n< 1000000 ? 6 |
| 902 | : n<10000000 ? 7 : n<100000000 ? 8 : n<1000000000 ? 9 : 10; |
| 903 | } |
| 904 | |
| 905 | /* |
| 906 | ** Search for an executable on the PATH environment variable. |
| 907 | ** Return true (1) if found and false (0) if not found. |
| 908 | ** |
| 909 | ** Print the full pathname of the first location if ePrint==1. Print |
| 910 | ** all pathnames for the executable if ePrint==2 or more. |
| 911 | */ |
| 912 | int fossil_app_on_path(const char *zBinary, int ePrint){ |
| 913 | const char *zPath = fossil_getenv("PATH"); |
| 914 | char *zFull; |
| 915 | int i; |
| 916 | int bExists; |
| 917 | int bFound = 0; |
| 918 | while( zPath && zPath[0] ){ |
| 919 | #ifdef _WIN32 |
| 920 | while( zPath[0]==';' ) zPath++; |
| 921 | for(i=0; zPath[i] && zPath[i]!=';'; i++){} |
| 922 | zFull = mprintf("%.*s\\%s.exe", i, zPath, zBinary); |
| 923 | bExists = file_access(zFull, R_OK); |
| 924 | if( bExists!=0 ){ |
| 925 | fossil_free(zFull); |
| 926 | zFull = mprintf("%.*s\\%s.bat", i, zPath, zBinary); |
| 927 | bExists = file_access(zFull, R_OK); |
| 928 | } |
| 929 | #else |
| 930 | while( zPath[0]==':' ) zPath++; |
| 931 | for(i=0; zPath[i] && zPath[i]!=':'; i++){} |
| 932 | zFull = mprintf("%.*s/%s", i, zPath, zBinary); |
| 933 | bExists = file_access(zFull, X_OK); |
| 934 | #endif |
| 935 | if( bExists==0 && ePrint ){ |
| 936 | fossil_print("%s\n", zFull); |
| 937 | } |
| 938 | fossil_free(zFull); |
| 939 | if( bExists==0 ){ |
| 940 | if( ePrint<2 ) return 1; |
| 941 | bFound = 1; |
| 942 | } |
| 943 | zPath += i; |
| 944 | } |
| 945 | return bFound; |
| 946 | } |
| 947 | |
| 948 | /* |
| 949 | ** COMMAND: which* |
| 950 | ** |
| 951 | ** Usage: fossil which [-a] NAME ... |
| 952 | ** |
| 953 | ** For each NAME mentioned as an argument, print the first location on the |
| 954 | ** on PATH of the executable with that name. Or, show all locations on PATH |
| 955 | ** for each argument if the -a option is used. |
| 956 | ** |
| 957 | ** This command is a substitute for the unix "which" command, which is not |
| 958 | ** always available, especially on Windows. |
| 959 | */ |
| 960 | void test_app_on_path(void){ |
| 961 | int i; |
| 962 | int ePrint = 1; |
| 963 | if( find_option("all","a",0)!=0 ) ePrint = 2; |
| 964 | verify_all_options(); |
| 965 | for(i=2; i<g.argc; i++){ |
| 966 | if( fossil_app_on_path(g.argv[i], ePrint)==0 ){ |
| 967 | fossil_print("NOT FOUND: %s\n", g.argv[i]); |
| 968 | } |
| 969 | } |
| 970 | } |
| 971 | |
| 972 | /* |
| 973 | ** Return the name of a command that will launch a web-browser. |
| 974 | */ |
| 975 | const char *fossil_web_browser(void){ |
| @@ -938,11 +984,11 @@ | |
| 984 | static const char *const azBrowserProg[] = |
| 985 | { "xdg-open", "gnome-open", "firefox", "google-chrome" }; |
| 986 | int i; |
| 987 | zBrowser = "echo"; |
| 988 | for(i=0; i<count(azBrowserProg); i++){ |
| 989 | if( fossil_app_on_path(azBrowserProg[i],0) ){ |
| 990 | zBrowser = azBrowserProg[i]; |
| 991 | break; |
| 992 | } |
| 993 | } |
| 994 | zBrowser = mprintf("%s 2>/dev/null", zBrowser); |
| 995 |
+19
-13
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -21,10 +21,13 @@ | ||
| 21 | 21 | #include "config.h" |
| 22 | 22 | #include <assert.h> |
| 23 | 23 | #include <ctype.h> |
| 24 | 24 | #include "wiki.h" |
| 25 | 25 | |
| 26 | +#define has_prefix(literal_prfx, zStr) \ | |
| 27 | + (fossil_strncmp((zStr), "" literal_prfx, (sizeof literal_prfx)-1)==0) | |
| 28 | + | |
| 26 | 29 | /* |
| 27 | 30 | ** Return true if the input string is a well-formed wiki page name. |
| 28 | 31 | ** |
| 29 | 32 | ** Well-formed wiki page names do not begin or end with whitespace, |
| 30 | 33 | ** and do not contain tabs or other control characters and do not |
| @@ -420,22 +423,22 @@ | ||
| 420 | 423 | */ |
| 421 | 424 | int wiki_page_type(const char *zPageName){ |
| 422 | 425 | if( db_get_boolean("wiki-about",1)==0 ){ |
| 423 | 426 | return WIKITYPE_NORMAL; |
| 424 | 427 | }else |
| 425 | - if( sqlite3_strglob("checkin/*", zPageName)==0 | |
| 428 | + if( has_prefix("checkin/", zPageName) | |
| 426 | 429 | && db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8) |
| 427 | 430 | ){ |
| 428 | 431 | return WIKITYPE_CHECKIN; |
| 429 | 432 | }else |
| 430 | - if( sqlite3_strglob("branch/*", zPageName)==0 ){ | |
| 433 | + if( has_prefix("branch/", zPageName) ){ | |
| 431 | 434 | return WIKITYPE_BRANCH; |
| 432 | 435 | }else |
| 433 | - if( sqlite3_strglob("tag/*", zPageName)==0 ){ | |
| 436 | + if( has_prefix("tag/", zPageName) ){ | |
| 434 | 437 | return WIKITYPE_TAG; |
| 435 | 438 | }else |
| 436 | - if( sqlite3_strglob("ticket/*", zPageName)==0 ){ | |
| 439 | + if( has_prefix("ticket/", zPageName) ){ | |
| 437 | 440 | return WIKITYPE_TICKET; |
| 438 | 441 | } |
| 439 | 442 | return WIKITYPE_NORMAL; |
| 440 | 443 | } |
| 441 | 444 | |
| @@ -1987,11 +1990,12 @@ | ||
| 1987 | 1990 | style_submenu_element("All", "%R/wcontent?all=1"); |
| 1988 | 1991 | } |
| 1989 | 1992 | cgi_check_for_malice(); |
| 1990 | 1993 | showCkBr = db_exists( |
| 1991 | 1994 | "SELECT tag.tagname AS tn FROM tag JOIN tagxref USING(tagid) " |
| 1992 | - "WHERE ( tn GLOB 'wiki-checkin/*' OR tn GLOB 'wiki-branch/*' ) " | |
| 1995 | + "WHERE ( tn GLOB 'wiki-checkin/*' OR tn GLOB 'wiki-branch/*' OR " | |
| 1996 | + " tn GLOB 'wiki-tag/*' OR tn GLOB 'wiki-ticket/*' ) " | |
| 1993 | 1997 | " AND TYPEOF(tagxref.value+0)='integer'" ); |
| 1994 | 1998 | if( showCkBr ){ |
| 1995 | 1999 | showCkBr = P("showckbr")!=0; |
| 1996 | 2000 | style_submenu_checkbox("showckbr", "Show associated wikis", 0, 0); |
| 1997 | 2001 | } |
| @@ -2017,20 +2021,20 @@ | ||
| 2017 | 2021 | char *zAge; |
| 2018 | 2022 | int wcnt = db_column_int(&q, 4); |
| 2019 | 2023 | char *zWDisplayName; |
| 2020 | 2024 | |
| 2021 | 2025 | if( !showCkBr && |
| 2022 | - (sqlite3_strglob("checkin/*", zWName)==0 || | |
| 2023 | - sqlite3_strglob("branch/*", zWName)==0 || | |
| 2024 | - sqlite3_strglob("tag/*", zWName)==0 || | |
| 2025 | - sqlite3_strglob("ticket/*", zWName)==0) ){ | |
| 2026 | + (has_prefix("checkin/", zWName) || | |
| 2027 | + has_prefix("branch/", zWName) || | |
| 2028 | + has_prefix("tag/", zWName) || | |
| 2029 | + has_prefix("ticket/", zWName) )){ | |
| 2026 | 2030 | continue; |
| 2027 | 2031 | } |
| 2028 | - if( sqlite3_strglob("checkin/*", zWName)==0 ){ | |
| 2032 | + if( has_prefix("checkin/",zWName) || has_prefix("ticket/",zWName) ){ | |
| 2029 | 2033 | zWDisplayName = mprintf("%.25s...", zWName); |
| 2030 | 2034 | }else{ |
| 2031 | - zWDisplayName = mprintf("%s", zWName); | |
| 2035 | + zWDisplayName = fossil_strdup(zWName); | |
| 2032 | 2036 | } |
| 2033 | 2037 | if( wrid==0 ){ |
| 2034 | 2038 | if( !showAll ) continue; |
| 2035 | 2039 | @ <tr><td data-sortkey="%h(zSort)">\ |
| 2036 | 2040 | @ %z(href("%R/whistory?name=%T",zWName))<s>%h(zWDisplayName)</s></a></td> |
| @@ -2522,12 +2526,14 @@ | ||
| 2522 | 2526 | const int wrid = db_column_int(&q, 2); |
| 2523 | 2527 | if(!showAll && !wrid){ |
| 2524 | 2528 | continue; |
| 2525 | 2529 | } |
| 2526 | 2530 | if( !showCkBr && |
| 2527 | - (sqlite3_strglob("checkin/*", zName)==0 || | |
| 2528 | - sqlite3_strglob("branch/*", zName)==0) ){ | |
| 2531 | + (has_prefix("checkin/", zName) || | |
| 2532 | + has_prefix("branch/", zName) || | |
| 2533 | + has_prefix("tag/", zName) || | |
| 2534 | + has_prefix("ticket/", zName) ) ){ | |
| 2529 | 2535 | continue; |
| 2530 | 2536 | } |
| 2531 | 2537 | if( showIds ){ |
| 2532 | 2538 | const char *zUuid = db_column_text(&q, 1); |
| 2533 | 2539 | fossil_print("%s ",zUuid); |
| 2534 | 2540 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -21,10 +21,13 @@ | |
| 21 | #include "config.h" |
| 22 | #include <assert.h> |
| 23 | #include <ctype.h> |
| 24 | #include "wiki.h" |
| 25 | |
| 26 | /* |
| 27 | ** Return true if the input string is a well-formed wiki page name. |
| 28 | ** |
| 29 | ** Well-formed wiki page names do not begin or end with whitespace, |
| 30 | ** and do not contain tabs or other control characters and do not |
| @@ -420,22 +423,22 @@ | |
| 420 | */ |
| 421 | int wiki_page_type(const char *zPageName){ |
| 422 | if( db_get_boolean("wiki-about",1)==0 ){ |
| 423 | return WIKITYPE_NORMAL; |
| 424 | }else |
| 425 | if( sqlite3_strglob("checkin/*", zPageName)==0 |
| 426 | && db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8) |
| 427 | ){ |
| 428 | return WIKITYPE_CHECKIN; |
| 429 | }else |
| 430 | if( sqlite3_strglob("branch/*", zPageName)==0 ){ |
| 431 | return WIKITYPE_BRANCH; |
| 432 | }else |
| 433 | if( sqlite3_strglob("tag/*", zPageName)==0 ){ |
| 434 | return WIKITYPE_TAG; |
| 435 | }else |
| 436 | if( sqlite3_strglob("ticket/*", zPageName)==0 ){ |
| 437 | return WIKITYPE_TICKET; |
| 438 | } |
| 439 | return WIKITYPE_NORMAL; |
| 440 | } |
| 441 | |
| @@ -1987,11 +1990,12 @@ | |
| 1987 | style_submenu_element("All", "%R/wcontent?all=1"); |
| 1988 | } |
| 1989 | cgi_check_for_malice(); |
| 1990 | showCkBr = db_exists( |
| 1991 | "SELECT tag.tagname AS tn FROM tag JOIN tagxref USING(tagid) " |
| 1992 | "WHERE ( tn GLOB 'wiki-checkin/*' OR tn GLOB 'wiki-branch/*' ) " |
| 1993 | " AND TYPEOF(tagxref.value+0)='integer'" ); |
| 1994 | if( showCkBr ){ |
| 1995 | showCkBr = P("showckbr")!=0; |
| 1996 | style_submenu_checkbox("showckbr", "Show associated wikis", 0, 0); |
| 1997 | } |
| @@ -2017,20 +2021,20 @@ | |
| 2017 | char *zAge; |
| 2018 | int wcnt = db_column_int(&q, 4); |
| 2019 | char *zWDisplayName; |
| 2020 | |
| 2021 | if( !showCkBr && |
| 2022 | (sqlite3_strglob("checkin/*", zWName)==0 || |
| 2023 | sqlite3_strglob("branch/*", zWName)==0 || |
| 2024 | sqlite3_strglob("tag/*", zWName)==0 || |
| 2025 | sqlite3_strglob("ticket/*", zWName)==0) ){ |
| 2026 | continue; |
| 2027 | } |
| 2028 | if( sqlite3_strglob("checkin/*", zWName)==0 ){ |
| 2029 | zWDisplayName = mprintf("%.25s...", zWName); |
| 2030 | }else{ |
| 2031 | zWDisplayName = mprintf("%s", zWName); |
| 2032 | } |
| 2033 | if( wrid==0 ){ |
| 2034 | if( !showAll ) continue; |
| 2035 | @ <tr><td data-sortkey="%h(zSort)">\ |
| 2036 | @ %z(href("%R/whistory?name=%T",zWName))<s>%h(zWDisplayName)</s></a></td> |
| @@ -2522,12 +2526,14 @@ | |
| 2522 | const int wrid = db_column_int(&q, 2); |
| 2523 | if(!showAll && !wrid){ |
| 2524 | continue; |
| 2525 | } |
| 2526 | if( !showCkBr && |
| 2527 | (sqlite3_strglob("checkin/*", zName)==0 || |
| 2528 | sqlite3_strglob("branch/*", zName)==0) ){ |
| 2529 | continue; |
| 2530 | } |
| 2531 | if( showIds ){ |
| 2532 | const char *zUuid = db_column_text(&q, 1); |
| 2533 | fossil_print("%s ",zUuid); |
| 2534 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -21,10 +21,13 @@ | |
| 21 | #include "config.h" |
| 22 | #include <assert.h> |
| 23 | #include <ctype.h> |
| 24 | #include "wiki.h" |
| 25 | |
| 26 | #define has_prefix(literal_prfx, zStr) \ |
| 27 | (fossil_strncmp((zStr), "" literal_prfx, (sizeof literal_prfx)-1)==0) |
| 28 | |
| 29 | /* |
| 30 | ** Return true if the input string is a well-formed wiki page name. |
| 31 | ** |
| 32 | ** Well-formed wiki page names do not begin or end with whitespace, |
| 33 | ** and do not contain tabs or other control characters and do not |
| @@ -420,22 +423,22 @@ | |
| 423 | */ |
| 424 | int wiki_page_type(const char *zPageName){ |
| 425 | if( db_get_boolean("wiki-about",1)==0 ){ |
| 426 | return WIKITYPE_NORMAL; |
| 427 | }else |
| 428 | if( has_prefix("checkin/", zPageName) |
| 429 | && db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8) |
| 430 | ){ |
| 431 | return WIKITYPE_CHECKIN; |
| 432 | }else |
| 433 | if( has_prefix("branch/", zPageName) ){ |
| 434 | return WIKITYPE_BRANCH; |
| 435 | }else |
| 436 | if( has_prefix("tag/", zPageName) ){ |
| 437 | return WIKITYPE_TAG; |
| 438 | }else |
| 439 | if( has_prefix("ticket/", zPageName) ){ |
| 440 | return WIKITYPE_TICKET; |
| 441 | } |
| 442 | return WIKITYPE_NORMAL; |
| 443 | } |
| 444 | |
| @@ -1987,11 +1990,12 @@ | |
| 1990 | style_submenu_element("All", "%R/wcontent?all=1"); |
| 1991 | } |
| 1992 | cgi_check_for_malice(); |
| 1993 | showCkBr = db_exists( |
| 1994 | "SELECT tag.tagname AS tn FROM tag JOIN tagxref USING(tagid) " |
| 1995 | "WHERE ( tn GLOB 'wiki-checkin/*' OR tn GLOB 'wiki-branch/*' OR " |
| 1996 | " tn GLOB 'wiki-tag/*' OR tn GLOB 'wiki-ticket/*' ) " |
| 1997 | " AND TYPEOF(tagxref.value+0)='integer'" ); |
| 1998 | if( showCkBr ){ |
| 1999 | showCkBr = P("showckbr")!=0; |
| 2000 | style_submenu_checkbox("showckbr", "Show associated wikis", 0, 0); |
| 2001 | } |
| @@ -2017,20 +2021,20 @@ | |
| 2021 | char *zAge; |
| 2022 | int wcnt = db_column_int(&q, 4); |
| 2023 | char *zWDisplayName; |
| 2024 | |
| 2025 | if( !showCkBr && |
| 2026 | (has_prefix("checkin/", zWName) || |
| 2027 | has_prefix("branch/", zWName) || |
| 2028 | has_prefix("tag/", zWName) || |
| 2029 | has_prefix("ticket/", zWName) )){ |
| 2030 | continue; |
| 2031 | } |
| 2032 | if( has_prefix("checkin/",zWName) || has_prefix("ticket/",zWName) ){ |
| 2033 | zWDisplayName = mprintf("%.25s...", zWName); |
| 2034 | }else{ |
| 2035 | zWDisplayName = fossil_strdup(zWName); |
| 2036 | } |
| 2037 | if( wrid==0 ){ |
| 2038 | if( !showAll ) continue; |
| 2039 | @ <tr><td data-sortkey="%h(zSort)">\ |
| 2040 | @ %z(href("%R/whistory?name=%T",zWName))<s>%h(zWDisplayName)</s></a></td> |
| @@ -2522,12 +2526,14 @@ | |
| 2526 | const int wrid = db_column_int(&q, 2); |
| 2527 | if(!showAll && !wrid){ |
| 2528 | continue; |
| 2529 | } |
| 2530 | if( !showCkBr && |
| 2531 | (has_prefix("checkin/", zName) || |
| 2532 | has_prefix("branch/", zName) || |
| 2533 | has_prefix("tag/", zName) || |
| 2534 | has_prefix("ticket/", zName) ) ){ |
| 2535 | continue; |
| 2536 | } |
| 2537 | if( showIds ){ |
| 2538 | const char *zUuid = db_column_text(&q, 1); |
| 2539 | fossil_print("%s ",zUuid); |
| 2540 |
+37
-7
| --- src/wikiformat.c | ||
| +++ src/wikiformat.c | ||
| @@ -34,10 +34,11 @@ | ||
| 34 | 34 | #define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */ |
| 35 | 35 | #define WIKI_MARKDOWNLINKS 0x080 /* Resolve hyperlinks as in markdown */ |
| 36 | 36 | #define WIKI_SAFE 0x100 /* Make the result safe for embedding */ |
| 37 | 37 | #define WIKI_TARGET_BLANK 0x200 /* Hyperlinks go to a new window */ |
| 38 | 38 | #define WIKI_NOBRACKET 0x400 /* Omit extra [..] around hyperlinks */ |
| 39 | +#define WIKI_ADMIN 0x800 /* Ignore g.perm.Hyperlink */ | |
| 39 | 40 | #endif |
| 40 | 41 | |
| 41 | 42 | |
| 42 | 43 | /* |
| 43 | 44 | ** These are the only markup attributes allowed. |
| @@ -1320,11 +1321,11 @@ | ||
| 1320 | 1321 | zTerm = ""; |
| 1321 | 1322 | }else{ |
| 1322 | 1323 | blob_appendf(pOut, "<span class=\"brokenlink\">%s", zLB); |
| 1323 | 1324 | zTerm = "]</span>"; |
| 1324 | 1325 | } |
| 1325 | - }else if( g.perm.Hyperlink ){ | |
| 1326 | + }else if( g.perm.Hyperlink || (mFlags & WIKI_ADMIN)!=0 ){ | |
| 1326 | 1327 | blob_appendf(pOut, "%z%s",xhref(zExtraNS, "%R/info/%s", zTarget), zLB); |
| 1327 | 1328 | zTerm = "]</a>"; |
| 1328 | 1329 | }else{ |
| 1329 | 1330 | zTerm = ""; |
| 1330 | 1331 | } |
| @@ -1364,10 +1365,24 @@ | ||
| 1364 | 1365 | } |
| 1365 | 1366 | if( zExtra ) fossil_free(zExtra); |
| 1366 | 1367 | assert( (int)strlen(zTerm)<nClose ); |
| 1367 | 1368 | sqlite3_snprintf(nClose, zClose, "%s", zTerm); |
| 1368 | 1369 | } |
| 1370 | + | |
| 1371 | +/* | |
| 1372 | +** Check zTarget to see if it looks like a valid hyperlink target. | |
| 1373 | +** Return true if it does seem valid and false if not. | |
| 1374 | +*/ | |
| 1375 | +int wiki_valid_link_target(char *zTarget){ | |
| 1376 | + char zClose[30]; | |
| 1377 | + Blob notUsed; | |
| 1378 | + blob_init(¬Used, 0, 0); | |
| 1379 | + wiki_resolve_hyperlink(¬Used, WIKI_NOBADLINKS|WIKI_ADMIN, | |
| 1380 | + zTarget, zClose, sizeof(zClose)-1, 0, 0); | |
| 1381 | + blob_reset(¬Used); | |
| 1382 | + return zClose[0]!=0; | |
| 1383 | +} | |
| 1369 | 1384 | |
| 1370 | 1385 | /* |
| 1371 | 1386 | ** Check to see if the given parsed markup is the correct |
| 1372 | 1387 | ** </verbatim> tag. |
| 1373 | 1388 | */ |
| @@ -1876,11 +1891,11 @@ | ||
| 1876 | 1891 | ** --htmlonly Set the WIKI_HTMLONLY flag |
| 1877 | 1892 | ** --inline Set the WIKI_INLINE flag |
| 1878 | 1893 | ** --linksonly Set the WIKI_LINKSONLY flag |
| 1879 | 1894 | ** --nobadlinks Set the WIKI_NOBADLINKS flag |
| 1880 | 1895 | ** --noblock Set the WIKI_NOBLOCK flag |
| 1881 | -** --text Run the output through html_to_plaintext(). | |
| 1896 | +** --text Run the output through html_to_plaintext(). | |
| 1882 | 1897 | */ |
| 1883 | 1898 | void test_wiki_render(void){ |
| 1884 | 1899 | Blob in, out; |
| 1885 | 1900 | int flags = 0; |
| 1886 | 1901 | int bText; |
| @@ -2474,20 +2489,21 @@ | ||
| 2474 | 2489 | blob_append_char(pOut, nNL ? '\n' : ' '); |
| 2475 | 2490 | nWS = 1; |
| 2476 | 2491 | } |
| 2477 | 2492 | } |
| 2478 | 2493 | }else if( zIn[0]=='&' ){ |
| 2479 | - char c = '?'; | |
| 2494 | + u32 c = '?'; | |
| 2480 | 2495 | if( zIn[1]=='#' ){ |
| 2481 | - int x = atoi(&zIn[1]); | |
| 2482 | - if( x>0 && x<=127 ) c = x; | |
| 2496 | + c = atoi(&zIn[2]); | |
| 2497 | + if( c==0 ) c = '?'; | |
| 2483 | 2498 | }else{ |
| 2484 | - static const struct { int n; char c; char *z; } aEntity[] = { | |
| 2499 | + static const struct { int n; u32 c; char *z; } aEntity[] = { | |
| 2485 | 2500 | { 5, '&', "&" }, |
| 2486 | 2501 | { 4, '<', "<" }, |
| 2487 | 2502 | { 4, '>', ">" }, |
| 2488 | 2503 | { 6, ' ', " " }, |
| 2504 | + { 6, '"', """ }, | |
| 2489 | 2505 | }; |
| 2490 | 2506 | int jj; |
| 2491 | 2507 | for(jj=0; jj<count(aEntity); jj++){ |
| 2492 | 2508 | if( aEntity[jj].n==n && strncmp(aEntity[jj].z,zIn,n)==0 ){ |
| 2493 | 2509 | c = aEntity[jj].c; |
| @@ -2501,11 +2517,25 @@ | ||
| 2501 | 2517 | nNL = c=='\n'; |
| 2502 | 2518 | }else{ |
| 2503 | 2519 | if( !seenText && !inTitle ) blob_append_char(pOut, '\n'); |
| 2504 | 2520 | seenText = 1; |
| 2505 | 2521 | nNL = nWS = 0; |
| 2506 | - blob_append_char(pOut, c); | |
| 2522 | + if( c<0x00080 ){ | |
| 2523 | + blob_append_char(pOut, c & 0xff); | |
| 2524 | + }else if( c<0x00800 ){ | |
| 2525 | + blob_append_char(pOut, 0xc0 + (u8)((c>>6)&0x1f)); | |
| 2526 | + blob_append_char(pOut, 0x80 + (u8)(c&0x3f)); | |
| 2527 | + }else if( c<0x10000 ){ | |
| 2528 | + blob_append_char(pOut, 0xe0 + (u8)((c>>12)&0x0f)); | |
| 2529 | + blob_append_char(pOut, 0x80 + (u8)((c>>6)&0x3f)); | |
| 2530 | + blob_append_char(pOut, 0x80 + (u8)(c&0x3f)); | |
| 2531 | + }else{ | |
| 2532 | + blob_append_char(pOut, 0xf0 + (u8)((c>>18)&0x07)); | |
| 2533 | + blob_append_char(pOut, 0x80 + (u8)((c>>12)&0x3f)); | |
| 2534 | + blob_append_char(pOut, 0x80 + (u8)((c>>6)&0x3f)); | |
| 2535 | + blob_append_char(pOut, 0x80 + (u8)(c&0x3f)); | |
| 2536 | + } | |
| 2507 | 2537 | } |
| 2508 | 2538 | }else{ |
| 2509 | 2539 | if( !seenText && !inTitle ) blob_append_char(pOut, '\n'); |
| 2510 | 2540 | seenText = 1; |
| 2511 | 2541 | nNL = nWS = 0; |
| 2512 | 2542 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -34,10 +34,11 @@ | |
| 34 | #define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */ |
| 35 | #define WIKI_MARKDOWNLINKS 0x080 /* Resolve hyperlinks as in markdown */ |
| 36 | #define WIKI_SAFE 0x100 /* Make the result safe for embedding */ |
| 37 | #define WIKI_TARGET_BLANK 0x200 /* Hyperlinks go to a new window */ |
| 38 | #define WIKI_NOBRACKET 0x400 /* Omit extra [..] around hyperlinks */ |
| 39 | #endif |
| 40 | |
| 41 | |
| 42 | /* |
| 43 | ** These are the only markup attributes allowed. |
| @@ -1320,11 +1321,11 @@ | |
| 1320 | zTerm = ""; |
| 1321 | }else{ |
| 1322 | blob_appendf(pOut, "<span class=\"brokenlink\">%s", zLB); |
| 1323 | zTerm = "]</span>"; |
| 1324 | } |
| 1325 | }else if( g.perm.Hyperlink ){ |
| 1326 | blob_appendf(pOut, "%z%s",xhref(zExtraNS, "%R/info/%s", zTarget), zLB); |
| 1327 | zTerm = "]</a>"; |
| 1328 | }else{ |
| 1329 | zTerm = ""; |
| 1330 | } |
| @@ -1364,10 +1365,24 @@ | |
| 1364 | } |
| 1365 | if( zExtra ) fossil_free(zExtra); |
| 1366 | assert( (int)strlen(zTerm)<nClose ); |
| 1367 | sqlite3_snprintf(nClose, zClose, "%s", zTerm); |
| 1368 | } |
| 1369 | |
| 1370 | /* |
| 1371 | ** Check to see if the given parsed markup is the correct |
| 1372 | ** </verbatim> tag. |
| 1373 | */ |
| @@ -1876,11 +1891,11 @@ | |
| 1876 | ** --htmlonly Set the WIKI_HTMLONLY flag |
| 1877 | ** --inline Set the WIKI_INLINE flag |
| 1878 | ** --linksonly Set the WIKI_LINKSONLY flag |
| 1879 | ** --nobadlinks Set the WIKI_NOBADLINKS flag |
| 1880 | ** --noblock Set the WIKI_NOBLOCK flag |
| 1881 | ** --text Run the output through html_to_plaintext(). |
| 1882 | */ |
| 1883 | void test_wiki_render(void){ |
| 1884 | Blob in, out; |
| 1885 | int flags = 0; |
| 1886 | int bText; |
| @@ -2474,20 +2489,21 @@ | |
| 2474 | blob_append_char(pOut, nNL ? '\n' : ' '); |
| 2475 | nWS = 1; |
| 2476 | } |
| 2477 | } |
| 2478 | }else if( zIn[0]=='&' ){ |
| 2479 | char c = '?'; |
| 2480 | if( zIn[1]=='#' ){ |
| 2481 | int x = atoi(&zIn[1]); |
| 2482 | if( x>0 && x<=127 ) c = x; |
| 2483 | }else{ |
| 2484 | static const struct { int n; char c; char *z; } aEntity[] = { |
| 2485 | { 5, '&', "&" }, |
| 2486 | { 4, '<', "<" }, |
| 2487 | { 4, '>', ">" }, |
| 2488 | { 6, ' ', " " }, |
| 2489 | }; |
| 2490 | int jj; |
| 2491 | for(jj=0; jj<count(aEntity); jj++){ |
| 2492 | if( aEntity[jj].n==n && strncmp(aEntity[jj].z,zIn,n)==0 ){ |
| 2493 | c = aEntity[jj].c; |
| @@ -2501,11 +2517,25 @@ | |
| 2501 | nNL = c=='\n'; |
| 2502 | }else{ |
| 2503 | if( !seenText && !inTitle ) blob_append_char(pOut, '\n'); |
| 2504 | seenText = 1; |
| 2505 | nNL = nWS = 0; |
| 2506 | blob_append_char(pOut, c); |
| 2507 | } |
| 2508 | }else{ |
| 2509 | if( !seenText && !inTitle ) blob_append_char(pOut, '\n'); |
| 2510 | seenText = 1; |
| 2511 | nNL = nWS = 0; |
| 2512 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -34,10 +34,11 @@ | |
| 34 | #define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */ |
| 35 | #define WIKI_MARKDOWNLINKS 0x080 /* Resolve hyperlinks as in markdown */ |
| 36 | #define WIKI_SAFE 0x100 /* Make the result safe for embedding */ |
| 37 | #define WIKI_TARGET_BLANK 0x200 /* Hyperlinks go to a new window */ |
| 38 | #define WIKI_NOBRACKET 0x400 /* Omit extra [..] around hyperlinks */ |
| 39 | #define WIKI_ADMIN 0x800 /* Ignore g.perm.Hyperlink */ |
| 40 | #endif |
| 41 | |
| 42 | |
| 43 | /* |
| 44 | ** These are the only markup attributes allowed. |
| @@ -1320,11 +1321,11 @@ | |
| 1321 | zTerm = ""; |
| 1322 | }else{ |
| 1323 | blob_appendf(pOut, "<span class=\"brokenlink\">%s", zLB); |
| 1324 | zTerm = "]</span>"; |
| 1325 | } |
| 1326 | }else if( g.perm.Hyperlink || (mFlags & WIKI_ADMIN)!=0 ){ |
| 1327 | blob_appendf(pOut, "%z%s",xhref(zExtraNS, "%R/info/%s", zTarget), zLB); |
| 1328 | zTerm = "]</a>"; |
| 1329 | }else{ |
| 1330 | zTerm = ""; |
| 1331 | } |
| @@ -1364,10 +1365,24 @@ | |
| 1365 | } |
| 1366 | if( zExtra ) fossil_free(zExtra); |
| 1367 | assert( (int)strlen(zTerm)<nClose ); |
| 1368 | sqlite3_snprintf(nClose, zClose, "%s", zTerm); |
| 1369 | } |
| 1370 | |
| 1371 | /* |
| 1372 | ** Check zTarget to see if it looks like a valid hyperlink target. |
| 1373 | ** Return true if it does seem valid and false if not. |
| 1374 | */ |
| 1375 | int wiki_valid_link_target(char *zTarget){ |
| 1376 | char zClose[30]; |
| 1377 | Blob notUsed; |
| 1378 | blob_init(¬Used, 0, 0); |
| 1379 | wiki_resolve_hyperlink(¬Used, WIKI_NOBADLINKS|WIKI_ADMIN, |
| 1380 | zTarget, zClose, sizeof(zClose)-1, 0, 0); |
| 1381 | blob_reset(¬Used); |
| 1382 | return zClose[0]!=0; |
| 1383 | } |
| 1384 | |
| 1385 | /* |
| 1386 | ** Check to see if the given parsed markup is the correct |
| 1387 | ** </verbatim> tag. |
| 1388 | */ |
| @@ -1876,11 +1891,11 @@ | |
| 1891 | ** --htmlonly Set the WIKI_HTMLONLY flag |
| 1892 | ** --inline Set the WIKI_INLINE flag |
| 1893 | ** --linksonly Set the WIKI_LINKSONLY flag |
| 1894 | ** --nobadlinks Set the WIKI_NOBADLINKS flag |
| 1895 | ** --noblock Set the WIKI_NOBLOCK flag |
| 1896 | ** --text Run the output through html_to_plaintext(). |
| 1897 | */ |
| 1898 | void test_wiki_render(void){ |
| 1899 | Blob in, out; |
| 1900 | int flags = 0; |
| 1901 | int bText; |
| @@ -2474,20 +2489,21 @@ | |
| 2489 | blob_append_char(pOut, nNL ? '\n' : ' '); |
| 2490 | nWS = 1; |
| 2491 | } |
| 2492 | } |
| 2493 | }else if( zIn[0]=='&' ){ |
| 2494 | u32 c = '?'; |
| 2495 | if( zIn[1]=='#' ){ |
| 2496 | c = atoi(&zIn[2]); |
| 2497 | if( c==0 ) c = '?'; |
| 2498 | }else{ |
| 2499 | static const struct { int n; u32 c; char *z; } aEntity[] = { |
| 2500 | { 5, '&', "&" }, |
| 2501 | { 4, '<', "<" }, |
| 2502 | { 4, '>', ">" }, |
| 2503 | { 6, ' ', " " }, |
| 2504 | { 6, '"', """ }, |
| 2505 | }; |
| 2506 | int jj; |
| 2507 | for(jj=0; jj<count(aEntity); jj++){ |
| 2508 | if( aEntity[jj].n==n && strncmp(aEntity[jj].z,zIn,n)==0 ){ |
| 2509 | c = aEntity[jj].c; |
| @@ -2501,11 +2517,25 @@ | |
| 2517 | nNL = c=='\n'; |
| 2518 | }else{ |
| 2519 | if( !seenText && !inTitle ) blob_append_char(pOut, '\n'); |
| 2520 | seenText = 1; |
| 2521 | nNL = nWS = 0; |
| 2522 | if( c<0x00080 ){ |
| 2523 | blob_append_char(pOut, c & 0xff); |
| 2524 | }else if( c<0x00800 ){ |
| 2525 | blob_append_char(pOut, 0xc0 + (u8)((c>>6)&0x1f)); |
| 2526 | blob_append_char(pOut, 0x80 + (u8)(c&0x3f)); |
| 2527 | }else if( c<0x10000 ){ |
| 2528 | blob_append_char(pOut, 0xe0 + (u8)((c>>12)&0x0f)); |
| 2529 | blob_append_char(pOut, 0x80 + (u8)((c>>6)&0x3f)); |
| 2530 | blob_append_char(pOut, 0x80 + (u8)(c&0x3f)); |
| 2531 | }else{ |
| 2532 | blob_append_char(pOut, 0xf0 + (u8)((c>>18)&0x07)); |
| 2533 | blob_append_char(pOut, 0x80 + (u8)((c>>12)&0x3f)); |
| 2534 | blob_append_char(pOut, 0x80 + (u8)((c>>6)&0x3f)); |
| 2535 | blob_append_char(pOut, 0x80 + (u8)(c&0x3f)); |
| 2536 | } |
| 2537 | } |
| 2538 | }else{ |
| 2539 | if( !seenText && !inTitle ) blob_append_char(pOut, '\n'); |
| 2540 | seenText = 1; |
| 2541 | nNL = nWS = 0; |
| 2542 |
+1
-1
| --- src/zip.c | ||
| +++ src/zip.c | ||
| @@ -652,11 +652,11 @@ | ||
| 652 | 652 | pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 653 | 653 | if( pManifest ){ |
| 654 | 654 | int flg, eflg = 0; |
| 655 | 655 | char *zName = 0; |
| 656 | 656 | zip_set_timedate(pManifest->rDate); |
| 657 | - flg = db_get_manifest_setting(); | |
| 657 | + flg = db_get_manifest_setting(blob_str(&hash)); | |
| 658 | 658 | if( flg ){ |
| 659 | 659 | /* eflg is the effective flags, taking include/exclude into account */ |
| 660 | 660 | if( (pInclude==0 || glob_match(pInclude, "manifest")) |
| 661 | 661 | && !glob_match(pExclude, "manifest") |
| 662 | 662 | && (flg & MFESTFLG_RAW) ){ |
| 663 | 663 |
| --- src/zip.c | |
| +++ src/zip.c | |
| @@ -652,11 +652,11 @@ | |
| 652 | pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 653 | if( pManifest ){ |
| 654 | int flg, eflg = 0; |
| 655 | char *zName = 0; |
| 656 | zip_set_timedate(pManifest->rDate); |
| 657 | flg = db_get_manifest_setting(); |
| 658 | if( flg ){ |
| 659 | /* eflg is the effective flags, taking include/exclude into account */ |
| 660 | if( (pInclude==0 || glob_match(pInclude, "manifest")) |
| 661 | && !glob_match(pExclude, "manifest") |
| 662 | && (flg & MFESTFLG_RAW) ){ |
| 663 |
| --- src/zip.c | |
| +++ src/zip.c | |
| @@ -652,11 +652,11 @@ | |
| 652 | pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 653 | if( pManifest ){ |
| 654 | int flg, eflg = 0; |
| 655 | char *zName = 0; |
| 656 | zip_set_timedate(pManifest->rDate); |
| 657 | flg = db_get_manifest_setting(blob_str(&hash)); |
| 658 | if( flg ){ |
| 659 | /* eflg is the effective flags, taking include/exclude into account */ |
| 660 | if( (pInclude==0 || glob_match(pInclude, "manifest")) |
| 661 | && !glob_match(pExclude, "manifest") |
| 662 | && (flg & MFESTFLG_RAW) ){ |
| 663 |
+18
-17
| --- tools/mkindex.c | ||
| +++ tools/mkindex.c | ||
| @@ -85,26 +85,26 @@ | ||
| 85 | 85 | |
| 86 | 86 | /*************************************************************************** |
| 87 | 87 | ** These macros must match similar macros in dispatch.c. |
| 88 | 88 | ** |
| 89 | 89 | ** Allowed values for CmdOrPage.eCmdFlags. */ |
| 90 | -#define CMDFLAG_1ST_TIER 0x0001 /* Most important commands */ | |
| 91 | -#define CMDFLAG_2ND_TIER 0x0002 /* Obscure and seldom used commands */ | |
| 92 | -#define CMDFLAG_TEST 0x0004 /* Commands for testing only */ | |
| 93 | -#define CMDFLAG_WEBPAGE 0x0008 /* Web pages */ | |
| 94 | -#define CMDFLAG_COMMAND 0x0010 /* A command */ | |
| 95 | -#define CMDFLAG_SETTING 0x0020 /* A setting */ | |
| 96 | -#define CMDFLAG_VERSIONABLE 0x0040 /* A versionable setting */ | |
| 97 | -#define CMDFLAG_BLOCKTEXT 0x0080 /* Multi-line text setting */ | |
| 98 | -#define CMDFLAG_BOOLEAN 0x0100 /* A boolean setting */ | |
| 99 | -#define CMDFLAG_RAWCONTENT 0x0200 /* Do not interpret webpage content */ | |
| 100 | -#define CMDFLAG_SENSITIVE 0x0400 /* Security-sensitive setting */ | |
| 101 | -#define CMDFLAG_HIDDEN 0x0800 /* Elide from most listings */ | |
| 102 | -#define CMDFLAG_LDAVG_EXEMPT 0x1000 /* Exempt from load_control() */ | |
| 103 | -#define CMDFLAG_ALIAS 0x2000 /* Command aliases */ | |
| 104 | -#define CMDFLAG_KEEPEMPTY 0x4000 /* Do not unset empty settings */ | |
| 105 | -#define CMDFLAG_ABBREVSUBCMD 0x8000 /* Abbreviated subcmd in help text */ | |
| 90 | +#define CMDFLAG_1ST_TIER 0x00001 /* Most important commands */ | |
| 91 | +#define CMDFLAG_2ND_TIER 0x00002 /* Obscure and seldom used commands */ | |
| 92 | +#define CMDFLAG_TEST 0x00004 /* Commands for testing only */ | |
| 93 | +#define CMDFLAG_WEBPAGE 0x00008 /* Web pages */ | |
| 94 | +#define CMDFLAG_COMMAND 0x00010 /* A command */ | |
| 95 | +#define CMDFLAG_SETTING 0x00020 /* A setting */ | |
| 96 | +#define CMDFLAG_VERSIONABLE 0x00040 /* A versionable setting */ | |
| 97 | +#define CMDFLAG_BLOCKTEXT 0x00080 /* Multi-line text setting */ | |
| 98 | +#define CMDFLAG_BOOLEAN 0x00100 /* A boolean setting */ | |
| 99 | +#define CMDFLAG_RAWCONTENT 0x00200 /* Do not interpret webpage content */ | |
| 100 | +#define CMDFLAG_SENSITIVE 0x00400 /* Security-sensitive setting */ | |
| 101 | +#define CMDFLAG_HIDDEN 0x00800 /* Elide from most listings */ | |
| 102 | +#define CMDFLAG_LDAVG_EXEMPT 0x01000 /* Exempt from load_control() */ | |
| 103 | +#define CMDFLAG_ALIAS 0x02000 /* Command aliases */ | |
| 104 | +#define CMDFLAG_KEEPEMPTY 0x04000 /* Do not unset empty settings */ | |
| 105 | +#define CMDFLAG_ABBREVSUBCMD 0x08000 /* Abbreviated subcmd in help text */ | |
| 106 | 106 | /**************************************************************************/ |
| 107 | 107 | |
| 108 | 108 | /* |
| 109 | 109 | ** Each entry looks like this: |
| 110 | 110 | */ |
| @@ -281,11 +281,12 @@ | ||
| 281 | 281 | aEntry[nUsed].zVar = string_dup(&zLine[i+9], j-9); |
| 282 | 282 | }else if( j==6 && strncmp(&zLine[i], "hidden", 6)==0 ){ |
| 283 | 283 | aEntry[nUsed].eType |= CMDFLAG_HIDDEN; |
| 284 | 284 | }else if( j==14 && strncmp(&zLine[i], "loadavg-exempt", 14)==0 ){ |
| 285 | 285 | aEntry[nUsed].eType |= CMDFLAG_LDAVG_EXEMPT; |
| 286 | - }else if( j==23 && strncmp(&zLine[i], "abbreviated-subcommands", 23)==0 ){ | |
| 286 | + }else if( (j==23 && strncmp(&zLine[i], "abbreviated-subcommands", 23)==0) | |
| 287 | + || (j==12 && strncmp(&zLine[i], "abbrv-subcom", 12)==0) ){ | |
| 287 | 288 | aEntry[nUsed].eType |= CMDFLAG_ABBREVSUBCMD; |
| 288 | 289 | }else{ |
| 289 | 290 | fprintf(stderr, "%s:%d: unknown option: '%.*s'\n", |
| 290 | 291 | zFile, nLine, j, &zLine[i]); |
| 291 | 292 | nErr++; |
| 292 | 293 |
| --- tools/mkindex.c | |
| +++ tools/mkindex.c | |
| @@ -85,26 +85,26 @@ | |
| 85 | |
| 86 | /*************************************************************************** |
| 87 | ** These macros must match similar macros in dispatch.c. |
| 88 | ** |
| 89 | ** Allowed values for CmdOrPage.eCmdFlags. */ |
| 90 | #define CMDFLAG_1ST_TIER 0x0001 /* Most important commands */ |
| 91 | #define CMDFLAG_2ND_TIER 0x0002 /* Obscure and seldom used commands */ |
| 92 | #define CMDFLAG_TEST 0x0004 /* Commands for testing only */ |
| 93 | #define CMDFLAG_WEBPAGE 0x0008 /* Web pages */ |
| 94 | #define CMDFLAG_COMMAND 0x0010 /* A command */ |
| 95 | #define CMDFLAG_SETTING 0x0020 /* A setting */ |
| 96 | #define CMDFLAG_VERSIONABLE 0x0040 /* A versionable setting */ |
| 97 | #define CMDFLAG_BLOCKTEXT 0x0080 /* Multi-line text setting */ |
| 98 | #define CMDFLAG_BOOLEAN 0x0100 /* A boolean setting */ |
| 99 | #define CMDFLAG_RAWCONTENT 0x0200 /* Do not interpret webpage content */ |
| 100 | #define CMDFLAG_SENSITIVE 0x0400 /* Security-sensitive setting */ |
| 101 | #define CMDFLAG_HIDDEN 0x0800 /* Elide from most listings */ |
| 102 | #define CMDFLAG_LDAVG_EXEMPT 0x1000 /* Exempt from load_control() */ |
| 103 | #define CMDFLAG_ALIAS 0x2000 /* Command aliases */ |
| 104 | #define CMDFLAG_KEEPEMPTY 0x4000 /* Do not unset empty settings */ |
| 105 | #define CMDFLAG_ABBREVSUBCMD 0x8000 /* Abbreviated subcmd in help text */ |
| 106 | /**************************************************************************/ |
| 107 | |
| 108 | /* |
| 109 | ** Each entry looks like this: |
| 110 | */ |
| @@ -281,11 +281,12 @@ | |
| 281 | aEntry[nUsed].zVar = string_dup(&zLine[i+9], j-9); |
| 282 | }else if( j==6 && strncmp(&zLine[i], "hidden", 6)==0 ){ |
| 283 | aEntry[nUsed].eType |= CMDFLAG_HIDDEN; |
| 284 | }else if( j==14 && strncmp(&zLine[i], "loadavg-exempt", 14)==0 ){ |
| 285 | aEntry[nUsed].eType |= CMDFLAG_LDAVG_EXEMPT; |
| 286 | }else if( j==23 && strncmp(&zLine[i], "abbreviated-subcommands", 23)==0 ){ |
| 287 | aEntry[nUsed].eType |= CMDFLAG_ABBREVSUBCMD; |
| 288 | }else{ |
| 289 | fprintf(stderr, "%s:%d: unknown option: '%.*s'\n", |
| 290 | zFile, nLine, j, &zLine[i]); |
| 291 | nErr++; |
| 292 |
| --- tools/mkindex.c | |
| +++ tools/mkindex.c | |
| @@ -85,26 +85,26 @@ | |
| 85 | |
| 86 | /*************************************************************************** |
| 87 | ** These macros must match similar macros in dispatch.c. |
| 88 | ** |
| 89 | ** Allowed values for CmdOrPage.eCmdFlags. */ |
| 90 | #define CMDFLAG_1ST_TIER 0x00001 /* Most important commands */ |
| 91 | #define CMDFLAG_2ND_TIER 0x00002 /* Obscure and seldom used commands */ |
| 92 | #define CMDFLAG_TEST 0x00004 /* Commands for testing only */ |
| 93 | #define CMDFLAG_WEBPAGE 0x00008 /* Web pages */ |
| 94 | #define CMDFLAG_COMMAND 0x00010 /* A command */ |
| 95 | #define CMDFLAG_SETTING 0x00020 /* A setting */ |
| 96 | #define CMDFLAG_VERSIONABLE 0x00040 /* A versionable setting */ |
| 97 | #define CMDFLAG_BLOCKTEXT 0x00080 /* Multi-line text setting */ |
| 98 | #define CMDFLAG_BOOLEAN 0x00100 /* A boolean setting */ |
| 99 | #define CMDFLAG_RAWCONTENT 0x00200 /* Do not interpret webpage content */ |
| 100 | #define CMDFLAG_SENSITIVE 0x00400 /* Security-sensitive setting */ |
| 101 | #define CMDFLAG_HIDDEN 0x00800 /* Elide from most listings */ |
| 102 | #define CMDFLAG_LDAVG_EXEMPT 0x01000 /* Exempt from load_control() */ |
| 103 | #define CMDFLAG_ALIAS 0x02000 /* Command aliases */ |
| 104 | #define CMDFLAG_KEEPEMPTY 0x04000 /* Do not unset empty settings */ |
| 105 | #define CMDFLAG_ABBREVSUBCMD 0x08000 /* Abbreviated subcmd in help text */ |
| 106 | /**************************************************************************/ |
| 107 | |
| 108 | /* |
| 109 | ** Each entry looks like this: |
| 110 | */ |
| @@ -281,11 +281,12 @@ | |
| 281 | aEntry[nUsed].zVar = string_dup(&zLine[i+9], j-9); |
| 282 | }else if( j==6 && strncmp(&zLine[i], "hidden", 6)==0 ){ |
| 283 | aEntry[nUsed].eType |= CMDFLAG_HIDDEN; |
| 284 | }else if( j==14 && strncmp(&zLine[i], "loadavg-exempt", 14)==0 ){ |
| 285 | aEntry[nUsed].eType |= CMDFLAG_LDAVG_EXEMPT; |
| 286 | }else if( (j==23 && strncmp(&zLine[i], "abbreviated-subcommands", 23)==0) |
| 287 | || (j==12 && strncmp(&zLine[i], "abbrv-subcom", 12)==0) ){ |
| 288 | aEntry[nUsed].eType |= CMDFLAG_ABBREVSUBCMD; |
| 289 | }else{ |
| 290 | fprintf(stderr, "%s:%d: unknown option: '%.*s'\n", |
| 291 | zFile, nLine, j, &zLine[i]); |
| 292 | nErr++; |
| 293 |
+5
| --- www/alerts.md | ||
| +++ www/alerts.md | ||
| @@ -7,10 +7,11 @@ | ||
| 7 | 7 | |
| 8 | 8 | * New [checkins](/help?cmd=ci) |
| 9 | 9 | * [Ticket](./tickets.wiki) changes |
| 10 | 10 | * [Wiki](./wikitheory.wiki) page changes |
| 11 | 11 | * New and edited [forum](./forum.wiki) posts |
| 12 | + * Users receiving [new permissions](./caps/index.md) (admins only) | |
| 12 | 13 | * Announcements |
| 13 | 14 | |
| 14 | 15 | Subscribers can elect to receive emails as soon as these events happen, |
| 15 | 16 | or they can receive a daily digest of the events instead. |
| 16 | 17 | |
| @@ -515,10 +516,14 @@ | ||
| 515 | 516 | email addresses until the user clicks the link in the verification |
| 516 | 517 | email. This checkbox lets the Fossil Admin user manually verify the |
| 517 | 518 | user, such as in the case where the verification email message got |
| 518 | 519 | lost. Unchecking this box does not cause another verification email |
| 519 | 520 | to be sent. |
| 521 | + | |
| 522 | +* Admin users (only) may activate the "user elevation" subscription, | |
| 523 | + which sends a notification when a user is created or is explicitly | |
| 524 | + assigned permission they did not formerly have. | |
| 520 | 525 | |
| 521 | 526 | This screen also allows a Fossil Admin user to perform other activities |
| 522 | 527 | on behalf of a subscriber which they could do themselves, such as to |
| 523 | 528 | [unsubscribe](#unsub) them. |
| 524 | 529 | |
| 525 | 530 |
| --- www/alerts.md | |
| +++ www/alerts.md | |
| @@ -7,10 +7,11 @@ | |
| 7 | |
| 8 | * New [checkins](/help?cmd=ci) |
| 9 | * [Ticket](./tickets.wiki) changes |
| 10 | * [Wiki](./wikitheory.wiki) page changes |
| 11 | * New and edited [forum](./forum.wiki) posts |
| 12 | * Announcements |
| 13 | |
| 14 | Subscribers can elect to receive emails as soon as these events happen, |
| 15 | or they can receive a daily digest of the events instead. |
| 16 | |
| @@ -515,10 +516,14 @@ | |
| 515 | email addresses until the user clicks the link in the verification |
| 516 | email. This checkbox lets the Fossil Admin user manually verify the |
| 517 | user, such as in the case where the verification email message got |
| 518 | lost. Unchecking this box does not cause another verification email |
| 519 | to be sent. |
| 520 | |
| 521 | This screen also allows a Fossil Admin user to perform other activities |
| 522 | on behalf of a subscriber which they could do themselves, such as to |
| 523 | [unsubscribe](#unsub) them. |
| 524 | |
| 525 |
| --- www/alerts.md | |
| +++ www/alerts.md | |
| @@ -7,10 +7,11 @@ | |
| 7 | |
| 8 | * New [checkins](/help?cmd=ci) |
| 9 | * [Ticket](./tickets.wiki) changes |
| 10 | * [Wiki](./wikitheory.wiki) page changes |
| 11 | * New and edited [forum](./forum.wiki) posts |
| 12 | * Users receiving [new permissions](./caps/index.md) (admins only) |
| 13 | * Announcements |
| 14 | |
| 15 | Subscribers can elect to receive emails as soon as these events happen, |
| 16 | or they can receive a daily digest of the events instead. |
| 17 | |
| @@ -515,10 +516,14 @@ | |
| 516 | email addresses until the user clicks the link in the verification |
| 517 | email. This checkbox lets the Fossil Admin user manually verify the |
| 518 | user, such as in the case where the verification email message got |
| 519 | lost. Unchecking this box does not cause another verification email |
| 520 | to be sent. |
| 521 | |
| 522 | * Admin users (only) may activate the "user elevation" subscription, |
| 523 | which sends a notification when a user is created or is explicitly |
| 524 | assigned permission they did not formerly have. |
| 525 | |
| 526 | This screen also allows a Fossil Admin user to perform other activities |
| 527 | on behalf of a subscriber which they could do themselves, such as to |
| 528 | [unsubscribe](#unsub) them. |
| 529 | |
| 530 |
+48
-17
| --- www/changes.wiki | ||
| +++ www/changes.wiki | ||
| @@ -1,28 +1,46 @@ | ||
| 1 | 1 | <title>Change Log</title> |
| 2 | 2 | |
| 3 | 3 | <h2 id='v2_26'>Changes for version 2.26 (pending)</h2> |
| 4 | 4 | |
| 5 | - * Enhanced the --from option on "[/help?cmd=diff|fossil diff]" so that | |
| 6 | - it optionally accepts a directory name as its argument, and uses files | |
| 7 | - under that directory as the baseline for the diff. | |
| 5 | + * Enhancements to [/help?cmd=diff|fossil diff] and similar: | |
| 6 | + <ol type="a"> | |
| 7 | + <li> The --from can optionally accepts a directory name as its argument, | |
| 8 | + and uses files under that directory as the baseline for the diff. | |
| 9 | + <li> For "gdiff", if no [/help?cmd=gdiff-command|gdiff-command setting] | |
| 10 | + is defined, Fossil tries to do a --tk diff if "tclsh" and "wish" | |
| 11 | + are available, or a --by diff if not. | |
| 12 | + <li> The "Reload" button is added to --tk diffs, to bring the displayed | |
| 13 | + diff up to date with the latest changes on disk. | |
| 14 | + <li> Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show | |
| 15 | + diffs of multiple files. | |
| 16 | + </ol> | |
| 8 | 17 | * Added the [/help?cmd=/ckout|/ckout web page] to provide information |
| 9 | 18 | about pending changes in a working check-out |
| 10 | 19 | * The [/help?cmd=ui|fossil ui] command defaults to using the |
| 11 | 20 | [/help?cmd=/ckout|/ckout page] as its start page. Or, if the |
| 12 | 21 | "--from PATH" option is present, the default start page becomes |
| 13 | 22 | "/ckout?exbase=PATH". |
| 14 | - * Added the [/help?cmd=merge-info|fossil merge-info] command and especially | |
| 15 | - the --tk option to that command, to provide analysis of the most recent | |
| 16 | - merge or update operation. | |
| 17 | - * Added the ability to sign check-ins with SSH keys. | |
| 18 | - * Issue a warning if a user tries to commit on a check-in where the | |
| 19 | - branch has been changed. | |
| 20 | - * When a merge conflict occurs, a new section is added to the conflict | |
| 21 | - text that shows Fossil's suggested resolution to the conflict. | |
| 22 | - * Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show | |
| 23 | - diffs of multiple files. | |
| 23 | + * Enhancements to [/help?cmd=merge|fossil merge]: | |
| 24 | + <ol type="a"> | |
| 25 | + <li> Added the [/help?cmd=merge-info|fossil merge-info] command and especially | |
| 26 | + the --tk option to that command, to provide analysis of the most recent | |
| 27 | + merge or update operation. | |
| 28 | + <li> When a merge conflict occurs, a new section is added to the conflict | |
| 29 | + text that shows Fossil's suggested resolution to the conflict. | |
| 30 | + </ol> | |
| 31 | + * Enhancements to [/help?cmd=commit|fossil commit]: | |
| 32 | + <ol type="a"> | |
| 33 | + <li> If Fossil sees potential formatting mistakes (bad hyperlinks) | |
| 34 | + in the check-in comment, it will alert the developer and give | |
| 35 | + him or her the opportunity to edit the comment before continuing. | |
| 36 | + <li> The new "--if-changes" option causes the commit to become | |
| 37 | + a quiet no-op if there are no pending changes. | |
| 38 | + <li> Added the ability to sign check-ins with SSH keys. | |
| 39 | + <li> Issue a warning if a user tries to commit on a check-in where the | |
| 40 | + branch has been changed. | |
| 41 | + </ol> | |
| 24 | 42 | * Deprecate the --comfmtflags and --comment-format global options and |
| 25 | 43 | no longer list them in the built-in help, but keep them working for |
| 26 | 44 | backwards compatibility. |
| 27 | 45 | Alternative TTY comment formatting can still be specified using the |
| 28 | 46 | [/help?cmd=comment-format|comment-format setting], if desired. The |
| @@ -49,30 +67,43 @@ | ||
| 49 | 67 | <li> Enhance the "ymd" query parameter so that when used like |
| 50 | 68 | "ymd=YYYYMMDD-YYYYMMDD" it shows all events in the range of |
| 51 | 69 | dates specified. |
| 52 | 70 | <li> Accept the "Z" (Zulu-time) suffix on date arguments for the |
| 53 | 71 | "ymd" and "yw" query parameters. |
| 72 | + <li> The new "min" query parameter, when added to a from=,to= query, | |
| 73 | + collapses long runs of check-ins on the same branch into just | |
| 74 | + end-points. | |
| 75 | + <li> The p= and d= parameters an reference different check-ins, which | |
| 76 | + case the timeline shows those check-ins that are both ancestors | |
| 77 | + of p= and descendants of d=. | |
| 54 | 78 | </ol> |
| 55 | - * Add the "--if-changes" option to the [/help?cmd=commit|fossil commit] | |
| 56 | - command that causes the command to become a quiet no-op if there are | |
| 57 | - no pending changes. | |
| 58 | 79 | * Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis |
| 59 | 80 | and debugging |
| 60 | 81 | * Fix a bug in [/help?cmd=patch|fossil patch create] that causes |
| 61 | 82 | [/help?cmd=revert|fossil revert] operations that happened on individual |
| 62 | 83 | files after a [/help?cmd=merge|fossil merge] to be omitted from the |
| 63 | 84 | patch. |
| 64 | 85 | * Added the [/help?cmd=patch|patch alias] command for managing aliases |
| 65 | 86 | for remote checkout names. |
| 66 | - * Enhance the [/help?cmd=help|fossil help] command: | |
| 87 | + * Enhancements to on-line help and the [/help?cmd=help|fossil help] command: | |
| 67 | 88 | <ol type="a"> |
| 89 | + <li> Add the ability to search the help text, either in the UI | |
| 90 | + (on the [/help?cmd=/search|/search page]) or from the command-line | |
| 91 | + (using the "[/help?cmd=search|fossil search -h PATTERN]" command.) | |
| 68 | 92 | <li> Accepts an optional SUBCOMMAND argument following the |
| 69 | 93 | COMMAND argument and only shows results for the specified |
| 70 | 94 | subcommand, not the entire command. |
| 71 | 95 | <li> The -u (--usage) option shows only the command-line syntax |
| 72 | 96 | <li> The -o (--options) option shows only the command-line options |
| 73 | 97 | </ol> |
| 98 | + * Added the ability to attach wiki pages to a ticket for extended | |
| 99 | + descriptions. | |
| 100 | + * Add a "user elevation" [/doc/trunk/www/alerts.md|subscription] | |
| 101 | + which alerts subscribers when an admin creates a new user or | |
| 102 | + adds new permissions to one. | |
| 103 | + * Diverse minor fixes and additions. | |
| 104 | + | |
| 74 | 105 | |
| 75 | 106 | <h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2> |
| 76 | 107 | |
| 77 | 108 | * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories |
| 78 | 109 | that have non-ASCII filenames |
| 79 | 110 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -1,28 +1,46 @@ | |
| 1 | <title>Change Log</title> |
| 2 | |
| 3 | <h2 id='v2_26'>Changes for version 2.26 (pending)</h2> |
| 4 | |
| 5 | * Enhanced the --from option on "[/help?cmd=diff|fossil diff]" so that |
| 6 | it optionally accepts a directory name as its argument, and uses files |
| 7 | under that directory as the baseline for the diff. |
| 8 | * Added the [/help?cmd=/ckout|/ckout web page] to provide information |
| 9 | about pending changes in a working check-out |
| 10 | * The [/help?cmd=ui|fossil ui] command defaults to using the |
| 11 | [/help?cmd=/ckout|/ckout page] as its start page. Or, if the |
| 12 | "--from PATH" option is present, the default start page becomes |
| 13 | "/ckout?exbase=PATH". |
| 14 | * Added the [/help?cmd=merge-info|fossil merge-info] command and especially |
| 15 | the --tk option to that command, to provide analysis of the most recent |
| 16 | merge or update operation. |
| 17 | * Added the ability to sign check-ins with SSH keys. |
| 18 | * Issue a warning if a user tries to commit on a check-in where the |
| 19 | branch has been changed. |
| 20 | * When a merge conflict occurs, a new section is added to the conflict |
| 21 | text that shows Fossil's suggested resolution to the conflict. |
| 22 | * Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show |
| 23 | diffs of multiple files. |
| 24 | * Deprecate the --comfmtflags and --comment-format global options and |
| 25 | no longer list them in the built-in help, but keep them working for |
| 26 | backwards compatibility. |
| 27 | Alternative TTY comment formatting can still be specified using the |
| 28 | [/help?cmd=comment-format|comment-format setting], if desired. The |
| @@ -49,30 +67,43 @@ | |
| 49 | <li> Enhance the "ymd" query parameter so that when used like |
| 50 | "ymd=YYYYMMDD-YYYYMMDD" it shows all events in the range of |
| 51 | dates specified. |
| 52 | <li> Accept the "Z" (Zulu-time) suffix on date arguments for the |
| 53 | "ymd" and "yw" query parameters. |
| 54 | </ol> |
| 55 | * Add the "--if-changes" option to the [/help?cmd=commit|fossil commit] |
| 56 | command that causes the command to become a quiet no-op if there are |
| 57 | no pending changes. |
| 58 | * Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis |
| 59 | and debugging |
| 60 | * Fix a bug in [/help?cmd=patch|fossil patch create] that causes |
| 61 | [/help?cmd=revert|fossil revert] operations that happened on individual |
| 62 | files after a [/help?cmd=merge|fossil merge] to be omitted from the |
| 63 | patch. |
| 64 | * Added the [/help?cmd=patch|patch alias] command for managing aliases |
| 65 | for remote checkout names. |
| 66 | * Enhance the [/help?cmd=help|fossil help] command: |
| 67 | <ol type="a"> |
| 68 | <li> Accepts an optional SUBCOMMAND argument following the |
| 69 | COMMAND argument and only shows results for the specified |
| 70 | subcommand, not the entire command. |
| 71 | <li> The -u (--usage) option shows only the command-line syntax |
| 72 | <li> The -o (--options) option shows only the command-line options |
| 73 | </ol> |
| 74 | |
| 75 | <h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2> |
| 76 | |
| 77 | * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories |
| 78 | that have non-ASCII filenames |
| 79 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -1,28 +1,46 @@ | |
| 1 | <title>Change Log</title> |
| 2 | |
| 3 | <h2 id='v2_26'>Changes for version 2.26 (pending)</h2> |
| 4 | |
| 5 | * Enhancements to [/help?cmd=diff|fossil diff] and similar: |
| 6 | <ol type="a"> |
| 7 | <li> The --from can optionally accepts a directory name as its argument, |
| 8 | and uses files under that directory as the baseline for the diff. |
| 9 | <li> For "gdiff", if no [/help?cmd=gdiff-command|gdiff-command setting] |
| 10 | is defined, Fossil tries to do a --tk diff if "tclsh" and "wish" |
| 11 | are available, or a --by diff if not. |
| 12 | <li> The "Reload" button is added to --tk diffs, to bring the displayed |
| 13 | diff up to date with the latest changes on disk. |
| 14 | <li> Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show |
| 15 | diffs of multiple files. |
| 16 | </ol> |
| 17 | * Added the [/help?cmd=/ckout|/ckout web page] to provide information |
| 18 | about pending changes in a working check-out |
| 19 | * The [/help?cmd=ui|fossil ui] command defaults to using the |
| 20 | [/help?cmd=/ckout|/ckout page] as its start page. Or, if the |
| 21 | "--from PATH" option is present, the default start page becomes |
| 22 | "/ckout?exbase=PATH". |
| 23 | * Enhancements to [/help?cmd=merge|fossil merge]: |
| 24 | <ol type="a"> |
| 25 | <li> Added the [/help?cmd=merge-info|fossil merge-info] command and especially |
| 26 | the --tk option to that command, to provide analysis of the most recent |
| 27 | merge or update operation. |
| 28 | <li> When a merge conflict occurs, a new section is added to the conflict |
| 29 | text that shows Fossil's suggested resolution to the conflict. |
| 30 | </ol> |
| 31 | * Enhancements to [/help?cmd=commit|fossil commit]: |
| 32 | <ol type="a"> |
| 33 | <li> If Fossil sees potential formatting mistakes (bad hyperlinks) |
| 34 | in the check-in comment, it will alert the developer and give |
| 35 | him or her the opportunity to edit the comment before continuing. |
| 36 | <li> The new "--if-changes" option causes the commit to become |
| 37 | a quiet no-op if there are no pending changes. |
| 38 | <li> Added the ability to sign check-ins with SSH keys. |
| 39 | <li> Issue a warning if a user tries to commit on a check-in where the |
| 40 | branch has been changed. |
| 41 | </ol> |
| 42 | * Deprecate the --comfmtflags and --comment-format global options and |
| 43 | no longer list them in the built-in help, but keep them working for |
| 44 | backwards compatibility. |
| 45 | Alternative TTY comment formatting can still be specified using the |
| 46 | [/help?cmd=comment-format|comment-format setting], if desired. The |
| @@ -49,30 +67,43 @@ | |
| 67 | <li> Enhance the "ymd" query parameter so that when used like |
| 68 | "ymd=YYYYMMDD-YYYYMMDD" it shows all events in the range of |
| 69 | dates specified. |
| 70 | <li> Accept the "Z" (Zulu-time) suffix on date arguments for the |
| 71 | "ymd" and "yw" query parameters. |
| 72 | <li> The new "min" query parameter, when added to a from=,to= query, |
| 73 | collapses long runs of check-ins on the same branch into just |
| 74 | end-points. |
| 75 | <li> The p= and d= parameters an reference different check-ins, which |
| 76 | case the timeline shows those check-ins that are both ancestors |
| 77 | of p= and descendants of d=. |
| 78 | </ol> |
| 79 | * Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis |
| 80 | and debugging |
| 81 | * Fix a bug in [/help?cmd=patch|fossil patch create] that causes |
| 82 | [/help?cmd=revert|fossil revert] operations that happened on individual |
| 83 | files after a [/help?cmd=merge|fossil merge] to be omitted from the |
| 84 | patch. |
| 85 | * Added the [/help?cmd=patch|patch alias] command for managing aliases |
| 86 | for remote checkout names. |
| 87 | * Enhancements to on-line help and the [/help?cmd=help|fossil help] command: |
| 88 | <ol type="a"> |
| 89 | <li> Add the ability to search the help text, either in the UI |
| 90 | (on the [/help?cmd=/search|/search page]) or from the command-line |
| 91 | (using the "[/help?cmd=search|fossil search -h PATTERN]" command.) |
| 92 | <li> Accepts an optional SUBCOMMAND argument following the |
| 93 | COMMAND argument and only shows results for the specified |
| 94 | subcommand, not the entire command. |
| 95 | <li> The -u (--usage) option shows only the command-line syntax |
| 96 | <li> The -o (--options) option shows only the command-line options |
| 97 | </ol> |
| 98 | * Added the ability to attach wiki pages to a ticket for extended |
| 99 | descriptions. |
| 100 | * Add a "user elevation" [/doc/trunk/www/alerts.md|subscription] |
| 101 | which alerts subscribers when an admin creates a new user or |
| 102 | adds new permissions to one. |
| 103 | * Diverse minor fixes and additions. |
| 104 | |
| 105 | |
| 106 | <h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2> |
| 107 | |
| 108 | * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories |
| 109 | that have non-ASCII filenames |
| 110 |
+4
-1
| --- www/fileformat.wiki | ||
| +++ www/fileformat.wiki | ||
| @@ -173,11 +173,14 @@ | ||
| 173 | 173 | same file as it existed in the parent check-in. If the name of the |
| 174 | 174 | file is unchanged from its parent, then the 4th argument is omitted. |
| 175 | 175 | |
| 176 | 176 | A manifest has zero or one <b>N</b> cards. The <b>N</b> card specifies the mimetype for the |
| 177 | 177 | text in the comment of the <b>C</b> card. If the <b>N</b> card is omitted, a default mimetype |
| 178 | -is used. | |
| 178 | +is used. Note that the <b>N</b> card has never actually been used by | |
| 179 | +any Fossil implementation. The implementation has always interpreted | |
| 180 | +check-in comments according to the [/wiki_rules|Fossil Wiki formatting rules]. | |
| 181 | +There are no current plans to ever change that. | |
| 179 | 182 | |
| 180 | 183 | A manifest has zero or one <b>P</b> cards. Most manifests have one <b>P</b> card. |
| 181 | 184 | The <b>P</b> card has a varying number of arguments that |
| 182 | 185 | define other manifests from which the current manifest |
| 183 | 186 | is derived. Each argument is a lowercase |
| 184 | 187 |
| --- www/fileformat.wiki | |
| +++ www/fileformat.wiki | |
| @@ -173,11 +173,14 @@ | |
| 173 | same file as it existed in the parent check-in. If the name of the |
| 174 | file is unchanged from its parent, then the 4th argument is omitted. |
| 175 | |
| 176 | A manifest has zero or one <b>N</b> cards. The <b>N</b> card specifies the mimetype for the |
| 177 | text in the comment of the <b>C</b> card. If the <b>N</b> card is omitted, a default mimetype |
| 178 | is used. |
| 179 | |
| 180 | A manifest has zero or one <b>P</b> cards. Most manifests have one <b>P</b> card. |
| 181 | The <b>P</b> card has a varying number of arguments that |
| 182 | define other manifests from which the current manifest |
| 183 | is derived. Each argument is a lowercase |
| 184 |
| --- www/fileformat.wiki | |
| +++ www/fileformat.wiki | |
| @@ -173,11 +173,14 @@ | |
| 173 | same file as it existed in the parent check-in. If the name of the |
| 174 | file is unchanged from its parent, then the 4th argument is omitted. |
| 175 | |
| 176 | A manifest has zero or one <b>N</b> cards. The <b>N</b> card specifies the mimetype for the |
| 177 | text in the comment of the <b>C</b> card. If the <b>N</b> card is omitted, a default mimetype |
| 178 | is used. Note that the <b>N</b> card has never actually been used by |
| 179 | any Fossil implementation. The implementation has always interpreted |
| 180 | check-in comments according to the [/wiki_rules|Fossil Wiki formatting rules]. |
| 181 | There are no current plans to ever change that. |
| 182 | |
| 183 | A manifest has zero or one <b>P</b> cards. Most manifests have one <b>P</b> card. |
| 184 | The <b>P</b> card has a varying number of arguments that |
| 185 | define other manifests from which the current manifest |
| 186 | is derived. Each argument is a lowercase |
| 187 |
+3
-3
| --- www/settings.wiki | ||
| +++ www/settings.wiki | ||
| @@ -1,18 +1,18 @@ | ||
| 1 | 1 | <title>Fossil Settings</title> |
| 2 | 2 | |
| 3 | -<h2>Using Fossil Settings</h2> | |
| 3 | +<h1>Using Fossil Settings</h1> | |
| 4 | 4 | |
| 5 | 5 | Settings control the behaviour of fossil. They are set with the |
| 6 | 6 | <tt>fossil settings</tt> command, or through the web interface in |
| 7 | 7 | the Settings page in the Admin section. |
| 8 | 8 | |
| 9 | 9 | For a list of all settings, view the Settings page, or type |
| 10 | 10 | <tt>fossil help settings</tt> from the command line. |
| 11 | 11 | |
| 12 | 12 | |
| 13 | -<h3 id="repo">Repository settings</h3> | |
| 13 | +<h2 id="repo">1.0 Repository settings</h2> | |
| 14 | 14 | |
| 15 | 15 | Settings are set on a per-repository basis. When you clone a repository, |
| 16 | 16 | a subset of settings are copied to your local repository. |
| 17 | 17 | |
| 18 | 18 | If you make a change to a setting on your local repository, it is not |
| @@ -24,11 +24,11 @@ | ||
| 24 | 24 | will be used for all repositories cloned to your machine, unless |
| 25 | 25 | overridden explicitly in a particular repository. Global settings can be |
| 26 | 26 | set by using the <tt>-global</tt> option on the <tt>fossil settings</tt> |
| 27 | 27 | command. |
| 28 | 28 | |
| 29 | -<h3 id="versionable">"Versionable" settings</h3> | |
| 29 | +<h2 id="versionable">2.0 "Versionable" settings</h2> | |
| 30 | 30 | |
| 31 | 31 | Most of the settings control the behaviour of fossil on your local |
| 32 | 32 | machine, largely acting to reflect your preference on how you want to |
| 33 | 33 | use Fossil, how you communicate with the server, or options for hosting |
| 34 | 34 | a repository on the web. |
| 35 | 35 |
| --- www/settings.wiki | |
| +++ www/settings.wiki | |
| @@ -1,18 +1,18 @@ | |
| 1 | <title>Fossil Settings</title> |
| 2 | |
| 3 | <h2>Using Fossil Settings</h2> |
| 4 | |
| 5 | Settings control the behaviour of fossil. They are set with the |
| 6 | <tt>fossil settings</tt> command, or through the web interface in |
| 7 | the Settings page in the Admin section. |
| 8 | |
| 9 | For a list of all settings, view the Settings page, or type |
| 10 | <tt>fossil help settings</tt> from the command line. |
| 11 | |
| 12 | |
| 13 | <h3 id="repo">Repository settings</h3> |
| 14 | |
| 15 | Settings are set on a per-repository basis. When you clone a repository, |
| 16 | a subset of settings are copied to your local repository. |
| 17 | |
| 18 | If you make a change to a setting on your local repository, it is not |
| @@ -24,11 +24,11 @@ | |
| 24 | will be used for all repositories cloned to your machine, unless |
| 25 | overridden explicitly in a particular repository. Global settings can be |
| 26 | set by using the <tt>-global</tt> option on the <tt>fossil settings</tt> |
| 27 | command. |
| 28 | |
| 29 | <h3 id="versionable">"Versionable" settings</h3> |
| 30 | |
| 31 | Most of the settings control the behaviour of fossil on your local |
| 32 | machine, largely acting to reflect your preference on how you want to |
| 33 | use Fossil, how you communicate with the server, or options for hosting |
| 34 | a repository on the web. |
| 35 |
| --- www/settings.wiki | |
| +++ www/settings.wiki | |
| @@ -1,18 +1,18 @@ | |
| 1 | <title>Fossil Settings</title> |
| 2 | |
| 3 | <h1>Using Fossil Settings</h1> |
| 4 | |
| 5 | Settings control the behaviour of fossil. They are set with the |
| 6 | <tt>fossil settings</tt> command, or through the web interface in |
| 7 | the Settings page in the Admin section. |
| 8 | |
| 9 | For a list of all settings, view the Settings page, or type |
| 10 | <tt>fossil help settings</tt> from the command line. |
| 11 | |
| 12 | |
| 13 | <h2 id="repo">1.0 Repository settings</h2> |
| 14 | |
| 15 | Settings are set on a per-repository basis. When you clone a repository, |
| 16 | a subset of settings are copied to your local repository. |
| 17 | |
| 18 | If you make a change to a setting on your local repository, it is not |
| @@ -24,11 +24,11 @@ | |
| 24 | will be used for all repositories cloned to your machine, unless |
| 25 | overridden explicitly in a particular repository. Global settings can be |
| 26 | set by using the <tt>-global</tt> option on the <tt>fossil settings</tt> |
| 27 | command. |
| 28 | |
| 29 | <h2 id="versionable">2.0 "Versionable" settings</h2> |
| 30 | |
| 31 | Most of the settings control the behaviour of fossil on your local |
| 32 | machine, largely acting to reflect your preference on how you want to |
| 33 | use Fossil, how you communicate with the server, or options for hosting |
| 34 | a repository on the web. |
| 35 |