Fossil SCM

merge trunk make Notepad the default comment editor on Windows

jan.nijtmans 2012-10-07 17:18 use-utf8-in-win-external-editor merge
Commit 10cf72bd3bba470c4ef81dd9620ce3dd6a27d075
--- Makefile.in
+++ Makefile.in
@@ -41,10 +41,11 @@
4141
LIB = @LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@
4242
TCC += @EXTRA_CFLAGS@ @CPPFLAGS@ @CFLAGS@ -DHAVE_AUTOCONFIG_H
4343
INSTALLDIR = $(DESTDIR)@prefix@/bin
4444
USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@
4545
FOSSIL_ENABLE_TCL = @FOSSIL_ENABLE_TCL@
46
+FOSSIL_ENABLE_TCL_STUBS = @FOSSIL_ENABLE_TCL_STUBS@
4647
4748
include $(SRCDIR)/main.mk
4849
4950
distclean: clean
5051
rm -f autoconfig.h config.log Makefile
5152
--- Makefile.in
+++ Makefile.in
@@ -41,10 +41,11 @@
41 LIB = @LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@
42 TCC += @EXTRA_CFLAGS@ @CPPFLAGS@ @CFLAGS@ -DHAVE_AUTOCONFIG_H
43 INSTALLDIR = $(DESTDIR)@prefix@/bin
44 USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@
45 FOSSIL_ENABLE_TCL = @FOSSIL_ENABLE_TCL@
 
46
47 include $(SRCDIR)/main.mk
48
49 distclean: clean
50 rm -f autoconfig.h config.log Makefile
51
--- Makefile.in
+++ Makefile.in
@@ -41,10 +41,11 @@
41 LIB = @LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@
42 TCC += @EXTRA_CFLAGS@ @CPPFLAGS@ @CFLAGS@ -DHAVE_AUTOCONFIG_H
43 INSTALLDIR = $(DESTDIR)@prefix@/bin
44 USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@
45 FOSSIL_ENABLE_TCL = @FOSSIL_ENABLE_TCL@
46 FOSSIL_ENABLE_TCL_STUBS = @FOSSIL_ENABLE_TCL_STUBS@
47
48 include $(SRCDIR)/main.mk
49
50 distclean: clean
51 rm -f autoconfig.h config.log Makefile
52
+37 -23
--- auto.def
+++ auto.def
@@ -5,10 +5,11 @@
55
options {
66
with-openssl:path|auto|none
77
=> {Look for openssl in the given path, or auto or none}
88
with-zlib:path => {Look for zlib in the given path}
99
with-tcl:path => {Enable Tcl integration, with Tcl in the specified path}
10
+ with-tcl-stubs=0 => {Enable Tcl integration via stubs mechanism}
1011
internal-sqlite=1 => {Don't use the internal sqlite, use the system one}
1112
static=0 => {Link a static executable}
1213
lineedit=1 => {Disable line editing}
1314
fossil-debug=0 => {Build with fossil debugging enabled}
1415
json=0 => {Build with fossil JSON API enabled}
@@ -32,11 +33,11 @@
3233
if {![opt-bool internal-sqlite]} {
3334
proc find_internal_sqlite {} {
3435
3536
# On some systems (slackware), libsqlite3 requires -ldl to link. So
3637
# search for the system SQLite once with -ldl, and once without. If
37
- # the library can only be found with $extralibs set to -ldl, then
38
+ # the library can only be found with $extralibs set to -ldl, then
3839
# the code below will append -ldl to LIBS.
3940
#
4041
foreach extralibs {{} {-ldl}} {
4142
4243
# Locate the system SQLite by searching for sqlite3_open(). Then check
@@ -92,35 +93,48 @@
9293
user-error "zlib not found please install it or specify the location with --with-zlib"
9394
}
9495
9596
set tclpath [opt-val with-tcl]
9697
if {$tclpath ne ""} {
97
- # Note parse-tclconfig-sh is in autosetup/local.tcl
98
+ # Note parse-tclconfig-sh is in autosetup/local.tcl
9899
if {$tclpath eq "1"} {
99100
# Use the system Tcl. Look in some likely places.
100
- array set tclconfig [parse-tclconfig-sh /usr /usr/local /usr/share /opt/local]
101
- set msg "on your system"
102
- } else {
101
+ array set tclconfig [parse-tclconfig-sh \
102
+ /usr /usr/local /usr/share /opt/local]
103
+ set msg "on your system"
104
+ } else {
103105
array set tclconfig [parse-tclconfig-sh $tclpath]
104
- set msg "at $tclpath"
105
- }
106
- if {![info exists tclconfig(TCL_INCLUDE_SPEC)]} {
107
- user-error "Cannot find Tcl $msg"
108
- }
109
- set cflags $tclconfig(TCL_INCLUDE_SPEC)
110
- set libs "$tclconfig(TCL_LIB_SPEC) $tclconfig(TCL_LIBS)"
111
- cc-with [list -cflags $cflags -libs $libs] {
112
- if {![cc-check-functions Tcl_CreateInterp]} {
113
- user-error "Cannot find a usable Tcl $msg"
114
- }
115
- }
116
- set version $tclconfig(TCL_VERSION)$tclconfig(TCL_PATCH_LEVEL)
117
- msg-result "Found Tcl $version at $tclconfig(TCL_PREFIX)"
118
- define-append LIBS $libs
119
- define-append EXTRA_CFLAGS $cflags
120
- define-append EXTRA_LDFLAGS $tclconfig(TCL_LD_FLAGS)
121
-
106
+ set msg "at $tclpath"
107
+ }
108
+ if {![info exists tclconfig(TCL_INCLUDE_SPEC)]} {
109
+ user-error "Cannot find Tcl $msg"
110
+ }
111
+ set tclstubs [opt-bool with-tcl-stubs]
112
+ if {$tclstubs && $tclconfig(TCL_SUPPORTS_STUBS)} {
113
+ set libs "$tclconfig(TCL_STUB_LIB_SPEC)"
114
+ define FOSSIL_ENABLE_TCL_STUBS
115
+ define USE_TCL_STUBS
116
+ } else {
117
+ set libs "$tclconfig(TCL_LIB_SPEC) $tclconfig(TCL_LIBS)"
118
+ }
119
+ set cflags $tclconfig(TCL_INCLUDE_SPEC)
120
+ cc-with [list -cflags $cflags -libs $libs] {
121
+ if {$tclstubs} {
122
+ if {![cc-check-functions Tcl_InitStubs]} {
123
+ user-error "Cannot find a usable Tcl stubs library $msg"
124
+ }
125
+ } else {
126
+ if {![cc-check-functions Tcl_CreateInterp]} {
127
+ user-error "Cannot find a usable Tcl library $msg"
128
+ }
129
+ }
130
+ }
131
+ set version $tclconfig(TCL_VERSION)$tclconfig(TCL_PATCH_LEVEL)
132
+ msg-result "Found Tcl $version at $tclconfig(TCL_PREFIX)"
133
+ define-append LIBS $libs
134
+ define-append EXTRA_CFLAGS $cflags
135
+ define-append EXTRA_LDFLAGS $tclconfig(TCL_LD_FLAGS)
122136
define FOSSIL_ENABLE_TCL
123137
}
124138
125139
# Helper for openssl checking
126140
proc check-for-openssl {msg {cflags {}}} {
127141
--- auto.def
+++ auto.def
@@ -5,10 +5,11 @@
5 options {
6 with-openssl:path|auto|none
7 => {Look for openssl in the given path, or auto or none}
8 with-zlib:path => {Look for zlib in the given path}
9 with-tcl:path => {Enable Tcl integration, with Tcl in the specified path}
 
10 internal-sqlite=1 => {Don't use the internal sqlite, use the system one}
11 static=0 => {Link a static executable}
12 lineedit=1 => {Disable line editing}
13 fossil-debug=0 => {Build with fossil debugging enabled}
14 json=0 => {Build with fossil JSON API enabled}
@@ -32,11 +33,11 @@
32 if {![opt-bool internal-sqlite]} {
33 proc find_internal_sqlite {} {
34
35 # On some systems (slackware), libsqlite3 requires -ldl to link. So
36 # search for the system SQLite once with -ldl, and once without. If
37 # the library can only be found with $extralibs set to -ldl, then
38 # the code below will append -ldl to LIBS.
39 #
40 foreach extralibs {{} {-ldl}} {
41
42 # Locate the system SQLite by searching for sqlite3_open(). Then check
@@ -92,35 +93,48 @@
92 user-error "zlib not found please install it or specify the location with --with-zlib"
93 }
94
95 set tclpath [opt-val with-tcl]
96 if {$tclpath ne ""} {
97 # Note parse-tclconfig-sh is in autosetup/local.tcl
98 if {$tclpath eq "1"} {
99 # Use the system Tcl. Look in some likely places.
100 array set tclconfig [parse-tclconfig-sh /usr /usr/local /usr/share /opt/local]
101 set msg "on your system"
102 } else {
 
103 array set tclconfig [parse-tclconfig-sh $tclpath]
104 set msg "at $tclpath"
105 }
106 if {![info exists tclconfig(TCL_INCLUDE_SPEC)]} {
107 user-error "Cannot find Tcl $msg"
108 }
109 set cflags $tclconfig(TCL_INCLUDE_SPEC)
110 set libs "$tclconfig(TCL_LIB_SPEC) $tclconfig(TCL_LIBS)"
111 cc-with [list -cflags $cflags -libs $libs] {
112 if {![cc-check-functions Tcl_CreateInterp]} {
113 user-error "Cannot find a usable Tcl $msg"
114 }
115 }
116 set version $tclconfig(TCL_VERSION)$tclconfig(TCL_PATCH_LEVEL)
117 msg-result "Found Tcl $version at $tclconfig(TCL_PREFIX)"
118 define-append LIBS $libs
119 define-append EXTRA_CFLAGS $cflags
120 define-append EXTRA_LDFLAGS $tclconfig(TCL_LD_FLAGS)
121
 
 
 
 
 
 
 
 
 
 
 
 
122 define FOSSIL_ENABLE_TCL
123 }
124
125 # Helper for openssl checking
126 proc check-for-openssl {msg {cflags {}}} {
127
--- auto.def
+++ auto.def
@@ -5,10 +5,11 @@
5 options {
6 with-openssl:path|auto|none
7 => {Look for openssl in the given path, or auto or none}
8 with-zlib:path => {Look for zlib in the given path}
9 with-tcl:path => {Enable Tcl integration, with Tcl in the specified path}
10 with-tcl-stubs=0 => {Enable Tcl integration via stubs mechanism}
11 internal-sqlite=1 => {Don't use the internal sqlite, use the system one}
12 static=0 => {Link a static executable}
13 lineedit=1 => {Disable line editing}
14 fossil-debug=0 => {Build with fossil debugging enabled}
15 json=0 => {Build with fossil JSON API enabled}
@@ -32,11 +33,11 @@
33 if {![opt-bool internal-sqlite]} {
34 proc find_internal_sqlite {} {
35
36 # On some systems (slackware), libsqlite3 requires -ldl to link. So
37 # search for the system SQLite once with -ldl, and once without. If
38 # the library can only be found with $extralibs set to -ldl, then
39 # the code below will append -ldl to LIBS.
40 #
41 foreach extralibs {{} {-ldl}} {
42
43 # Locate the system SQLite by searching for sqlite3_open(). Then check
@@ -92,35 +93,48 @@
93 user-error "zlib not found please install it or specify the location with --with-zlib"
94 }
95
96 set tclpath [opt-val with-tcl]
97 if {$tclpath ne ""} {
98 # Note parse-tclconfig-sh is in autosetup/local.tcl
99 if {$tclpath eq "1"} {
100 # Use the system Tcl. Look in some likely places.
101 array set tclconfig [parse-tclconfig-sh \
102 /usr /usr/local /usr/share /opt/local]
103 set msg "on your system"
104 } else {
105 array set tclconfig [parse-tclconfig-sh $tclpath]
106 set msg "at $tclpath"
107 }
108 if {![info exists tclconfig(TCL_INCLUDE_SPEC)]} {
109 user-error "Cannot find Tcl $msg"
110 }
111 set tclstubs [opt-bool with-tcl-stubs]
112 if {$tclstubs && $tclconfig(TCL_SUPPORTS_STUBS)} {
113 set libs "$tclconfig(TCL_STUB_LIB_SPEC)"
114 define FOSSIL_ENABLE_TCL_STUBS
115 define USE_TCL_STUBS
116 } else {
117 set libs "$tclconfig(TCL_LIB_SPEC) $tclconfig(TCL_LIBS)"
118 }
119 set cflags $tclconfig(TCL_INCLUDE_SPEC)
120 cc-with [list -cflags $cflags -libs $libs] {
121 if {$tclstubs} {
122 if {![cc-check-functions Tcl_InitStubs]} {
123 user-error "Cannot find a usable Tcl stubs library $msg"
124 }
125 } else {
126 if {![cc-check-functions Tcl_CreateInterp]} {
127 user-error "Cannot find a usable Tcl library $msg"
128 }
129 }
130 }
131 set version $tclconfig(TCL_VERSION)$tclconfig(TCL_PATCH_LEVEL)
132 msg-result "Found Tcl $version at $tclconfig(TCL_PREFIX)"
133 define-append LIBS $libs
134 define-append EXTRA_CFLAGS $cflags
135 define-append EXTRA_LDFLAGS $tclconfig(TCL_LD_FLAGS)
136 define FOSSIL_ENABLE_TCL
137 }
138
139 # Helper for openssl checking
140 proc check-for-openssl {msg {cflags {}}} {
141
+61 -28
--- src/bisect.c
+++ src/bisect.c
@@ -85,10 +85,66 @@
8585
}
8686
}
8787
assert( r>=0 );
8888
return r;
8989
}
90
+
91
+/*
92
+** List a bisect path.
93
+*/
94
+static void bisect_list(int abbreviated){
95
+ PathNode *p;
96
+ int vid = db_lget_int("checkout", 0);
97
+ int n;
98
+ Stmt s;
99
+ int nStep;
100
+ int nHidden = 0;
101
+ bisect_path();
102
+ db_prepare(&s, "SELECT blob.uuid, datetime(event.mtime) "
103
+ " FROM blob, event"
104
+ " WHERE blob.rid=:rid AND event.objid=:rid"
105
+ " AND event.type='ci'");
106
+ nStep = path_length();
107
+ if( abbreviated ){
108
+ for(p=path_last(); p; p=p->pFrom) p->isHidden = 1;
109
+ for(p=path_last(), n=0; p; p=p->pFrom, n++){
110
+ if( p->rid==bisect.good
111
+ || p->rid==bisect.bad
112
+ || p->rid==vid
113
+ || (nStep>1 && n==nStep/2)
114
+ ){
115
+ p->isHidden = 0;
116
+ if( p->pFrom ) p->pFrom->isHidden = 0;
117
+ }
118
+ }
119
+ for(p=path_last(); p; p=p->pFrom){
120
+ if( p->pFrom && p->pFrom->isHidden==0 ) p->isHidden = 0;
121
+ }
122
+ }
123
+ for(p=path_last(), n=0; p; p=p->pFrom, n++){
124
+ if( p->isHidden && (nHidden || (p->pFrom && p->pFrom->isHidden)) ){
125
+ nHidden++;
126
+ continue;
127
+ }else if( nHidden ){
128
+ fossil_print(" ... eliding %d check-ins\n", nHidden);
129
+ nHidden = 0;
130
+ }
131
+ db_bind_int(&s, ":rid", p->rid);
132
+ if( db_step(&s)==SQLITE_ROW ){
133
+ const char *zUuid = db_column_text(&s, 0);
134
+ const char *zDate = db_column_text(&s, 1);
135
+ fossil_print("%s %S", zDate, zUuid);
136
+ if( p->rid==bisect.good ) fossil_print(" GOOD");
137
+ if( p->rid==bisect.bad ) fossil_print(" BAD");
138
+ if( p->rid==vid ) fossil_print(" CURRENT");
139
+ if( nStep>1 && n==nStep/2 ) fossil_print(" NEXT");
140
+ fossil_print("\n");
141
+ }
142
+ db_reset(&s);
143
+ }
144
+ db_finalize(&s);
145
+}
90146
91147
/*
92148
** COMMAND: bisect
93149
**
94150
** Usage: %fossil bisect SUBCOMMAND ...
@@ -118,11 +174,11 @@
118174
** fossil bisect reset
119175
**
120176
** Reinitialize a bisect session. This cancels prior bisect history
121177
** and allows a bisect session to start over from the beginning.
122178
**
123
-** fossil bisect vlist
179
+** fossil bisect vlist|ls ?--all?
124180
**
125181
** List the versions in between "bad" and "good".
126182
*/
127183
void bisect_cmd(void){
128184
int n;
@@ -179,10 +235,11 @@
179235
g.argv[1] = "update";
180236
g.argv[2] = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pMid->rid);
181237
g.argc = 3;
182238
g.fNoSync = 1;
183239
update_cmd();
240
+ bisect_list(1);
184241
}else if( memcmp(zCmd, "options", n)==0 ){
185242
if( g.argc==3 ){
186243
unsigned int i;
187244
for(i=0; i<sizeof(aBisectOption)/sizeof(aBisectOption[0]); i++){
188245
char *z = mprintf("bisect-%s", aBisectOption[i].zName);
@@ -213,36 +270,12 @@
213270
}
214271
}else if( memcmp(zCmd, "reset", n)==0 ){
215272
db_multi_exec(
216273
"DELETE FROM vvar WHERE name IN ('bisect-good', 'bisect-bad');"
217274
);
218
- }else if( memcmp(zCmd, "vlist", n)==0 ){
219
- PathNode *p;
220
- int vid = db_lget_int("checkout", 0);
221
- int n;
222
- Stmt s;
223
- int nStep;
224
- bisect_path();
225
- db_prepare(&s, "SELECT substr(blob.uuid,1,20) || ' ' || "
226
- " datetime(event.mtime) FROM blob, event"
227
- " WHERE blob.rid=:rid AND event.objid=:rid"
228
- " AND event.type='ci'");
229
- nStep = path_length();
230
- for(p=path_last(), n=0; p; p=p->pFrom, n++){
231
- const char *z;
232
- db_bind_int(&s, ":rid", p->rid);
233
- if( db_step(&s)==SQLITE_ROW ){
234
- z = db_column_text(&s, 0);
235
- fossil_print("%s", z);
236
- if( p->rid==bisect.good ) fossil_print(" GOOD");
237
- if( p->rid==bisect.bad ) fossil_print(" BAD");
238
- if( p->rid==vid ) fossil_print(" CURRENT");
239
- if( nStep>1 && n==nStep/2 ) fossil_print(" NEXT");
240
- fossil_print("\n");
241
- }
242
- db_reset(&s);
243
- }
244
- db_finalize(&s);
275
+ }else if( memcmp(zCmd, "vlist", n)==0 || memcmp(zCmd, "ls", n)==0 ){
276
+ int fAll = find_option("all", 0, 0)!=0;
277
+ bisect_list(!fAll);
245278
}else{
246279
usage("bad|good|next|reset|vlist ...");
247280
}
248281
}
249282
--- src/bisect.c
+++ src/bisect.c
@@ -85,10 +85,66 @@
85 }
86 }
87 assert( r>=0 );
88 return r;
89 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
91 /*
92 ** COMMAND: bisect
93 **
94 ** Usage: %fossil bisect SUBCOMMAND ...
@@ -118,11 +174,11 @@
118 ** fossil bisect reset
119 **
120 ** Reinitialize a bisect session. This cancels prior bisect history
121 ** and allows a bisect session to start over from the beginning.
122 **
123 ** fossil bisect vlist
124 **
125 ** List the versions in between "bad" and "good".
126 */
127 void bisect_cmd(void){
128 int n;
@@ -179,10 +235,11 @@
179 g.argv[1] = "update";
180 g.argv[2] = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pMid->rid);
181 g.argc = 3;
182 g.fNoSync = 1;
183 update_cmd();
 
184 }else if( memcmp(zCmd, "options", n)==0 ){
185 if( g.argc==3 ){
186 unsigned int i;
187 for(i=0; i<sizeof(aBisectOption)/sizeof(aBisectOption[0]); i++){
188 char *z = mprintf("bisect-%s", aBisectOption[i].zName);
@@ -213,36 +270,12 @@
213 }
214 }else if( memcmp(zCmd, "reset", n)==0 ){
215 db_multi_exec(
216 "DELETE FROM vvar WHERE name IN ('bisect-good', 'bisect-bad');"
217 );
218 }else if( memcmp(zCmd, "vlist", n)==0 ){
219 PathNode *p;
220 int vid = db_lget_int("checkout", 0);
221 int n;
222 Stmt s;
223 int nStep;
224 bisect_path();
225 db_prepare(&s, "SELECT substr(blob.uuid,1,20) || ' ' || "
226 " datetime(event.mtime) FROM blob, event"
227 " WHERE blob.rid=:rid AND event.objid=:rid"
228 " AND event.type='ci'");
229 nStep = path_length();
230 for(p=path_last(), n=0; p; p=p->pFrom, n++){
231 const char *z;
232 db_bind_int(&s, ":rid", p->rid);
233 if( db_step(&s)==SQLITE_ROW ){
234 z = db_column_text(&s, 0);
235 fossil_print("%s", z);
236 if( p->rid==bisect.good ) fossil_print(" GOOD");
237 if( p->rid==bisect.bad ) fossil_print(" BAD");
238 if( p->rid==vid ) fossil_print(" CURRENT");
239 if( nStep>1 && n==nStep/2 ) fossil_print(" NEXT");
240 fossil_print("\n");
241 }
242 db_reset(&s);
243 }
244 db_finalize(&s);
245 }else{
246 usage("bad|good|next|reset|vlist ...");
247 }
248 }
249
--- src/bisect.c
+++ src/bisect.c
@@ -85,10 +85,66 @@
85 }
86 }
87 assert( r>=0 );
88 return r;
89 }
90
91 /*
92 ** List a bisect path.
93 */
94 static void bisect_list(int abbreviated){
95 PathNode *p;
96 int vid = db_lget_int("checkout", 0);
97 int n;
98 Stmt s;
99 int nStep;
100 int nHidden = 0;
101 bisect_path();
102 db_prepare(&s, "SELECT blob.uuid, datetime(event.mtime) "
103 " FROM blob, event"
104 " WHERE blob.rid=:rid AND event.objid=:rid"
105 " AND event.type='ci'");
106 nStep = path_length();
107 if( abbreviated ){
108 for(p=path_last(); p; p=p->pFrom) p->isHidden = 1;
109 for(p=path_last(), n=0; p; p=p->pFrom, n++){
110 if( p->rid==bisect.good
111 || p->rid==bisect.bad
112 || p->rid==vid
113 || (nStep>1 && n==nStep/2)
114 ){
115 p->isHidden = 0;
116 if( p->pFrom ) p->pFrom->isHidden = 0;
117 }
118 }
119 for(p=path_last(); p; p=p->pFrom){
120 if( p->pFrom && p->pFrom->isHidden==0 ) p->isHidden = 0;
121 }
122 }
123 for(p=path_last(), n=0; p; p=p->pFrom, n++){
124 if( p->isHidden && (nHidden || (p->pFrom && p->pFrom->isHidden)) ){
125 nHidden++;
126 continue;
127 }else if( nHidden ){
128 fossil_print(" ... eliding %d check-ins\n", nHidden);
129 nHidden = 0;
130 }
131 db_bind_int(&s, ":rid", p->rid);
132 if( db_step(&s)==SQLITE_ROW ){
133 const char *zUuid = db_column_text(&s, 0);
134 const char *zDate = db_column_text(&s, 1);
135 fossil_print("%s %S", zDate, zUuid);
136 if( p->rid==bisect.good ) fossil_print(" GOOD");
137 if( p->rid==bisect.bad ) fossil_print(" BAD");
138 if( p->rid==vid ) fossil_print(" CURRENT");
139 if( nStep>1 && n==nStep/2 ) fossil_print(" NEXT");
140 fossil_print("\n");
141 }
142 db_reset(&s);
143 }
144 db_finalize(&s);
145 }
146
147 /*
148 ** COMMAND: bisect
149 **
150 ** Usage: %fossil bisect SUBCOMMAND ...
@@ -118,11 +174,11 @@
174 ** fossil bisect reset
175 **
176 ** Reinitialize a bisect session. This cancels prior bisect history
177 ** and allows a bisect session to start over from the beginning.
178 **
179 ** fossil bisect vlist|ls ?--all?
180 **
181 ** List the versions in between "bad" and "good".
182 */
183 void bisect_cmd(void){
184 int n;
@@ -179,10 +235,11 @@
235 g.argv[1] = "update";
236 g.argv[2] = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pMid->rid);
237 g.argc = 3;
238 g.fNoSync = 1;
239 update_cmd();
240 bisect_list(1);
241 }else if( memcmp(zCmd, "options", n)==0 ){
242 if( g.argc==3 ){
243 unsigned int i;
244 for(i=0; i<sizeof(aBisectOption)/sizeof(aBisectOption[0]); i++){
245 char *z = mprintf("bisect-%s", aBisectOption[i].zName);
@@ -213,36 +270,12 @@
270 }
271 }else if( memcmp(zCmd, "reset", n)==0 ){
272 db_multi_exec(
273 "DELETE FROM vvar WHERE name IN ('bisect-good', 'bisect-bad');"
274 );
275 }else if( memcmp(zCmd, "vlist", n)==0 || memcmp(zCmd, "ls", n)==0 ){
276 int fAll = find_option("all", 0, 0)!=0;
277 bisect_list(!fAll);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278 }else{
279 usage("bad|good|next|reset|vlist ...");
280 }
281 }
282
+99 -81
--- src/checkin.c
+++ src/checkin.c
@@ -417,10 +417,96 @@
417417
}
418418
}
419419
}
420420
db_finalize(&q);
421421
}
422
+
423
+/*
424
+** Prompt the user for a check-in or stash comment (given in pPrompt),
425
+** gather the response, then return the response in pComment.
426
+**
427
+** Lines of the prompt that begin with # are discarded. Excess whitespace
428
+** is removed from the reply.
429
+**
430
+** Appropriate encoding translations are made on windows.
431
+*/
432
+void prompt_for_user_comment(Blob *pComment, Blob *pPrompt){
433
+ const char *zEditor;
434
+ char *zCmd;
435
+ char *zFile;
436
+ Blob reply, line;
437
+ char *zComment;
438
+ int i;
439
+
440
+ zEditor = db_get("editor", 0);
441
+ if( zEditor==0 ){
442
+ zEditor = fossil_getenv("VISUAL");
443
+ }
444
+ if( zEditor==0 ){
445
+ zEditor = fossil_getenv("EDITOR");
446
+ }
447
+#ifdef _WIN32
448
+ if( zEditor==0 ){
449
+ zEditor = mprintf("%s\\notepad.exe", fossil_getenv("SystemRoot"));
450
+ }
451
+#endif
452
+ if( zEditor==0 ){
453
+ blob_append(pPrompt,
454
+ "#\n"
455
+ "# Since no default text editor is set using EDITOR or VISUAL\n"
456
+ "# environment variables or the \"fossil set editor\" command,\n"
457
+ "# and because no comment was specified using the \"-m\" or \"-M\"\n"
458
+ "# command-line options, you will need to enter the comment below.\n"
459
+ "# Type \".\" on a line by itself when you are done:\n", -1);
460
+ zFile = mprintf("-");
461
+ }else{
462
+ zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'",
463
+ g.zLocalRoot);
464
+ }
465
+#if defined(_WIN32)
466
+ blob_add_cr(pPrompt);
467
+#endif
468
+ blob_write_to_file(pPrompt, zFile);
469
+ if( zEditor ){
470
+ zCmd = mprintf("%s \"%s\"", zEditor, zFile);
471
+ fossil_print("%s\n", zCmd);
472
+ if( fossil_system(zCmd) ){
473
+ fossil_fatal("editor aborted: \"%s\"", zCmd);
474
+ }
475
+
476
+ blob_read_from_file(&reply, zFile);
477
+ }else{
478
+ char zIn[300];
479
+ blob_zero(&reply);
480
+ while( fgets(zIn, sizeof(zIn), stdin)!=0 ){
481
+ if( zIn[0]=='.' && (zIn[1]==0 || zIn[1]=='\r' || zIn[1]=='\n') ){
482
+ break;
483
+ }
484
+ blob_append(&reply, zIn, -1);
485
+ }
486
+ }
487
+ blob_remove_cr(&reply);
488
+ file_delete(zFile);
489
+ free(zFile);
490
+ blob_zero(pComment);
491
+ while( blob_line(&reply, &line) ){
492
+ int i, n;
493
+ char *z;
494
+ n = blob_size(&line);
495
+ z = blob_buffer(&line);
496
+ for(i=0; i<n && fossil_isspace(z[i]); i++){}
497
+ if( i<n && z[i]=='#' ) continue;
498
+ if( i<n || blob_size(pComment)>0 ){
499
+ blob_appendf(pComment, "%b", &line);
500
+ }
501
+ }
502
+ blob_reset(&reply);
503
+ zComment = blob_str(pComment);
504
+ i = strlen(zComment);
505
+ while( i>0 && fossil_isspace(zComment[i-1]) ){ i--; }
506
+ blob_resize(pComment, i);
507
+}
422508
423509
/*
424510
** Prepare a commit comment. Let the user modify it using the
425511
** editor specified in the global_config table or either
426512
** the VISUAL or EDITOR environment variable.
@@ -442,111 +528,43 @@
442528
char *zInit,
443529
const char *zBranch,
444530
int parent_rid,
445531
const char *zUserOvrd
446532
){
533
+ Blob prompt;
534
+#ifdef _WIN32
447535
static const unsigned char bom[] = { 0xEF, 0xBB, 0xBF };
448
- const char *zEditor;
449
- char *zCmd;
450
- char *zFile;
451
- Blob text, line;
452
- char *zComment;
453
- int i;
454
-#ifdef _WIN32
455
- blob_init(&text, (const char *) bom, 3);
536
+ blob_init(&prompt, (const char *) bom, 3);
456537
if( zInit && zInit[0]) {
457
- blob_append(&text, zInit, -1);
538
+ blob_append(&prompt, zInit, -1);
458539
}
459540
#else
460
- blob_init(&text, zInit, -1);
541
+ blob_init(&prompt, zInit, -1);
461542
#endif
462
- blob_append(&text,
543
+ blob_append(&prompt,
463544
"\n"
464545
"# Enter comments on this check-in. Lines beginning with # are ignored.\n"
465546
"# The check-in comment follows wiki formatting rules.\n"
466547
"#\n", -1
467548
);
468
- blob_appendf(&text, "# user: %s\n", zUserOvrd ? zUserOvrd : g.zLogin);
549
+ blob_appendf(&prompt, "# user: %s\n", zUserOvrd ? zUserOvrd : g.zLogin);
469550
if( zBranch && zBranch[0] ){
470
- blob_appendf(&text, "# tags: %s\n#\n", zBranch);
551
+ blob_appendf(&prompt, "# tags: %s\n#\n", zBranch);
471552
}else{
472553
char *zTags = info_tags_of_checkin(parent_rid, 1);
473
- if( zTags ) blob_appendf(&text, "# tags: %z\n#\n", zTags);
554
+ if( zTags ) blob_appendf(&prompt, "# tags: %z\n#\n", zTags);
474555
}
556
+ status_report(&prompt, "# ", 1, 0);
475557
if( g.markPrivate ){
476
- blob_append(&text,
558
+ blob_append(&prompt,
477559
"# PRIVATE BRANCH: This check-in will be private and will not sync to\n"
478560
"# repositories.\n"
479561
"#\n", -1
480562
);
481563
}
482
- status_report(&text, "# ", 1, 0);
483
- zEditor = db_get("editor", 0);
484
- if( zEditor==0 ){
485
- zEditor = fossil_getenv("VISUAL");
486
- }
487
- if( zEditor==0 ){
488
- zEditor = fossil_getenv("EDITOR");
489
- }
490
- if( zEditor==0 ){
491
- blob_append(&text,
492
- "#\n"
493
- "# Since no default text editor is set using EDITOR or VISUAL\n"
494
- "# environment variables or the \"fossil set editor\" command,\n"
495
- "# and because no check-in comment was specified using the \"-m\"\n"
496
- "# or \"-M\" command-line options, you will need to enter the\n"
497
- "# check-in comment below. Type \".\" on a line by itself when\n"
498
- "# you are done:\n", -1);
499
- zFile = mprintf("-");
500
- }else{
501
- zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'",
502
- g.zLocalRoot);
503
- }
504
-#if defined(_WIN32)
505
- blob_add_cr(&text);
506
-#endif
507
- blob_write_to_file(&text, zFile);
508
- if( zEditor ){
509
- zCmd = mprintf("%s \"%s\"", zEditor, zFile);
510
- fossil_print("%s\n", zCmd);
511
- if( fossil_system(zCmd) ){
512
- fossil_panic("editor aborted");
513
- }
514
- blob_reset(&text);
515
- blob_read_from_file(&text, zFile);
516
- }else{
517
- char zIn[300];
518
- blob_reset(&text);
519
- while( fgets(zIn, sizeof(zIn), stdin)!=0 ){
520
- if( zIn[0]=='.' && (zIn[1]==0 || zIn[1]=='\r' || zIn[1]=='\n') ){
521
- break;
522
- }
523
- blob_append(&text, zIn, -1);
524
- }
525
- }
526
- blob_remove_cr(&text);
527
- if( zEditor ) {
528
- file_delete(zFile);
529
- }
530
- fossil_free(zFile);
531
- blob_zero(pComment);
532
- while( blob_line(&text, &line) ){
533
- int i, n;
534
- char *z;
535
- n = blob_size(&line);
536
- z = blob_buffer(&line);
537
- for(i=0; i<n && fossil_isspace(z[i]); i++){}
538
- if( i<n && z[i]=='#' ) continue;
539
- if( i<n || blob_size(pComment)>0 ){
540
- blob_appendf(pComment, "%b", &line);
541
- }
542
- }
543
- blob_reset(&text);
544
- zComment = blob_str(pComment);
545
- i = strlen(zComment);
546
- while( i>0 && fossil_isspace(zComment[i-1]) ){ i--; }
547
- blob_resize(pComment, i);
564
+ prompt_for_user_comment(pComment, &prompt);
565
+ blob_reset(&prompt);
548566
}
549567
550568
/*
551569
** Populate the Global.aCommitFile[] based on the command line arguments
552570
** to a [commit] command. Global.aCommitFile is an array of integers
553571
--- src/checkin.c
+++ src/checkin.c
@@ -417,10 +417,96 @@
417 }
418 }
419 }
420 db_finalize(&q);
421 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
422
423 /*
424 ** Prepare a commit comment. Let the user modify it using the
425 ** editor specified in the global_config table or either
426 ** the VISUAL or EDITOR environment variable.
@@ -442,111 +528,43 @@
442 char *zInit,
443 const char *zBranch,
444 int parent_rid,
445 const char *zUserOvrd
446 ){
 
 
447 static const unsigned char bom[] = { 0xEF, 0xBB, 0xBF };
448 const char *zEditor;
449 char *zCmd;
450 char *zFile;
451 Blob text, line;
452 char *zComment;
453 int i;
454 #ifdef _WIN32
455 blob_init(&text, (const char *) bom, 3);
456 if( zInit && zInit[0]) {
457 blob_append(&text, zInit, -1);
458 }
459 #else
460 blob_init(&text, zInit, -1);
461 #endif
462 blob_append(&text,
463 "\n"
464 "# Enter comments on this check-in. Lines beginning with # are ignored.\n"
465 "# The check-in comment follows wiki formatting rules.\n"
466 "#\n", -1
467 );
468 blob_appendf(&text, "# user: %s\n", zUserOvrd ? zUserOvrd : g.zLogin);
469 if( zBranch && zBranch[0] ){
470 blob_appendf(&text, "# tags: %s\n#\n", zBranch);
471 }else{
472 char *zTags = info_tags_of_checkin(parent_rid, 1);
473 if( zTags ) blob_appendf(&text, "# tags: %z\n#\n", zTags);
474 }
 
475 if( g.markPrivate ){
476 blob_append(&text,
477 "# PRIVATE BRANCH: This check-in will be private and will not sync to\n"
478 "# repositories.\n"
479 "#\n", -1
480 );
481 }
482 status_report(&text, "# ", 1, 0);
483 zEditor = db_get("editor", 0);
484 if( zEditor==0 ){
485 zEditor = fossil_getenv("VISUAL");
486 }
487 if( zEditor==0 ){
488 zEditor = fossil_getenv("EDITOR");
489 }
490 if( zEditor==0 ){
491 blob_append(&text,
492 "#\n"
493 "# Since no default text editor is set using EDITOR or VISUAL\n"
494 "# environment variables or the \"fossil set editor\" command,\n"
495 "# and because no check-in comment was specified using the \"-m\"\n"
496 "# or \"-M\" command-line options, you will need to enter the\n"
497 "# check-in comment below. Type \".\" on a line by itself when\n"
498 "# you are done:\n", -1);
499 zFile = mprintf("-");
500 }else{
501 zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'",
502 g.zLocalRoot);
503 }
504 #if defined(_WIN32)
505 blob_add_cr(&text);
506 #endif
507 blob_write_to_file(&text, zFile);
508 if( zEditor ){
509 zCmd = mprintf("%s \"%s\"", zEditor, zFile);
510 fossil_print("%s\n", zCmd);
511 if( fossil_system(zCmd) ){
512 fossil_panic("editor aborted");
513 }
514 blob_reset(&text);
515 blob_read_from_file(&text, zFile);
516 }else{
517 char zIn[300];
518 blob_reset(&text);
519 while( fgets(zIn, sizeof(zIn), stdin)!=0 ){
520 if( zIn[0]=='.' && (zIn[1]==0 || zIn[1]=='\r' || zIn[1]=='\n') ){
521 break;
522 }
523 blob_append(&text, zIn, -1);
524 }
525 }
526 blob_remove_cr(&text);
527 if( zEditor ) {
528 file_delete(zFile);
529 }
530 fossil_free(zFile);
531 blob_zero(pComment);
532 while( blob_line(&text, &line) ){
533 int i, n;
534 char *z;
535 n = blob_size(&line);
536 z = blob_buffer(&line);
537 for(i=0; i<n && fossil_isspace(z[i]); i++){}
538 if( i<n && z[i]=='#' ) continue;
539 if( i<n || blob_size(pComment)>0 ){
540 blob_appendf(pComment, "%b", &line);
541 }
542 }
543 blob_reset(&text);
544 zComment = blob_str(pComment);
545 i = strlen(zComment);
546 while( i>0 && fossil_isspace(zComment[i-1]) ){ i--; }
547 blob_resize(pComment, i);
548 }
549
550 /*
551 ** Populate the Global.aCommitFile[] based on the command line arguments
552 ** to a [commit] command. Global.aCommitFile is an array of integers
553
--- src/checkin.c
+++ src/checkin.c
@@ -417,10 +417,96 @@
417 }
418 }
419 }
420 db_finalize(&q);
421 }
422
423 /*
424 ** Prompt the user for a check-in or stash comment (given in pPrompt),
425 ** gather the response, then return the response in pComment.
426 **
427 ** Lines of the prompt that begin with # are discarded. Excess whitespace
428 ** is removed from the reply.
429 **
430 ** Appropriate encoding translations are made on windows.
431 */
432 void prompt_for_user_comment(Blob *pComment, Blob *pPrompt){
433 const char *zEditor;
434 char *zCmd;
435 char *zFile;
436 Blob reply, line;
437 char *zComment;
438 int i;
439
440 zEditor = db_get("editor", 0);
441 if( zEditor==0 ){
442 zEditor = fossil_getenv("VISUAL");
443 }
444 if( zEditor==0 ){
445 zEditor = fossil_getenv("EDITOR");
446 }
447 #ifdef _WIN32
448 if( zEditor==0 ){
449 zEditor = mprintf("%s\\notepad.exe", fossil_getenv("SystemRoot"));
450 }
451 #endif
452 if( zEditor==0 ){
453 blob_append(pPrompt,
454 "#\n"
455 "# Since no default text editor is set using EDITOR or VISUAL\n"
456 "# environment variables or the \"fossil set editor\" command,\n"
457 "# and because no comment was specified using the \"-m\" or \"-M\"\n"
458 "# command-line options, you will need to enter the comment below.\n"
459 "# Type \".\" on a line by itself when you are done:\n", -1);
460 zFile = mprintf("-");
461 }else{
462 zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'",
463 g.zLocalRoot);
464 }
465 #if defined(_WIN32)
466 blob_add_cr(pPrompt);
467 #endif
468 blob_write_to_file(pPrompt, zFile);
469 if( zEditor ){
470 zCmd = mprintf("%s \"%s\"", zEditor, zFile);
471 fossil_print("%s\n", zCmd);
472 if( fossil_system(zCmd) ){
473 fossil_fatal("editor aborted: \"%s\"", zCmd);
474 }
475
476 blob_read_from_file(&reply, zFile);
477 }else{
478 char zIn[300];
479 blob_zero(&reply);
480 while( fgets(zIn, sizeof(zIn), stdin)!=0 ){
481 if( zIn[0]=='.' && (zIn[1]==0 || zIn[1]=='\r' || zIn[1]=='\n') ){
482 break;
483 }
484 blob_append(&reply, zIn, -1);
485 }
486 }
487 blob_remove_cr(&reply);
488 file_delete(zFile);
489 free(zFile);
490 blob_zero(pComment);
491 while( blob_line(&reply, &line) ){
492 int i, n;
493 char *z;
494 n = blob_size(&line);
495 z = blob_buffer(&line);
496 for(i=0; i<n && fossil_isspace(z[i]); i++){}
497 if( i<n && z[i]=='#' ) continue;
498 if( i<n || blob_size(pComment)>0 ){
499 blob_appendf(pComment, "%b", &line);
500 }
501 }
502 blob_reset(&reply);
503 zComment = blob_str(pComment);
504 i = strlen(zComment);
505 while( i>0 && fossil_isspace(zComment[i-1]) ){ i--; }
506 blob_resize(pComment, i);
507 }
508
509 /*
510 ** Prepare a commit comment. Let the user modify it using the
511 ** editor specified in the global_config table or either
512 ** the VISUAL or EDITOR environment variable.
@@ -442,111 +528,43 @@
528 char *zInit,
529 const char *zBranch,
530 int parent_rid,
531 const char *zUserOvrd
532 ){
533 Blob prompt;
534 #ifdef _WIN32
535 static const unsigned char bom[] = { 0xEF, 0xBB, 0xBF };
536 blob_init(&prompt, (const char *) bom, 3);
 
 
 
 
 
 
 
537 if( zInit && zInit[0]) {
538 blob_append(&prompt, zInit, -1);
539 }
540 #else
541 blob_init(&prompt, zInit, -1);
542 #endif
543 blob_append(&prompt,
544 "\n"
545 "# Enter comments on this check-in. Lines beginning with # are ignored.\n"
546 "# The check-in comment follows wiki formatting rules.\n"
547 "#\n", -1
548 );
549 blob_appendf(&prompt, "# user: %s\n", zUserOvrd ? zUserOvrd : g.zLogin);
550 if( zBranch && zBranch[0] ){
551 blob_appendf(&prompt, "# tags: %s\n#\n", zBranch);
552 }else{
553 char *zTags = info_tags_of_checkin(parent_rid, 1);
554 if( zTags ) blob_appendf(&prompt, "# tags: %z\n#\n", zTags);
555 }
556 status_report(&prompt, "# ", 1, 0);
557 if( g.markPrivate ){
558 blob_append(&prompt,
559 "# PRIVATE BRANCH: This check-in will be private and will not sync to\n"
560 "# repositories.\n"
561 "#\n", -1
562 );
563 }
564 prompt_for_user_comment(pComment, &prompt);
565 blob_reset(&prompt);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
566 }
567
568 /*
569 ** Populate the Global.aCommitFile[] based on the command line arguments
570 ** to a [commit] command. Global.aCommitFile is an array of integers
571
+99 -81
--- src/checkin.c
+++ src/checkin.c
@@ -417,10 +417,96 @@
417417
}
418418
}
419419
}
420420
db_finalize(&q);
421421
}
422
+
423
+/*
424
+** Prompt the user for a check-in or stash comment (given in pPrompt),
425
+** gather the response, then return the response in pComment.
426
+**
427
+** Lines of the prompt that begin with # are discarded. Excess whitespace
428
+** is removed from the reply.
429
+**
430
+** Appropriate encoding translations are made on windows.
431
+*/
432
+void prompt_for_user_comment(Blob *pComment, Blob *pPrompt){
433
+ const char *zEditor;
434
+ char *zCmd;
435
+ char *zFile;
436
+ Blob reply, line;
437
+ char *zComment;
438
+ int i;
439
+
440
+ zEditor = db_get("editor", 0);
441
+ if( zEditor==0 ){
442
+ zEditor = fossil_getenv("VISUAL");
443
+ }
444
+ if( zEditor==0 ){
445
+ zEditor = fossil_getenv("EDITOR");
446
+ }
447
+#ifdef _WIN32
448
+ if( zEditor==0 ){
449
+ zEditor = mprintf("%s\\notepad.exe", fossil_getenv("SystemRoot"));
450
+ }
451
+#endif
452
+ if( zEditor==0 ){
453
+ blob_append(pPrompt,
454
+ "#\n"
455
+ "# Since no default text editor is set using EDITOR or VISUAL\n"
456
+ "# environment variables or the \"fossil set editor\" command,\n"
457
+ "# and because no comment was specified using the \"-m\" or \"-M\"\n"
458
+ "# command-line options, you will need to enter the comment below.\n"
459
+ "# Type \".\" on a line by itself when you are done:\n", -1);
460
+ zFile = mprintf("-");
461
+ }else{
462
+ zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'",
463
+ g.zLocalRoot);
464
+ }
465
+#if defined(_WIN32)
466
+ blob_add_cr(pPrompt);
467
+#endif
468
+ blob_write_to_file(pPrompt, zFile);
469
+ if( zEditor ){
470
+ zCmd = mprintf("%s \"%s\"", zEditor, zFile);
471
+ fossil_print("%s\n", zCmd);
472
+ if( fossil_system(zCmd) ){
473
+ fossil_fatal("editor aborted: \"%s\"", zCmd);
474
+ }
475
+
476
+ blob_read_from_file(&reply, zFile);
477
+ }else{
478
+ char zIn[300];
479
+ blob_zero(&reply);
480
+ while( fgets(zIn, sizeof(zIn), stdin)!=0 ){
481
+ if( zIn[0]=='.' && (zIn[1]==0 || zIn[1]=='\r' || zIn[1]=='\n') ){
482
+ break;
483
+ }
484
+ blob_append(&reply, zIn, -1);
485
+ }
486
+ }
487
+ blob_remove_cr(&reply);
488
+ file_delete(zFile);
489
+ free(zFile);
490
+ blob_zero(pComment);
491
+ while( blob_line(&reply, &line) ){
492
+ int i, n;
493
+ char *z;
494
+ n = blob_size(&line);
495
+ z = blob_buffer(&line);
496
+ for(i=0; i<n && fossil_isspace(z[i]); i++){}
497
+ if( i<n && z[i]=='#' ) continue;
498
+ if( i<n || blob_size(pComment)>0 ){
499
+ blob_appendf(pComment, "%b", &line);
500
+ }
501
+ }
502
+ blob_reset(&reply);
503
+ zComment = blob_str(pComment);
504
+ i = strlen(zComment);
505
+ while( i>0 && fossil_isspace(zComment[i-1]) ){ i--; }
506
+ blob_resize(pComment, i);
507
+}
422508
423509
/*
424510
** Prepare a commit comment. Let the user modify it using the
425511
** editor specified in the global_config table or either
426512
** the VISUAL or EDITOR environment variable.
@@ -442,111 +528,43 @@
442528
char *zInit,
443529
const char *zBranch,
444530
int parent_rid,
445531
const char *zUserOvrd
446532
){
533
+ Blob prompt;
534
+#ifdef _WIN32
447535
static const unsigned char bom[] = { 0xEF, 0xBB, 0xBF };
448
- const char *zEditor;
449
- char *zCmd;
450
- char *zFile;
451
- Blob text, line;
452
- char *zComment;
453
- int i;
454
-#ifdef _WIN32
455
- blob_init(&text, (const char *) bom, 3);
536
+ blob_init(&prompt, (const char *) bom, 3);
456537
if( zInit && zInit[0]) {
457
- blob_append(&text, zInit, -1);
538
+ blob_append(&prompt, zInit, -1);
458539
}
459540
#else
460
- blob_init(&text, zInit, -1);
541
+ blob_init(&prompt, zInit, -1);
461542
#endif
462
- blob_append(&text,
543
+ blob_append(&prompt,
463544
"\n"
464545
"# Enter comments on this check-in. Lines beginning with # are ignored.\n"
465546
"# The check-in comment follows wiki formatting rules.\n"
466547
"#\n", -1
467548
);
468
- blob_appendf(&text, "# user: %s\n", zUserOvrd ? zUserOvrd : g.zLogin);
549
+ blob_appendf(&prompt, "# user: %s\n", zUserOvrd ? zUserOvrd : g.zLogin);
469550
if( zBranch && zBranch[0] ){
470
- blob_appendf(&text, "# tags: %s\n#\n", zBranch);
551
+ blob_appendf(&prompt, "# tags: %s\n#\n", zBranch);
471552
}else{
472553
char *zTags = info_tags_of_checkin(parent_rid, 1);
473
- if( zTags ) blob_appendf(&text, "# tags: %z\n#\n", zTags);
554
+ if( zTags ) blob_appendf(&prompt, "# tags: %z\n#\n", zTags);
474555
}
556
+ status_report(&prompt, "# ", 1, 0);
475557
if( g.markPrivate ){
476
- blob_append(&text,
558
+ blob_append(&prompt,
477559
"# PRIVATE BRANCH: This check-in will be private and will not sync to\n"
478560
"# repositories.\n"
479561
"#\n", -1
480562
);
481563
}
482
- status_report(&text, "# ", 1, 0);
483
- zEditor = db_get("editor", 0);
484
- if( zEditor==0 ){
485
- zEditor = fossil_getenv("VISUAL");
486
- }
487
- if( zEditor==0 ){
488
- zEditor = fossil_getenv("EDITOR");
489
- }
490
- if( zEditor==0 ){
491
- blob_append(&text,
492
- "#\n"
493
- "# Since no default text editor is set using EDITOR or VISUAL\n"
494
- "# environment variables or the \"fossil set editor\" command,\n"
495
- "# and because no check-in comment was specified using the \"-m\"\n"
496
- "# or \"-M\" command-line options, you will need to enter the\n"
497
- "# check-in comment below. Type \".\" on a line by itself when\n"
498
- "# you are done:\n", -1);
499
- zFile = mprintf("-");
500
- }else{
501
- zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'",
502
- g.zLocalRoot);
503
- }
504
-#if defined(_WIN32)
505
- blob_add_cr(&text);
506
-#endif
507
- blob_write_to_file(&text, zFile);
508
- if( zEditor ){
509
- zCmd = mprintf("%s \"%s\"", zEditor, zFile);
510
- fossil_print("%s\n", zCmd);
511
- if( fossil_system(zCmd) ){
512
- fossil_panic("editor aborted");
513
- }
514
- blob_reset(&text);
515
- blob_read_from_file(&text, zFile);
516
- }else{
517
- char zIn[300];
518
- blob_reset(&text);
519
- while( fgets(zIn, sizeof(zIn), stdin)!=0 ){
520
- if( zIn[0]=='.' && (zIn[1]==0 || zIn[1]=='\r' || zIn[1]=='\n') ){
521
- break;
522
- }
523
- blob_append(&text, zIn, -1);
524
- }
525
- }
526
- blob_remove_cr(&text);
527
- if( zEditor ) {
528
- file_delete(zFile);
529
- }
530
- fossil_free(zFile);
531
- blob_zero(pComment);
532
- while( blob_line(&text, &line) ){
533
- int i, n;
534
- char *z;
535
- n = blob_size(&line);
536
- z = blob_buffer(&line);
537
- for(i=0; i<n && fossil_isspace(z[i]); i++){}
538
- if( i<n && z[i]=='#' ) continue;
539
- if( i<n || blob_size(pComment)>0 ){
540
- blob_appendf(pComment, "%b", &line);
541
- }
542
- }
543
- blob_reset(&text);
544
- zComment = blob_str(pComment);
545
- i = strlen(zComment);
546
- while( i>0 && fossil_isspace(zComment[i-1]) ){ i--; }
547
- blob_resize(pComment, i);
564
+ prompt_for_user_comment(pComment, &prompt);
565
+ blob_reset(&prompt);
548566
}
549567
550568
/*
551569
** Populate the Global.aCommitFile[] based on the command line arguments
552570
** to a [commit] command. Global.aCommitFile is an array of integers
553571
--- src/checkin.c
+++ src/checkin.c
@@ -417,10 +417,96 @@
417 }
418 }
419 }
420 db_finalize(&q);
421 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
422
423 /*
424 ** Prepare a commit comment. Let the user modify it using the
425 ** editor specified in the global_config table or either
426 ** the VISUAL or EDITOR environment variable.
@@ -442,111 +528,43 @@
442 char *zInit,
443 const char *zBranch,
444 int parent_rid,
445 const char *zUserOvrd
446 ){
 
 
447 static const unsigned char bom[] = { 0xEF, 0xBB, 0xBF };
448 const char *zEditor;
449 char *zCmd;
450 char *zFile;
451 Blob text, line;
452 char *zComment;
453 int i;
454 #ifdef _WIN32
455 blob_init(&text, (const char *) bom, 3);
456 if( zInit && zInit[0]) {
457 blob_append(&text, zInit, -1);
458 }
459 #else
460 blob_init(&text, zInit, -1);
461 #endif
462 blob_append(&text,
463 "\n"
464 "# Enter comments on this check-in. Lines beginning with # are ignored.\n"
465 "# The check-in comment follows wiki formatting rules.\n"
466 "#\n", -1
467 );
468 blob_appendf(&text, "# user: %s\n", zUserOvrd ? zUserOvrd : g.zLogin);
469 if( zBranch && zBranch[0] ){
470 blob_appendf(&text, "# tags: %s\n#\n", zBranch);
471 }else{
472 char *zTags = info_tags_of_checkin(parent_rid, 1);
473 if( zTags ) blob_appendf(&text, "# tags: %z\n#\n", zTags);
474 }
 
475 if( g.markPrivate ){
476 blob_append(&text,
477 "# PRIVATE BRANCH: This check-in will be private and will not sync to\n"
478 "# repositories.\n"
479 "#\n", -1
480 );
481 }
482 status_report(&text, "# ", 1, 0);
483 zEditor = db_get("editor", 0);
484 if( zEditor==0 ){
485 zEditor = fossil_getenv("VISUAL");
486 }
487 if( zEditor==0 ){
488 zEditor = fossil_getenv("EDITOR");
489 }
490 if( zEditor==0 ){
491 blob_append(&text,
492 "#\n"
493 "# Since no default text editor is set using EDITOR or VISUAL\n"
494 "# environment variables or the \"fossil set editor\" command,\n"
495 "# and because no check-in comment was specified using the \"-m\"\n"
496 "# or \"-M\" command-line options, you will need to enter the\n"
497 "# check-in comment below. Type \".\" on a line by itself when\n"
498 "# you are done:\n", -1);
499 zFile = mprintf("-");
500 }else{
501 zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'",
502 g.zLocalRoot);
503 }
504 #if defined(_WIN32)
505 blob_add_cr(&text);
506 #endif
507 blob_write_to_file(&text, zFile);
508 if( zEditor ){
509 zCmd = mprintf("%s \"%s\"", zEditor, zFile);
510 fossil_print("%s\n", zCmd);
511 if( fossil_system(zCmd) ){
512 fossil_panic("editor aborted");
513 }
514 blob_reset(&text);
515 blob_read_from_file(&text, zFile);
516 }else{
517 char zIn[300];
518 blob_reset(&text);
519 while( fgets(zIn, sizeof(zIn), stdin)!=0 ){
520 if( zIn[0]=='.' && (zIn[1]==0 || zIn[1]=='\r' || zIn[1]=='\n') ){
521 break;
522 }
523 blob_append(&text, zIn, -1);
524 }
525 }
526 blob_remove_cr(&text);
527 if( zEditor ) {
528 file_delete(zFile);
529 }
530 fossil_free(zFile);
531 blob_zero(pComment);
532 while( blob_line(&text, &line) ){
533 int i, n;
534 char *z;
535 n = blob_size(&line);
536 z = blob_buffer(&line);
537 for(i=0; i<n && fossil_isspace(z[i]); i++){}
538 if( i<n && z[i]=='#' ) continue;
539 if( i<n || blob_size(pComment)>0 ){
540 blob_appendf(pComment, "%b", &line);
541 }
542 }
543 blob_reset(&text);
544 zComment = blob_str(pComment);
545 i = strlen(zComment);
546 while( i>0 && fossil_isspace(zComment[i-1]) ){ i--; }
547 blob_resize(pComment, i);
548 }
549
550 /*
551 ** Populate the Global.aCommitFile[] based on the command line arguments
552 ** to a [commit] command. Global.aCommitFile is an array of integers
553
--- src/checkin.c
+++ src/checkin.c
@@ -417,10 +417,96 @@
417 }
418 }
419 }
420 db_finalize(&q);
421 }
422
423 /*
424 ** Prompt the user for a check-in or stash comment (given in pPrompt),
425 ** gather the response, then return the response in pComment.
426 **
427 ** Lines of the prompt that begin with # are discarded. Excess whitespace
428 ** is removed from the reply.
429 **
430 ** Appropriate encoding translations are made on windows.
431 */
432 void prompt_for_user_comment(Blob *pComment, Blob *pPrompt){
433 const char *zEditor;
434 char *zCmd;
435 char *zFile;
436 Blob reply, line;
437 char *zComment;
438 int i;
439
440 zEditor = db_get("editor", 0);
441 if( zEditor==0 ){
442 zEditor = fossil_getenv("VISUAL");
443 }
444 if( zEditor==0 ){
445 zEditor = fossil_getenv("EDITOR");
446 }
447 #ifdef _WIN32
448 if( zEditor==0 ){
449 zEditor = mprintf("%s\\notepad.exe", fossil_getenv("SystemRoot"));
450 }
451 #endif
452 if( zEditor==0 ){
453 blob_append(pPrompt,
454 "#\n"
455 "# Since no default text editor is set using EDITOR or VISUAL\n"
456 "# environment variables or the \"fossil set editor\" command,\n"
457 "# and because no comment was specified using the \"-m\" or \"-M\"\n"
458 "# command-line options, you will need to enter the comment below.\n"
459 "# Type \".\" on a line by itself when you are done:\n", -1);
460 zFile = mprintf("-");
461 }else{
462 zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'",
463 g.zLocalRoot);
464 }
465 #if defined(_WIN32)
466 blob_add_cr(pPrompt);
467 #endif
468 blob_write_to_file(pPrompt, zFile);
469 if( zEditor ){
470 zCmd = mprintf("%s \"%s\"", zEditor, zFile);
471 fossil_print("%s\n", zCmd);
472 if( fossil_system(zCmd) ){
473 fossil_fatal("editor aborted: \"%s\"", zCmd);
474 }
475
476 blob_read_from_file(&reply, zFile);
477 }else{
478 char zIn[300];
479 blob_zero(&reply);
480 while( fgets(zIn, sizeof(zIn), stdin)!=0 ){
481 if( zIn[0]=='.' && (zIn[1]==0 || zIn[1]=='\r' || zIn[1]=='\n') ){
482 break;
483 }
484 blob_append(&reply, zIn, -1);
485 }
486 }
487 blob_remove_cr(&reply);
488 file_delete(zFile);
489 free(zFile);
490 blob_zero(pComment);
491 while( blob_line(&reply, &line) ){
492 int i, n;
493 char *z;
494 n = blob_size(&line);
495 z = blob_buffer(&line);
496 for(i=0; i<n && fossil_isspace(z[i]); i++){}
497 if( i<n && z[i]=='#' ) continue;
498 if( i<n || blob_size(pComment)>0 ){
499 blob_appendf(pComment, "%b", &line);
500 }
501 }
502 blob_reset(&reply);
503 zComment = blob_str(pComment);
504 i = strlen(zComment);
505 while( i>0 && fossil_isspace(zComment[i-1]) ){ i--; }
506 blob_resize(pComment, i);
507 }
508
509 /*
510 ** Prepare a commit comment. Let the user modify it using the
511 ** editor specified in the global_config table or either
512 ** the VISUAL or EDITOR environment variable.
@@ -442,111 +528,43 @@
528 char *zInit,
529 const char *zBranch,
530 int parent_rid,
531 const char *zUserOvrd
532 ){
533 Blob prompt;
534 #ifdef _WIN32
535 static const unsigned char bom[] = { 0xEF, 0xBB, 0xBF };
536 blob_init(&prompt, (const char *) bom, 3);
 
 
 
 
 
 
 
537 if( zInit && zInit[0]) {
538 blob_append(&prompt, zInit, -1);
539 }
540 #else
541 blob_init(&prompt, zInit, -1);
542 #endif
543 blob_append(&prompt,
544 "\n"
545 "# Enter comments on this check-in. Lines beginning with # are ignored.\n"
546 "# The check-in comment follows wiki formatting rules.\n"
547 "#\n", -1
548 );
549 blob_appendf(&prompt, "# user: %s\n", zUserOvrd ? zUserOvrd : g.zLogin);
550 if( zBranch && zBranch[0] ){
551 blob_appendf(&prompt, "# tags: %s\n#\n", zBranch);
552 }else{
553 char *zTags = info_tags_of_checkin(parent_rid, 1);
554 if( zTags ) blob_appendf(&prompt, "# tags: %z\n#\n", zTags);
555 }
556 status_report(&prompt, "# ", 1, 0);
557 if( g.markPrivate ){
558 blob_append(&prompt,
559 "# PRIVATE BRANCH: This check-in will be private and will not sync to\n"
560 "# repositories.\n"
561 "#\n", -1
562 );
563 }
564 prompt_for_user_comment(pComment, &prompt);
565 blob_reset(&prompt);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
566 }
567
568 /*
569 ** Populate the Global.aCommitFile[] based on the command line arguments
570 ** to a [commit] command. Global.aCommitFile is an array of integers
571
--- src/configure.c
+++ src/configure.c
@@ -89,17 +89,20 @@
8989
{ "timeline-block-markup", CONFIGSET_SKIN },
9090
{ "timeline-max-comment", CONFIGSET_SKIN },
9191
{ "adunit", CONFIGSET_SKIN },
9292
{ "adunit-omit-if-admin", CONFIGSET_SKIN },
9393
{ "adunit-omit-if-user", CONFIGSET_SKIN },
94
+
9495
#ifdef FOSSIL_ENABLE_TCL
9596
{ "tcl", CONFIGSET_SKIN|CONFIGSET_TKT|CONFIGSET_XFER },
97
+ { "tcl-setup", CONFIGSET_SKIN|CONFIGSET_TKT|CONFIGSET_XFER },
9698
#endif
9799
98100
{ "project-name", CONFIGSET_PROJ },
99101
{ "project-description", CONFIGSET_PROJ },
100102
{ "manifest", CONFIGSET_PROJ },
103
+ { "binary-glob", CONFIGSET_PROJ },
101104
{ "ignore-glob", CONFIGSET_PROJ },
102105
{ "crnl-glob", CONFIGSET_PROJ },
103106
{ "empty-dirs", CONFIGSET_PROJ },
104107
{ "allow-symlinks", CONFIGSET_PROJ },
105108
106109
--- src/configure.c
+++ src/configure.c
@@ -89,17 +89,20 @@
89 { "timeline-block-markup", CONFIGSET_SKIN },
90 { "timeline-max-comment", CONFIGSET_SKIN },
91 { "adunit", CONFIGSET_SKIN },
92 { "adunit-omit-if-admin", CONFIGSET_SKIN },
93 { "adunit-omit-if-user", CONFIGSET_SKIN },
 
94 #ifdef FOSSIL_ENABLE_TCL
95 { "tcl", CONFIGSET_SKIN|CONFIGSET_TKT|CONFIGSET_XFER },
 
96 #endif
97
98 { "project-name", CONFIGSET_PROJ },
99 { "project-description", CONFIGSET_PROJ },
100 { "manifest", CONFIGSET_PROJ },
 
101 { "ignore-glob", CONFIGSET_PROJ },
102 { "crnl-glob", CONFIGSET_PROJ },
103 { "empty-dirs", CONFIGSET_PROJ },
104 { "allow-symlinks", CONFIGSET_PROJ },
105
106
--- src/configure.c
+++ src/configure.c
@@ -89,17 +89,20 @@
89 { "timeline-block-markup", CONFIGSET_SKIN },
90 { "timeline-max-comment", CONFIGSET_SKIN },
91 { "adunit", CONFIGSET_SKIN },
92 { "adunit-omit-if-admin", CONFIGSET_SKIN },
93 { "adunit-omit-if-user", CONFIGSET_SKIN },
94
95 #ifdef FOSSIL_ENABLE_TCL
96 { "tcl", CONFIGSET_SKIN|CONFIGSET_TKT|CONFIGSET_XFER },
97 { "tcl-setup", CONFIGSET_SKIN|CONFIGSET_TKT|CONFIGSET_XFER },
98 #endif
99
100 { "project-name", CONFIGSET_PROJ },
101 { "project-description", CONFIGSET_PROJ },
102 { "manifest", CONFIGSET_PROJ },
103 { "binary-glob", CONFIGSET_PROJ },
104 { "ignore-glob", CONFIGSET_PROJ },
105 { "crnl-glob", CONFIGSET_PROJ },
106 { "empty-dirs", CONFIGSET_PROJ },
107 { "allow-symlinks", CONFIGSET_PROJ },
108
109
+16 -5
--- src/db.c
+++ src/db.c
@@ -2014,10 +2014,11 @@
20142014
{ "binary-glob", 0, 32, 1, "" },
20152015
{ "clearsign", 0, 0, 0, "off" },
20162016
{ "case-sensitive",0, 0, 0, "on" },
20172017
{ "crnl-glob", 0, 16, 1, "" },
20182018
{ "default-perms", 0, 16, 0, "u" },
2019
+ { "diff-binary", 0, 0, 0, "on" },
20192020
{ "diff-command", 0, 16, 0, "" },
20202021
{ "dont-push", 0, 0, 0, "off" },
20212022
{ "editor", 0, 16, 0, "" },
20222023
{ "gdiff-command", 0, 16, 0, "gdiff" },
20232024
{ "gmerge-command",0, 40, 0, "" },
@@ -2038,10 +2039,11 @@
20382039
{ "ssl-ca-location",0, 40, 0, "" },
20392040
{ "ssl-identity", 0, 40, 0, "" },
20402041
{ "ssh-command", 0, 32, 0, "" },
20412042
#ifdef FOSSIL_ENABLE_TCL
20422043
{ "tcl", 0, 0, 0, "off" },
2044
+ { "tcl-setup", 0, 40, 0, "" },
20432045
#endif
20442046
{ "web-browser", 0, 32, 0, "" },
20452047
{ "white-foreground", 0, 0, 0, "off" },
20462048
{ 0,0,0,0,0 }
20472049
};
@@ -2106,10 +2108,14 @@
21062108
** Set to "*" to disable CR+NL checking.
21072109
**
21082110
** default-perms Permissions given automatically to new users. For more
21092111
** information on permissions see Users page in Server
21102112
** Administration of the HTTP UI. Default: u.
2113
+**
2114
+** diff-binary If TRUE (the default), permit files that may be binary
2115
+** or that match the "binary-glob" setting to be used with
2116
+** external diff programs. If FALSE, skip these files.
21112117
**
21122118
** diff-command External command to run when performing a diff.
21132119
** If undefined, the internal text diff will be used.
21142120
**
21152121
** dont-push Prevent this repository from pushing from client to
@@ -2198,15 +2204,20 @@
21982204
** password authentication.
21992205
**
22002206
** ssh-command Command used to talk to a remote machine with
22012207
** the "ssh://" protocol.
22022208
**
2203
-** tcl If enabled, Tcl integration commands will be added to
2204
-** the TH1 interpreter, allowing Tcl expressions and
2205
-** scripts to be evaluated from TH1. Additionally, the
2206
-** Tcl interpreter will be able to evaluate TH1 expressions
2207
-** and scripts. Default: off.
2209
+** tcl If enabled (and Fossil was compiled with Tcl support),
2210
+** Tcl integration commands will be added to the TH1
2211
+** interpreter, allowing arbitrary Tcl expressions and
2212
+** scripts to be evaluated from TH1. Additionally, the Tcl
2213
+** interpreter will be able to evaluate arbitrary TH1
2214
+** expressions and scripts. Default: off.
2215
+**
2216
+** tcl-setup This is the setup script to be evaluated after creating
2217
+** and initializing the Tcl interpreter. By default, this
2218
+** is empty and no extra setup is performed.
22082219
**
22092220
** web-browser A shell command used to launch your preferred
22102221
** web browser when given a URL as an argument.
22112222
** Defaults to "start" on windows, "open" on Mac,
22122223
** and "firefox" on Unix.
22132224
--- src/db.c
+++ src/db.c
@@ -2014,10 +2014,11 @@
2014 { "binary-glob", 0, 32, 1, "" },
2015 { "clearsign", 0, 0, 0, "off" },
2016 { "case-sensitive",0, 0, 0, "on" },
2017 { "crnl-glob", 0, 16, 1, "" },
2018 { "default-perms", 0, 16, 0, "u" },
 
2019 { "diff-command", 0, 16, 0, "" },
2020 { "dont-push", 0, 0, 0, "off" },
2021 { "editor", 0, 16, 0, "" },
2022 { "gdiff-command", 0, 16, 0, "gdiff" },
2023 { "gmerge-command",0, 40, 0, "" },
@@ -2038,10 +2039,11 @@
2038 { "ssl-ca-location",0, 40, 0, "" },
2039 { "ssl-identity", 0, 40, 0, "" },
2040 { "ssh-command", 0, 32, 0, "" },
2041 #ifdef FOSSIL_ENABLE_TCL
2042 { "tcl", 0, 0, 0, "off" },
 
2043 #endif
2044 { "web-browser", 0, 32, 0, "" },
2045 { "white-foreground", 0, 0, 0, "off" },
2046 { 0,0,0,0,0 }
2047 };
@@ -2106,10 +2108,14 @@
2106 ** Set to "*" to disable CR+NL checking.
2107 **
2108 ** default-perms Permissions given automatically to new users. For more
2109 ** information on permissions see Users page in Server
2110 ** Administration of the HTTP UI. Default: u.
 
 
 
 
2111 **
2112 ** diff-command External command to run when performing a diff.
2113 ** If undefined, the internal text diff will be used.
2114 **
2115 ** dont-push Prevent this repository from pushing from client to
@@ -2198,15 +2204,20 @@
2198 ** password authentication.
2199 **
2200 ** ssh-command Command used to talk to a remote machine with
2201 ** the "ssh://" protocol.
2202 **
2203 ** tcl If enabled, Tcl integration commands will be added to
2204 ** the TH1 interpreter, allowing Tcl expressions and
2205 ** scripts to be evaluated from TH1. Additionally, the
2206 ** Tcl interpreter will be able to evaluate TH1 expressions
2207 ** and scripts. Default: off.
 
 
 
 
 
2208 **
2209 ** web-browser A shell command used to launch your preferred
2210 ** web browser when given a URL as an argument.
2211 ** Defaults to "start" on windows, "open" on Mac,
2212 ** and "firefox" on Unix.
2213
--- src/db.c
+++ src/db.c
@@ -2014,10 +2014,11 @@
2014 { "binary-glob", 0, 32, 1, "" },
2015 { "clearsign", 0, 0, 0, "off" },
2016 { "case-sensitive",0, 0, 0, "on" },
2017 { "crnl-glob", 0, 16, 1, "" },
2018 { "default-perms", 0, 16, 0, "u" },
2019 { "diff-binary", 0, 0, 0, "on" },
2020 { "diff-command", 0, 16, 0, "" },
2021 { "dont-push", 0, 0, 0, "off" },
2022 { "editor", 0, 16, 0, "" },
2023 { "gdiff-command", 0, 16, 0, "gdiff" },
2024 { "gmerge-command",0, 40, 0, "" },
@@ -2038,10 +2039,11 @@
2039 { "ssl-ca-location",0, 40, 0, "" },
2040 { "ssl-identity", 0, 40, 0, "" },
2041 { "ssh-command", 0, 32, 0, "" },
2042 #ifdef FOSSIL_ENABLE_TCL
2043 { "tcl", 0, 0, 0, "off" },
2044 { "tcl-setup", 0, 40, 0, "" },
2045 #endif
2046 { "web-browser", 0, 32, 0, "" },
2047 { "white-foreground", 0, 0, 0, "off" },
2048 { 0,0,0,0,0 }
2049 };
@@ -2106,10 +2108,14 @@
2108 ** Set to "*" to disable CR+NL checking.
2109 **
2110 ** default-perms Permissions given automatically to new users. For more
2111 ** information on permissions see Users page in Server
2112 ** Administration of the HTTP UI. Default: u.
2113 **
2114 ** diff-binary If TRUE (the default), permit files that may be binary
2115 ** or that match the "binary-glob" setting to be used with
2116 ** external diff programs. If FALSE, skip these files.
2117 **
2118 ** diff-command External command to run when performing a diff.
2119 ** If undefined, the internal text diff will be used.
2120 **
2121 ** dont-push Prevent this repository from pushing from client to
@@ -2198,15 +2204,20 @@
2204 ** password authentication.
2205 **
2206 ** ssh-command Command used to talk to a remote machine with
2207 ** the "ssh://" protocol.
2208 **
2209 ** tcl If enabled (and Fossil was compiled with Tcl support),
2210 ** Tcl integration commands will be added to the TH1
2211 ** interpreter, allowing arbitrary Tcl expressions and
2212 ** scripts to be evaluated from TH1. Additionally, the Tcl
2213 ** interpreter will be able to evaluate arbitrary TH1
2214 ** expressions and scripts. Default: off.
2215 **
2216 ** tcl-setup This is the setup script to be evaluated after creating
2217 ** and initializing the Tcl interpreter. By default, this
2218 ** is empty and no extra setup is performed.
2219 **
2220 ** web-browser A shell command used to launch your preferred
2221 ** web browser when given a URL as an argument.
2222 ** Defaults to "start" on windows, "open" on Mac,
2223 ** and "firefox" on Unix.
2224
+45 -7
--- src/diff.c
+++ src/diff.c
@@ -38,10 +38,20 @@
3838
#define DIFF_LINENO ((u64)0x20000000) /* Show line numbers */
3939
#define DIFF_WS_WARNING ((u64)0x40000000) /* Warn about whitespace */
4040
#define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */
4141
#define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */
4242
43
+/*
44
+** These error messages are shared in multiple locations. They are defined
45
+** here for consistency.
46
+*/
47
+#define DIFF_CANNOT_COMPUTE_BINARY \
48
+ "cannot compute difference between binary files\n"
49
+
50
+#define DIFF_CANNOT_COMPUTE_SYMLINK \
51
+ "cannot compute difference between symlink and regular file\n"
52
+
4353
#endif /* INTERFACE */
4454
4555
/*
4656
** Maximum length of a line in a text file. (8192)
4757
*/
@@ -157,10 +167,38 @@
157167
158168
/* Return results */
159169
*pnLine = nLine;
160170
return a;
161171
}
172
+
173
+/*
174
+** Returns non-zero if the specified content appears to be binary or
175
+** contains a line that is too long.
176
+*/
177
+int looks_like_binary(Blob *pContent){
178
+ const char *z = blob_str(pContent);
179
+ int n = blob_size(pContent);
180
+ int i, j;
181
+
182
+ /* Count the number of lines. Allocate space to hold
183
+ ** the returned array.
184
+ */
185
+ for(i=j=0; i<n; i++, j++){
186
+ int c = z[i];
187
+ if( c==0 ) return 1; /* \000 byte in a file -> binary */
188
+ if( c=='\n' && z[i+1]!=0 ){
189
+ if( j>LENGTH_MASK ){
190
+ return 1; /* Very long line -> binary */
191
+ }
192
+ j = 0;
193
+ }
194
+ }
195
+ if( j>LENGTH_MASK ){
196
+ return 1; /* Very long line -> binary */
197
+ }
198
+ return 0; /* No problems seen -> not binary */
199
+}
162200
163201
/*
164202
** Return true if two DLine elements are identical.
165203
*/
166204
static int same_dline(DLine *pA, DLine *pB){
@@ -1499,14 +1537,14 @@
14991537
c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
15001538
&c.nFrom, ignoreEolWs);
15011539
c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
15021540
&c.nTo, ignoreEolWs);
15031541
if( c.aFrom==0 || c.aTo==0 ){
1504
- free(c.aFrom);
1505
- free(c.aTo);
1542
+ fossil_free(c.aFrom);
1543
+ fossil_free(c.aTo);
15061544
if( pOut ){
1507
- blob_appendf(pOut, "cannot compute difference between binary files\n");
1545
+ blob_appendf(pOut, DIFF_CANNOT_COMPUTE_BINARY);
15081546
}
15091547
return 0;
15101548
}
15111549
15121550
/* Compute the difference */
@@ -1521,13 +1559,13 @@
15211559
sbsDiff(&c, pOut, nContext, width, escHtml);
15221560
}else{
15231561
int showLn = (diffFlags & DIFF_LINENO)!=0;
15241562
contextDiff(&c, pOut, nContext, showLn, escHtml);
15251563
}
1526
- free(c.aFrom);
1527
- free(c.aTo);
1528
- free(c.aEdit);
1564
+ fossil_free(c.aFrom);
1565
+ fossil_free(c.aTo);
1566
+ fossil_free(c.aEdit);
15291567
return 0;
15301568
}else{
15311569
/* If a context diff is not requested, then return the
15321570
** array of COPY/DELETE/INSERT triples.
15331571
*/
@@ -1702,11 +1740,11 @@
17021740
}
17031741
lnTo += p->c.aEdit[i+2];
17041742
}
17051743
17061744
/* Clear out the diff results */
1707
- free(p->c.aEdit);
1745
+ fossil_free(p->c.aEdit);
17081746
p->c.aEdit = 0;
17091747
p->c.nEdit = 0;
17101748
p->c.nEditAlloc = 0;
17111749
17121750
/* Clear out the from file */
17131751
--- src/diff.c
+++ src/diff.c
@@ -38,10 +38,20 @@
38 #define DIFF_LINENO ((u64)0x20000000) /* Show line numbers */
39 #define DIFF_WS_WARNING ((u64)0x40000000) /* Warn about whitespace */
40 #define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */
41 #define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */
42
 
 
 
 
 
 
 
 
 
 
43 #endif /* INTERFACE */
44
45 /*
46 ** Maximum length of a line in a text file. (8192)
47 */
@@ -157,10 +167,38 @@
157
158 /* Return results */
159 *pnLine = nLine;
160 return a;
161 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
163 /*
164 ** Return true if two DLine elements are identical.
165 */
166 static int same_dline(DLine *pA, DLine *pB){
@@ -1499,14 +1537,14 @@
1499 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
1500 &c.nFrom, ignoreEolWs);
1501 c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
1502 &c.nTo, ignoreEolWs);
1503 if( c.aFrom==0 || c.aTo==0 ){
1504 free(c.aFrom);
1505 free(c.aTo);
1506 if( pOut ){
1507 blob_appendf(pOut, "cannot compute difference between binary files\n");
1508 }
1509 return 0;
1510 }
1511
1512 /* Compute the difference */
@@ -1521,13 +1559,13 @@
1521 sbsDiff(&c, pOut, nContext, width, escHtml);
1522 }else{
1523 int showLn = (diffFlags & DIFF_LINENO)!=0;
1524 contextDiff(&c, pOut, nContext, showLn, escHtml);
1525 }
1526 free(c.aFrom);
1527 free(c.aTo);
1528 free(c.aEdit);
1529 return 0;
1530 }else{
1531 /* If a context diff is not requested, then return the
1532 ** array of COPY/DELETE/INSERT triples.
1533 */
@@ -1702,11 +1740,11 @@
1702 }
1703 lnTo += p->c.aEdit[i+2];
1704 }
1705
1706 /* Clear out the diff results */
1707 free(p->c.aEdit);
1708 p->c.aEdit = 0;
1709 p->c.nEdit = 0;
1710 p->c.nEditAlloc = 0;
1711
1712 /* Clear out the from file */
1713
--- src/diff.c
+++ src/diff.c
@@ -38,10 +38,20 @@
38 #define DIFF_LINENO ((u64)0x20000000) /* Show line numbers */
39 #define DIFF_WS_WARNING ((u64)0x40000000) /* Warn about whitespace */
40 #define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */
41 #define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */
42
43 /*
44 ** These error messages are shared in multiple locations. They are defined
45 ** here for consistency.
46 */
47 #define DIFF_CANNOT_COMPUTE_BINARY \
48 "cannot compute difference between binary files\n"
49
50 #define DIFF_CANNOT_COMPUTE_SYMLINK \
51 "cannot compute difference between symlink and regular file\n"
52
53 #endif /* INTERFACE */
54
55 /*
56 ** Maximum length of a line in a text file. (8192)
57 */
@@ -157,10 +167,38 @@
167
168 /* Return results */
169 *pnLine = nLine;
170 return a;
171 }
172
173 /*
174 ** Returns non-zero if the specified content appears to be binary or
175 ** contains a line that is too long.
176 */
177 int looks_like_binary(Blob *pContent){
178 const char *z = blob_str(pContent);
179 int n = blob_size(pContent);
180 int i, j;
181
182 /* Count the number of lines. Allocate space to hold
183 ** the returned array.
184 */
185 for(i=j=0; i<n; i++, j++){
186 int c = z[i];
187 if( c==0 ) return 1; /* \000 byte in a file -> binary */
188 if( c=='\n' && z[i+1]!=0 ){
189 if( j>LENGTH_MASK ){
190 return 1; /* Very long line -> binary */
191 }
192 j = 0;
193 }
194 }
195 if( j>LENGTH_MASK ){
196 return 1; /* Very long line -> binary */
197 }
198 return 0; /* No problems seen -> not binary */
199 }
200
201 /*
202 ** Return true if two DLine elements are identical.
203 */
204 static int same_dline(DLine *pA, DLine *pB){
@@ -1499,14 +1537,14 @@
1537 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
1538 &c.nFrom, ignoreEolWs);
1539 c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
1540 &c.nTo, ignoreEolWs);
1541 if( c.aFrom==0 || c.aTo==0 ){
1542 fossil_free(c.aFrom);
1543 fossil_free(c.aTo);
1544 if( pOut ){
1545 blob_appendf(pOut, DIFF_CANNOT_COMPUTE_BINARY);
1546 }
1547 return 0;
1548 }
1549
1550 /* Compute the difference */
@@ -1521,13 +1559,13 @@
1559 sbsDiff(&c, pOut, nContext, width, escHtml);
1560 }else{
1561 int showLn = (diffFlags & DIFF_LINENO)!=0;
1562 contextDiff(&c, pOut, nContext, showLn, escHtml);
1563 }
1564 fossil_free(c.aFrom);
1565 fossil_free(c.aTo);
1566 fossil_free(c.aEdit);
1567 return 0;
1568 }else{
1569 /* If a context diff is not requested, then return the
1570 ** array of COPY/DELETE/INSERT triples.
1571 */
@@ -1702,11 +1740,11 @@
1740 }
1741 lnTo += p->c.aEdit[i+2];
1742 }
1743
1744 /* Clear out the diff results */
1745 fossil_free(p->c.aEdit);
1746 p->c.aEdit = 0;
1747 p->c.nEdit = 0;
1748 p->c.nEditAlloc = 0;
1749
1750 /* Clear out the from file */
1751
+196 -28
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -69,16 +69,23 @@
6969
** The difference is the set of edits needed to transform pFile1 into
7070
** zFile2. The content of pFile1 is in memory. zFile2 exists on disk.
7171
**
7272
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
7373
** command zDiffCmd to do the diffing.
74
+**
75
+** When using an external diff program, zBinGlob contains the GLOB patterns
76
+** for file names to treat as binary. If fIncludeBinary is zero, these files
77
+** will be skipped in addition to files that may contain binary content.
7478
*/
7579
void diff_file(
7680
Blob *pFile1, /* In memory content to compare from */
81
+ int isBin1, /* Does the 'from' content appear to be binary */
7782
const char *zFile2, /* On disk content to compare to */
7883
const char *zName, /* Display name of the file */
7984
const char *zDiffCmd, /* Command for comparison */
85
+ const char *zBinGlob, /* Treat file names matching this as binary */
86
+ int fIncludeBinary, /* Include binary files for external diff */
8087
u64 diffFlags /* Flags to control the diff */
8188
){
8289
if( zDiffCmd==0 ){
8390
Blob out; /* Diff output text */
8491
Blob file2; /* Content of zFile2 */
@@ -116,10 +123,41 @@
116123
blob_reset(&file2);
117124
}else{
118125
int cnt = 0;
119126
Blob nameFile1; /* Name of temporary file to old pFile1 content */
120127
Blob cmd; /* Text of command to run */
128
+
129
+ if( !fIncludeBinary ){
130
+ Blob file2;
131
+ if( isBin1 ){
132
+ fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
133
+ return;
134
+ }
135
+ if( zBinGlob ){
136
+ Glob *pBinary = glob_create(zBinGlob);
137
+ if( glob_match(pBinary, zName) ){
138
+ fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
139
+ glob_free(pBinary);
140
+ return;
141
+ }
142
+ glob_free(pBinary);
143
+ }
144
+ blob_zero(&file2);
145
+ if( file_wd_size(zFile2)>=0 ){
146
+ if( file_wd_islink(zFile2) ){
147
+ blob_read_link(&file2, zFile2);
148
+ }else{
149
+ blob_read_from_file(&file2, zFile2);
150
+ }
151
+ }
152
+ if( looks_like_binary(&file2) ){
153
+ fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
154
+ blob_reset(&file2);
155
+ return;
156
+ }
157
+ blob_reset(&file2);
158
+ }
121159
122160
/* Construct a temporary file to hold pFile1 based on the name of
123161
** zFile2 */
124162
blob_zero(&nameFile1);
125163
do{
@@ -151,16 +189,24 @@
151189
** The difference is the set of edits needed to transform pFile1 into
152190
** pFile2.
153191
**
154192
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
155193
** command zDiffCmd to do the diffing.
194
+**
195
+** When using an external diff program, zBinGlob contains the GLOB patterns
196
+** for file names to treat as binary. If fIncludeBinary is zero, these files
197
+** will be skipped in addition to files that may contain binary content.
156198
*/
157199
void diff_file_mem(
158200
Blob *pFile1, /* In memory content to compare from */
159201
Blob *pFile2, /* In memory content to compare to */
202
+ int isBin1, /* Does the 'from' content appear to be binary */
203
+ int isBin2, /* Does the 'to' content appear to be binary */
160204
const char *zName, /* Display name of the file */
161205
const char *zDiffCmd, /* Command for comparison */
206
+ const char *zBinGlob, /* Treat file names matching this as binary */
207
+ int fIncludeBinary, /* Include binary files for external diff */
162208
u64 diffFlags /* Diff flags */
163209
){
164210
if( diffFlags & DIFF_BRIEF ) return;
165211
if( zDiffCmd==0 ){
166212
Blob out; /* Diff output text */
@@ -174,10 +220,26 @@
174220
blob_reset(&out);
175221
}else{
176222
Blob cmd;
177223
char zTemp1[300];
178224
char zTemp2[300];
225
+
226
+ if( !fIncludeBinary ){
227
+ if( isBin1 || isBin2 ){
228
+ fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
229
+ return;
230
+ }
231
+ if( zBinGlob ){
232
+ Glob *pBinary = glob_create(zBinGlob);
233
+ if( glob_match(pBinary, zName) ){
234
+ fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
235
+ glob_free(pBinary);
236
+ return;
237
+ }
238
+ glob_free(pBinary);
239
+ }
240
+ }
179241
180242
/* Construct a temporary file names */
181243
file_tempname(sizeof(zTemp1), zTemp1);
182244
file_tempname(sizeof(zTemp2), zTemp2);
183245
blob_write_to_file(pFile1, zTemp1);
@@ -201,40 +263,60 @@
201263
}
202264
203265
/*
204266
** Do a diff against a single file named in zFileTreeName from version zFrom
205267
** against the same file on disk.
268
+**
269
+** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
270
+** command zDiffCmd to do the diffing.
271
+**
272
+** When using an external diff program, zBinGlob contains the GLOB patterns
273
+** for file names to treat as binary. If fIncludeBinary is zero, these files
274
+** will be skipped in addition to files that may contain binary content.
206275
*/
207276
static void diff_one_against_disk(
208277
const char *zFrom, /* Name of file */
209278
const char *zDiffCmd, /* Use this "diff" command */
279
+ const char *zBinGlob, /* Treat file names matching this as binary */
280
+ int fIncludeBinary, /* Include binary files for external diff */
210281
u64 diffFlags, /* Diff control flags */
211282
const char *zFileTreeName
212283
){
213284
Blob fname;
214285
Blob content;
215286
int isLink;
287
+ int isBin;
216288
file_tree_name(zFileTreeName, &fname, 1);
217
- historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0, 0);
289
+ historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0,
290
+ fIncludeBinary ? 0 : &isBin, 0);
218291
if( !isLink != !file_wd_islink(zFrom) ){
219
- fossil_print("cannot compute difference between "
220
- "symlink and regular file\n");
292
+ fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK);
221293
}else{
222
- diff_file(&content, zFileTreeName, zFileTreeName, zDiffCmd, diffFlags);
294
+ diff_file(&content, isBin, zFileTreeName, zFileTreeName,
295
+ zDiffCmd, zBinGlob, fIncludeBinary, diffFlags);
223296
}
224297
blob_reset(&content);
225298
blob_reset(&fname);
226299
}
227300
228301
/*
229302
** Run a diff between the version zFrom and files on disk. zFrom might
230303
** be NULL which means to simply show the difference between the edited
231304
** files on disk and the check-out on which they are based.
305
+**
306
+** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
307
+** command zDiffCmd to do the diffing.
308
+**
309
+** When using an external diff program, zBinGlob contains the GLOB patterns
310
+** for file names to treat as binary. If fIncludeBinary is zero, these files
311
+** will be skipped in addition to files that may contain binary content.
232312
*/
233313
static void diff_all_against_disk(
234314
const char *zFrom, /* Version to difference from */
235315
const char *zDiffCmd, /* Use this diff command. NULL for built-in */
316
+ const char *zBinGlob, /* Treat file names matching this as binary */
317
+ int fIncludeBinary, /* Treat file names matching this as binary */
236318
u64 diffFlags /* Flags controlling diff output */
237319
){
238320
int vid;
239321
Blob sql;
240322
Stmt q;
@@ -307,24 +389,26 @@
307389
srcid = 0;
308390
if( !asNewFile ){ showDiff = 0; }
309391
}
310392
if( showDiff ){
311393
Blob content;
394
+ int isBin;
312395
if( !isLink != !file_wd_islink(zFullName) ){
313396
diff_print_index(zPathname, diffFlags);
314397
diff_print_filenames(zPathname, zPathname, diffFlags);
315
- fossil_print("cannot compute difference between "
316
- "symlink and regular file\n");
398
+ fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK);
317399
continue;
318400
}
319401
if( srcid>0 ){
320402
content_get(srcid, &content);
321403
}else{
322404
blob_zero(&content);
323405
}
406
+ isBin = fIncludeBinary ? 0 : looks_like_binary(&content);
324407
diff_print_index(zPathname, diffFlags);
325
- diff_file(&content, zFullName, zPathname, zDiffCmd, diffFlags);
408
+ diff_file(&content, isBin, zFullName, zPathname, zDiffCmd,
409
+ zBinGlob, fIncludeBinary, diffFlags);
326410
blob_reset(&content);
327411
}
328412
free(zToFree);
329413
}
330414
db_finalize(&q);
@@ -332,50 +416,72 @@
332416
}
333417
334418
/*
335419
** Output the differences between two versions of a single file.
336420
** zFrom and zTo are the check-ins containing the two file versions.
421
+**
422
+** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
423
+** command zDiffCmd to do the diffing.
424
+**
425
+** When using an external diff program, zBinGlob contains the GLOB patterns
426
+** for file names to treat as binary. If fIncludeBinary is zero, these files
427
+** will be skipped in addition to files that may contain binary content.
337428
*/
338429
static void diff_one_two_versions(
339430
const char *zFrom,
340431
const char *zTo,
341432
const char *zDiffCmd,
433
+ const char *zBinGlob,
434
+ int fIncludeBinary,
342435
u64 diffFlags,
343436
const char *zFileTreeName
344437
){
345438
char *zName;
346439
Blob fname;
347440
Blob v1, v2;
348441
int isLink1, isLink2;
442
+ int isBin1, isBin2;
349443
if( diffFlags & DIFF_BRIEF ) return;
350444
file_tree_name(zFileTreeName, &fname, 1);
351445
zName = blob_str(&fname);
352
- historical_version_of_file(zFrom, zName, &v1, &isLink1, 0, 0);
353
- historical_version_of_file(zTo, zName, &v2, &isLink2, 0, 0);
446
+ historical_version_of_file(zFrom, zName, &v1, &isLink1, 0,
447
+ fIncludeBinary ? 0 : &isBin1, 0);
448
+ historical_version_of_file(zTo, zName, &v2, &isLink2, 0,
449
+ fIncludeBinary ? 0 : &isBin2, 0);
354450
if( isLink1 != isLink2 ){
355451
diff_print_filenames(zName, zName, diffFlags);
356
- fossil_print("cannot compute difference "
357
- " between symlink and regular file\n");
452
+ fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK);
358453
}else{
359
- diff_file_mem(&v1, &v2, zName, zDiffCmd, diffFlags);
454
+ diff_file_mem(&v1, &v2, isBin1, isBin2, zName, zDiffCmd,
455
+ zBinGlob, fIncludeBinary, diffFlags);
360456
}
361457
blob_reset(&v1);
362458
blob_reset(&v2);
363459
blob_reset(&fname);
364460
}
365461
366462
/*
367463
** Show the difference between two files identified by ManifestFile
368464
** entries.
465
+**
466
+** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
467
+** command zDiffCmd to do the diffing.
468
+**
469
+** When using an external diff program, zBinGlob contains the GLOB patterns
470
+** for file names to treat as binary. If fIncludeBinary is zero, these files
471
+** will be skipped in addition to files that may contain binary content.
369472
*/
370473
static void diff_manifest_entry(
371474
struct ManifestFile *pFrom,
372475
struct ManifestFile *pTo,
373476
const char *zDiffCmd,
477
+ const char *zBinGlob,
478
+ int fIncludeBinary,
374479
u64 diffFlags
375480
){
376481
Blob f1, f2;
482
+ int isBin1, isBin2;
377483
int rid;
378484
const char *zName = pFrom ? pFrom->zName : pTo->zName;
379485
if( diffFlags & DIFF_BRIEF ) return;
380486
diff_print_index(zName, diffFlags);
381487
if( pFrom ){
@@ -388,22 +494,34 @@
388494
rid = uuid_to_rid(pTo->zUuid, 0);
389495
content_get(rid, &f2);
390496
}else{
391497
blob_zero(&f2);
392498
}
393
- diff_file_mem(&f1, &f2, zName, zDiffCmd, diffFlags);
499
+ isBin1 = fIncludeBinary ? 0 : looks_like_binary(&f1);
500
+ isBin2 = fIncludeBinary ? 0 : looks_like_binary(&f2);
501
+ diff_file_mem(&f1, &f2, isBin1, isBin2, zName, zDiffCmd,
502
+ zBinGlob, fIncludeBinary, diffFlags);
394503
blob_reset(&f1);
395504
blob_reset(&f2);
396505
}
397506
398507
/*
399508
** Output the differences between two check-ins.
509
+**
510
+** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
511
+** command zDiffCmd to do the diffing.
512
+**
513
+** When using an external diff program, zBinGlob contains the GLOB patterns
514
+** for file names to treat as binary. If fIncludeBinary is zero, these files
515
+** will be skipped in addition to files that may contain binary content.
400516
*/
401517
static void diff_all_two_versions(
402518
const char *zFrom,
403519
const char *zTo,
404520
const char *zDiffCmd,
521
+ const char *zBinGlob,
522
+ int fIncludeBinary,
405523
u64 diffFlags
406524
){
407525
Manifest *pFrom, *pTo;
408526
ManifestFile *pFromFile, *pToFile;
409527
int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0;
@@ -425,17 +543,19 @@
425543
cmp = fossil_strcmp(pFromFile->zName, pToFile->zName);
426544
}
427545
if( cmp<0 ){
428546
fossil_print("DELETED %s\n", pFromFile->zName);
429547
if( asNewFlag ){
430
- diff_manifest_entry(pFromFile, 0, zDiffCmd, diffFlags);
548
+ diff_manifest_entry(pFromFile, 0, zDiffCmd, zBinGlob,
549
+ fIncludeBinary, diffFlags);
431550
}
432551
pFromFile = manifest_file_next(pFrom,0);
433552
}else if( cmp>0 ){
434553
fossil_print("ADDED %s\n", pToFile->zName);
435554
if( asNewFlag ){
436
- diff_manifest_entry(0, pToFile, zDiffCmd, diffFlags);
555
+ diff_manifest_entry(0, pToFile, zDiffCmd, zBinGlob,
556
+ fIncludeBinary, diffFlags);
437557
}
438558
pToFile = manifest_file_next(pTo,0);
439559
}else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
440560
/* No changes */
441561
pFromFile = manifest_file_next(pFrom,0);
@@ -442,11 +562,12 @@
442562
pToFile = manifest_file_next(pTo,0);
443563
}else{
444564
if( diffFlags & DIFF_BRIEF ){
445565
fossil_print("CHANGED %s\n", pFromFile->zName);
446566
}else{
447
- diff_manifest_entry(pFromFile, pToFile, zDiffCmd, diffFlags);
567
+ diff_manifest_entry(pFromFile, pToFile, zDiffCmd, zBinGlob,
568
+ fIncludeBinary, diffFlags);
448569
}
449570
pFromFile = manifest_file_next(pFrom,0);
450571
pToFile = manifest_file_next(pTo,0);
451572
}
452573
}
@@ -493,10 +614,11 @@
493614
@ .t tag config add -background {#c0ffc0}
494615
@ .t tag config rm -background {#ffc0c0}
495616
@ proc dehtml {x} {
496617
@ return [string map {&amp; & &lt; < &gt; > &#39; ' &quot; \"} $x]
497618
@ }
619
+@ # puts $cmd
498620
@ set in [open $cmd r]
499621
@ while {![eof $in]} {
500622
@ set line [gets $in]
501623
@ if {[regexp {^<a name="chunk.*"></a>} $line]} continue
502624
@ if {[regexp {^===} $line]} {
@@ -528,25 +650,52 @@
528650
** Steps:
529651
** (1) Write the Tcl/Tk script used for rendering into a temp file.
530652
** (2) Invoke "wish" on the temp file using fossil_system().
531653
** (3) Delete the temp file.
532654
*/
533
-static void diff_tk(void){
655
+void diff_tk(const char *zSubCmd, int firstArg){
534656
int i;
535657
Blob script;
536658
char *zTempFile;
537659
char *zCmd;
538660
blob_zero(&script);
539
- blob_appendf(&script, "set cmd {| \"%/\" diff --html -y", g.nameOfExe);
540
- for(i=2; i<g.argc; i++){
541
- blob_appendf(&script, " \"%s\"", g.argv[i]);
661
+ blob_appendf(&script, "set cmd {| \"%/\" %s --html -y -i",
662
+ g.nameOfExe, zSubCmd);
663
+ for(i=firstArg; i<g.argc; i++){
664
+ blob_append(&script, " ", 1);
665
+ shell_escape(&script, g.argv[i]);
542666
}
543667
blob_appendf(&script, "}\n%s", zDiffScript);
544668
zTempFile = write_blob_to_temp_file(&script);
545669
zCmd = mprintf("tclsh \"%s\"", zTempFile);
546670
fossil_system(zCmd);
547671
file_delete(zTempFile);
672
+ fossil_free(zCmd);
673
+}
674
+
675
+/*
676
+** Returns non-zero if files that may be binary should be used with external
677
+** diff programs.
678
+*/
679
+int diff_include_binary_files(void){
680
+ if( is_truth(find_option("diff-binary", 0, 1)) ){
681
+ return 1;
682
+ }
683
+ if( db_get_boolean("diff-binary", 1) ){
684
+ return 1;
685
+ }
686
+ return 0;
687
+}
688
+
689
+/*
690
+** Returns the GLOB pattern for file names that should be treated as binary
691
+** by the diff subsystem, if any.
692
+*/
693
+const char *diff_get_binary_glob(void){
694
+ const char *zBinGlob = find_option("binary", 0, 1);
695
+ if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
696
+ return zBinGlob;
548697
}
549698
550699
/*
551700
** COMMAND: diff
552701
** COMMAND: gdiff
@@ -573,10 +722,17 @@
573722
** the "-i" option is a no-op. The "-i" option converts "gdiff" into "diff".
574723
**
575724
** The "-N" or "--new-file" option causes the complete text of added or
576725
** deleted files to be displayed.
577726
**
727
+** The "--diff-binary" option enables or disables the inclusion of binary files
728
+** when using an external diff program.
729
+**
730
+** The "--binary" option causes files matching the glob PATTERN to be treated
731
+** as binary when considering if they should be used with external diff program.
732
+** This option overrides the "binary-glob" setting.
733
+**
578734
** Options:
579735
** --branch BRANCH Show diff of all changes on BRANCH
580736
** --brief Show filenames only
581737
** --context|-c N Use N lines of context
582738
** --from|-r VERSION select VERSION as source for the diff
@@ -585,24 +741,28 @@
585741
** --tk Launch a Tcl/Tk GUI for display
586742
** --to VERSION select VERSION as target for the diff
587743
** --side-by-side|-y side-by-side diff
588744
** --unified unified diff
589745
** --width|-W N Width of lines in side-by-side diff
746
+** --diff-binary BOOL Include binary files when using external commands
747
+** --binary PATTERN Treat files that match the glob PATTERN as binary
590748
*/
591749
void diff_cmd(void){
592750
int isGDiff; /* True for gdiff. False for normal diff */
593751
int isInternDiff; /* True for internal diff */
594752
int hasNFlag; /* True if -N or --new-file flag is used */
595753
const char *zFrom; /* Source version number */
596754
const char *zTo; /* Target version number */
597755
const char *zBranch; /* Branch to diff */
598756
const char *zDiffCmd = 0; /* External diff command. NULL for internal diff */
757
+ const char *zBinGlob = 0; /* Treat file names matching this as binary */
758
+ int fIncludeBinary = 0; /* Include binary files for external diff */
599759
u64 diffFlags = 0; /* Flags to control the DIFF */
600760
int f;
601761
602762
if( find_option("tk",0,0)!=0 ){
603
- diff_tk();
763
+ diff_tk("diff", 2);
604764
return;
605765
}
606766
isGDiff = g.argv[1][0]=='g';
607767
isInternDiff = find_option("internal","i",0)!=0;
608768
zFrom = find_option("from", "r", 1);
@@ -619,35 +779,43 @@
619779
zTo = zBranch;
620780
zFrom = mprintf("root:%s", zBranch);
621781
}
622782
if( zTo==0 ){
623783
db_must_be_within_tree();
624
- verify_all_options();
625784
if( !isInternDiff ){
626785
zDiffCmd = diff_command_external(isGDiff);
627786
}
787
+ zBinGlob = diff_get_binary_glob();
788
+ fIncludeBinary = diff_include_binary_files();
789
+ verify_all_options();
628790
if( g.argc>=3 ){
629791
for(f=2; f<g.argc; ++f){
630
- diff_one_against_disk(zFrom, zDiffCmd, diffFlags, g.argv[f]);
792
+ diff_one_against_disk(zFrom, zDiffCmd, zBinGlob, fIncludeBinary,
793
+ diffFlags, g.argv[f]);
631794
}
632795
}else{
633
- diff_all_against_disk(zFrom, zDiffCmd, diffFlags);
796
+ diff_all_against_disk(zFrom, zDiffCmd, zBinGlob, fIncludeBinary,
797
+ diffFlags);
634798
}
635799
}else if( zFrom==0 ){
636800
fossil_fatal("must use --from if --to is present");
637801
}else{
638802
db_find_and_open_repository(0, 0);
639
- verify_all_options();
640803
if( !isInternDiff ){
641804
zDiffCmd = diff_command_external(isGDiff);
642805
}
806
+ zBinGlob = diff_get_binary_glob();
807
+ fIncludeBinary = diff_include_binary_files();
808
+ verify_all_options();
643809
if( g.argc>=3 ){
644810
for(f=2; f<g.argc; ++f){
645
- diff_one_two_versions(zFrom, zTo, zDiffCmd, diffFlags, g.argv[f]);
811
+ diff_one_two_versions(zFrom, zTo, zDiffCmd, zBinGlob, fIncludeBinary,
812
+ diffFlags, g.argv[f]);
646813
}
647814
}else{
648
- diff_all_two_versions(zFrom, zTo, zDiffCmd, diffFlags);
815
+ diff_all_two_versions(zFrom, zTo, zDiffCmd, zBinGlob, fIncludeBinary,
816
+ diffFlags);
649817
}
650818
}
651819
}
652820
653821
/*
@@ -660,7 +828,7 @@
660828
login_check_credentials();
661829
if( !g.perm.Read ){ login_needed(); return; }
662830
if( zFrom==0 || zTo==0 ) fossil_redirect_home();
663831
664832
cgi_set_content_type("text/plain");
665
- diff_all_two_versions(zFrom, zTo, 0, DIFF_NEWFILE);
833
+ diff_all_two_versions(zFrom, zTo, 0, 0, 0, DIFF_NEWFILE);
666834
}
667835
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -69,16 +69,23 @@
69 ** The difference is the set of edits needed to transform pFile1 into
70 ** zFile2. The content of pFile1 is in memory. zFile2 exists on disk.
71 **
72 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
73 ** command zDiffCmd to do the diffing.
 
 
 
 
74 */
75 void diff_file(
76 Blob *pFile1, /* In memory content to compare from */
 
77 const char *zFile2, /* On disk content to compare to */
78 const char *zName, /* Display name of the file */
79 const char *zDiffCmd, /* Command for comparison */
 
 
80 u64 diffFlags /* Flags to control the diff */
81 ){
82 if( zDiffCmd==0 ){
83 Blob out; /* Diff output text */
84 Blob file2; /* Content of zFile2 */
@@ -116,10 +123,41 @@
116 blob_reset(&file2);
117 }else{
118 int cnt = 0;
119 Blob nameFile1; /* Name of temporary file to old pFile1 content */
120 Blob cmd; /* Text of command to run */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
122 /* Construct a temporary file to hold pFile1 based on the name of
123 ** zFile2 */
124 blob_zero(&nameFile1);
125 do{
@@ -151,16 +189,24 @@
151 ** The difference is the set of edits needed to transform pFile1 into
152 ** pFile2.
153 **
154 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
155 ** command zDiffCmd to do the diffing.
 
 
 
 
156 */
157 void diff_file_mem(
158 Blob *pFile1, /* In memory content to compare from */
159 Blob *pFile2, /* In memory content to compare to */
 
 
160 const char *zName, /* Display name of the file */
161 const char *zDiffCmd, /* Command for comparison */
 
 
162 u64 diffFlags /* Diff flags */
163 ){
164 if( diffFlags & DIFF_BRIEF ) return;
165 if( zDiffCmd==0 ){
166 Blob out; /* Diff output text */
@@ -174,10 +220,26 @@
174 blob_reset(&out);
175 }else{
176 Blob cmd;
177 char zTemp1[300];
178 char zTemp2[300];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
180 /* Construct a temporary file names */
181 file_tempname(sizeof(zTemp1), zTemp1);
182 file_tempname(sizeof(zTemp2), zTemp2);
183 blob_write_to_file(pFile1, zTemp1);
@@ -201,40 +263,60 @@
201 }
202
203 /*
204 ** Do a diff against a single file named in zFileTreeName from version zFrom
205 ** against the same file on disk.
 
 
 
 
 
 
 
206 */
207 static void diff_one_against_disk(
208 const char *zFrom, /* Name of file */
209 const char *zDiffCmd, /* Use this "diff" command */
 
 
210 u64 diffFlags, /* Diff control flags */
211 const char *zFileTreeName
212 ){
213 Blob fname;
214 Blob content;
215 int isLink;
 
216 file_tree_name(zFileTreeName, &fname, 1);
217 historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0, 0);
 
218 if( !isLink != !file_wd_islink(zFrom) ){
219 fossil_print("cannot compute difference between "
220 "symlink and regular file\n");
221 }else{
222 diff_file(&content, zFileTreeName, zFileTreeName, zDiffCmd, diffFlags);
 
223 }
224 blob_reset(&content);
225 blob_reset(&fname);
226 }
227
228 /*
229 ** Run a diff between the version zFrom and files on disk. zFrom might
230 ** be NULL which means to simply show the difference between the edited
231 ** files on disk and the check-out on which they are based.
 
 
 
 
 
 
 
232 */
233 static void diff_all_against_disk(
234 const char *zFrom, /* Version to difference from */
235 const char *zDiffCmd, /* Use this diff command. NULL for built-in */
 
 
236 u64 diffFlags /* Flags controlling diff output */
237 ){
238 int vid;
239 Blob sql;
240 Stmt q;
@@ -307,24 +389,26 @@
307 srcid = 0;
308 if( !asNewFile ){ showDiff = 0; }
309 }
310 if( showDiff ){
311 Blob content;
 
312 if( !isLink != !file_wd_islink(zFullName) ){
313 diff_print_index(zPathname, diffFlags);
314 diff_print_filenames(zPathname, zPathname, diffFlags);
315 fossil_print("cannot compute difference between "
316 "symlink and regular file\n");
317 continue;
318 }
319 if( srcid>0 ){
320 content_get(srcid, &content);
321 }else{
322 blob_zero(&content);
323 }
 
324 diff_print_index(zPathname, diffFlags);
325 diff_file(&content, zFullName, zPathname, zDiffCmd, diffFlags);
 
326 blob_reset(&content);
327 }
328 free(zToFree);
329 }
330 db_finalize(&q);
@@ -332,50 +416,72 @@
332 }
333
334 /*
335 ** Output the differences between two versions of a single file.
336 ** zFrom and zTo are the check-ins containing the two file versions.
 
 
 
 
 
 
 
337 */
338 static void diff_one_two_versions(
339 const char *zFrom,
340 const char *zTo,
341 const char *zDiffCmd,
 
 
342 u64 diffFlags,
343 const char *zFileTreeName
344 ){
345 char *zName;
346 Blob fname;
347 Blob v1, v2;
348 int isLink1, isLink2;
 
349 if( diffFlags & DIFF_BRIEF ) return;
350 file_tree_name(zFileTreeName, &fname, 1);
351 zName = blob_str(&fname);
352 historical_version_of_file(zFrom, zName, &v1, &isLink1, 0, 0);
353 historical_version_of_file(zTo, zName, &v2, &isLink2, 0, 0);
 
 
354 if( isLink1 != isLink2 ){
355 diff_print_filenames(zName, zName, diffFlags);
356 fossil_print("cannot compute difference "
357 " between symlink and regular file\n");
358 }else{
359 diff_file_mem(&v1, &v2, zName, zDiffCmd, diffFlags);
 
360 }
361 blob_reset(&v1);
362 blob_reset(&v2);
363 blob_reset(&fname);
364 }
365
366 /*
367 ** Show the difference between two files identified by ManifestFile
368 ** entries.
 
 
 
 
 
 
 
369 */
370 static void diff_manifest_entry(
371 struct ManifestFile *pFrom,
372 struct ManifestFile *pTo,
373 const char *zDiffCmd,
 
 
374 u64 diffFlags
375 ){
376 Blob f1, f2;
 
377 int rid;
378 const char *zName = pFrom ? pFrom->zName : pTo->zName;
379 if( diffFlags & DIFF_BRIEF ) return;
380 diff_print_index(zName, diffFlags);
381 if( pFrom ){
@@ -388,22 +494,34 @@
388 rid = uuid_to_rid(pTo->zUuid, 0);
389 content_get(rid, &f2);
390 }else{
391 blob_zero(&f2);
392 }
393 diff_file_mem(&f1, &f2, zName, zDiffCmd, diffFlags);
 
 
 
394 blob_reset(&f1);
395 blob_reset(&f2);
396 }
397
398 /*
399 ** Output the differences between two check-ins.
 
 
 
 
 
 
 
400 */
401 static void diff_all_two_versions(
402 const char *zFrom,
403 const char *zTo,
404 const char *zDiffCmd,
 
 
405 u64 diffFlags
406 ){
407 Manifest *pFrom, *pTo;
408 ManifestFile *pFromFile, *pToFile;
409 int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0;
@@ -425,17 +543,19 @@
425 cmp = fossil_strcmp(pFromFile->zName, pToFile->zName);
426 }
427 if( cmp<0 ){
428 fossil_print("DELETED %s\n", pFromFile->zName);
429 if( asNewFlag ){
430 diff_manifest_entry(pFromFile, 0, zDiffCmd, diffFlags);
 
431 }
432 pFromFile = manifest_file_next(pFrom,0);
433 }else if( cmp>0 ){
434 fossil_print("ADDED %s\n", pToFile->zName);
435 if( asNewFlag ){
436 diff_manifest_entry(0, pToFile, zDiffCmd, diffFlags);
 
437 }
438 pToFile = manifest_file_next(pTo,0);
439 }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
440 /* No changes */
441 pFromFile = manifest_file_next(pFrom,0);
@@ -442,11 +562,12 @@
442 pToFile = manifest_file_next(pTo,0);
443 }else{
444 if( diffFlags & DIFF_BRIEF ){
445 fossil_print("CHANGED %s\n", pFromFile->zName);
446 }else{
447 diff_manifest_entry(pFromFile, pToFile, zDiffCmd, diffFlags);
 
448 }
449 pFromFile = manifest_file_next(pFrom,0);
450 pToFile = manifest_file_next(pTo,0);
451 }
452 }
@@ -493,10 +614,11 @@
493 @ .t tag config add -background {#c0ffc0}
494 @ .t tag config rm -background {#ffc0c0}
495 @ proc dehtml {x} {
496 @ return [string map {&amp; & &lt; < &gt; > &#39; ' &quot; \"} $x]
497 @ }
 
498 @ set in [open $cmd r]
499 @ while {![eof $in]} {
500 @ set line [gets $in]
501 @ if {[regexp {^<a name="chunk.*"></a>} $line]} continue
502 @ if {[regexp {^===} $line]} {
@@ -528,25 +650,52 @@
528 ** Steps:
529 ** (1) Write the Tcl/Tk script used for rendering into a temp file.
530 ** (2) Invoke "wish" on the temp file using fossil_system().
531 ** (3) Delete the temp file.
532 */
533 static void diff_tk(void){
534 int i;
535 Blob script;
536 char *zTempFile;
537 char *zCmd;
538 blob_zero(&script);
539 blob_appendf(&script, "set cmd {| \"%/\" diff --html -y", g.nameOfExe);
540 for(i=2; i<g.argc; i++){
541 blob_appendf(&script, " \"%s\"", g.argv[i]);
 
 
542 }
543 blob_appendf(&script, "}\n%s", zDiffScript);
544 zTempFile = write_blob_to_temp_file(&script);
545 zCmd = mprintf("tclsh \"%s\"", zTempFile);
546 fossil_system(zCmd);
547 file_delete(zTempFile);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
548 }
549
550 /*
551 ** COMMAND: diff
552 ** COMMAND: gdiff
@@ -573,10 +722,17 @@
573 ** the "-i" option is a no-op. The "-i" option converts "gdiff" into "diff".
574 **
575 ** The "-N" or "--new-file" option causes the complete text of added or
576 ** deleted files to be displayed.
577 **
 
 
 
 
 
 
 
578 ** Options:
579 ** --branch BRANCH Show diff of all changes on BRANCH
580 ** --brief Show filenames only
581 ** --context|-c N Use N lines of context
582 ** --from|-r VERSION select VERSION as source for the diff
@@ -585,24 +741,28 @@
585 ** --tk Launch a Tcl/Tk GUI for display
586 ** --to VERSION select VERSION as target for the diff
587 ** --side-by-side|-y side-by-side diff
588 ** --unified unified diff
589 ** --width|-W N Width of lines in side-by-side diff
 
 
590 */
591 void diff_cmd(void){
592 int isGDiff; /* True for gdiff. False for normal diff */
593 int isInternDiff; /* True for internal diff */
594 int hasNFlag; /* True if -N or --new-file flag is used */
595 const char *zFrom; /* Source version number */
596 const char *zTo; /* Target version number */
597 const char *zBranch; /* Branch to diff */
598 const char *zDiffCmd = 0; /* External diff command. NULL for internal diff */
 
 
599 u64 diffFlags = 0; /* Flags to control the DIFF */
600 int f;
601
602 if( find_option("tk",0,0)!=0 ){
603 diff_tk();
604 return;
605 }
606 isGDiff = g.argv[1][0]=='g';
607 isInternDiff = find_option("internal","i",0)!=0;
608 zFrom = find_option("from", "r", 1);
@@ -619,35 +779,43 @@
619 zTo = zBranch;
620 zFrom = mprintf("root:%s", zBranch);
621 }
622 if( zTo==0 ){
623 db_must_be_within_tree();
624 verify_all_options();
625 if( !isInternDiff ){
626 zDiffCmd = diff_command_external(isGDiff);
627 }
 
 
 
628 if( g.argc>=3 ){
629 for(f=2; f<g.argc; ++f){
630 diff_one_against_disk(zFrom, zDiffCmd, diffFlags, g.argv[f]);
 
631 }
632 }else{
633 diff_all_against_disk(zFrom, zDiffCmd, diffFlags);
 
634 }
635 }else if( zFrom==0 ){
636 fossil_fatal("must use --from if --to is present");
637 }else{
638 db_find_and_open_repository(0, 0);
639 verify_all_options();
640 if( !isInternDiff ){
641 zDiffCmd = diff_command_external(isGDiff);
642 }
 
 
 
643 if( g.argc>=3 ){
644 for(f=2; f<g.argc; ++f){
645 diff_one_two_versions(zFrom, zTo, zDiffCmd, diffFlags, g.argv[f]);
 
646 }
647 }else{
648 diff_all_two_versions(zFrom, zTo, zDiffCmd, diffFlags);
 
649 }
650 }
651 }
652
653 /*
@@ -660,7 +828,7 @@
660 login_check_credentials();
661 if( !g.perm.Read ){ login_needed(); return; }
662 if( zFrom==0 || zTo==0 ) fossil_redirect_home();
663
664 cgi_set_content_type("text/plain");
665 diff_all_two_versions(zFrom, zTo, 0, DIFF_NEWFILE);
666 }
667
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -69,16 +69,23 @@
69 ** The difference is the set of edits needed to transform pFile1 into
70 ** zFile2. The content of pFile1 is in memory. zFile2 exists on disk.
71 **
72 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
73 ** command zDiffCmd to do the diffing.
74 **
75 ** When using an external diff program, zBinGlob contains the GLOB patterns
76 ** for file names to treat as binary. If fIncludeBinary is zero, these files
77 ** will be skipped in addition to files that may contain binary content.
78 */
79 void diff_file(
80 Blob *pFile1, /* In memory content to compare from */
81 int isBin1, /* Does the 'from' content appear to be binary */
82 const char *zFile2, /* On disk content to compare to */
83 const char *zName, /* Display name of the file */
84 const char *zDiffCmd, /* Command for comparison */
85 const char *zBinGlob, /* Treat file names matching this as binary */
86 int fIncludeBinary, /* Include binary files for external diff */
87 u64 diffFlags /* Flags to control the diff */
88 ){
89 if( zDiffCmd==0 ){
90 Blob out; /* Diff output text */
91 Blob file2; /* Content of zFile2 */
@@ -116,10 +123,41 @@
123 blob_reset(&file2);
124 }else{
125 int cnt = 0;
126 Blob nameFile1; /* Name of temporary file to old pFile1 content */
127 Blob cmd; /* Text of command to run */
128
129 if( !fIncludeBinary ){
130 Blob file2;
131 if( isBin1 ){
132 fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
133 return;
134 }
135 if( zBinGlob ){
136 Glob *pBinary = glob_create(zBinGlob);
137 if( glob_match(pBinary, zName) ){
138 fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
139 glob_free(pBinary);
140 return;
141 }
142 glob_free(pBinary);
143 }
144 blob_zero(&file2);
145 if( file_wd_size(zFile2)>=0 ){
146 if( file_wd_islink(zFile2) ){
147 blob_read_link(&file2, zFile2);
148 }else{
149 blob_read_from_file(&file2, zFile2);
150 }
151 }
152 if( looks_like_binary(&file2) ){
153 fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
154 blob_reset(&file2);
155 return;
156 }
157 blob_reset(&file2);
158 }
159
160 /* Construct a temporary file to hold pFile1 based on the name of
161 ** zFile2 */
162 blob_zero(&nameFile1);
163 do{
@@ -151,16 +189,24 @@
189 ** The difference is the set of edits needed to transform pFile1 into
190 ** pFile2.
191 **
192 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
193 ** command zDiffCmd to do the diffing.
194 **
195 ** When using an external diff program, zBinGlob contains the GLOB patterns
196 ** for file names to treat as binary. If fIncludeBinary is zero, these files
197 ** will be skipped in addition to files that may contain binary content.
198 */
199 void diff_file_mem(
200 Blob *pFile1, /* In memory content to compare from */
201 Blob *pFile2, /* In memory content to compare to */
202 int isBin1, /* Does the 'from' content appear to be binary */
203 int isBin2, /* Does the 'to' content appear to be binary */
204 const char *zName, /* Display name of the file */
205 const char *zDiffCmd, /* Command for comparison */
206 const char *zBinGlob, /* Treat file names matching this as binary */
207 int fIncludeBinary, /* Include binary files for external diff */
208 u64 diffFlags /* Diff flags */
209 ){
210 if( diffFlags & DIFF_BRIEF ) return;
211 if( zDiffCmd==0 ){
212 Blob out; /* Diff output text */
@@ -174,10 +220,26 @@
220 blob_reset(&out);
221 }else{
222 Blob cmd;
223 char zTemp1[300];
224 char zTemp2[300];
225
226 if( !fIncludeBinary ){
227 if( isBin1 || isBin2 ){
228 fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
229 return;
230 }
231 if( zBinGlob ){
232 Glob *pBinary = glob_create(zBinGlob);
233 if( glob_match(pBinary, zName) ){
234 fossil_print(DIFF_CANNOT_COMPUTE_BINARY);
235 glob_free(pBinary);
236 return;
237 }
238 glob_free(pBinary);
239 }
240 }
241
242 /* Construct a temporary file names */
243 file_tempname(sizeof(zTemp1), zTemp1);
244 file_tempname(sizeof(zTemp2), zTemp2);
245 blob_write_to_file(pFile1, zTemp1);
@@ -201,40 +263,60 @@
263 }
264
265 /*
266 ** Do a diff against a single file named in zFileTreeName from version zFrom
267 ** against the same file on disk.
268 **
269 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
270 ** command zDiffCmd to do the diffing.
271 **
272 ** When using an external diff program, zBinGlob contains the GLOB patterns
273 ** for file names to treat as binary. If fIncludeBinary is zero, these files
274 ** will be skipped in addition to files that may contain binary content.
275 */
276 static void diff_one_against_disk(
277 const char *zFrom, /* Name of file */
278 const char *zDiffCmd, /* Use this "diff" command */
279 const char *zBinGlob, /* Treat file names matching this as binary */
280 int fIncludeBinary, /* Include binary files for external diff */
281 u64 diffFlags, /* Diff control flags */
282 const char *zFileTreeName
283 ){
284 Blob fname;
285 Blob content;
286 int isLink;
287 int isBin;
288 file_tree_name(zFileTreeName, &fname, 1);
289 historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0,
290 fIncludeBinary ? 0 : &isBin, 0);
291 if( !isLink != !file_wd_islink(zFrom) ){
292 fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK);
 
293 }else{
294 diff_file(&content, isBin, zFileTreeName, zFileTreeName,
295 zDiffCmd, zBinGlob, fIncludeBinary, diffFlags);
296 }
297 blob_reset(&content);
298 blob_reset(&fname);
299 }
300
301 /*
302 ** Run a diff between the version zFrom and files on disk. zFrom might
303 ** be NULL which means to simply show the difference between the edited
304 ** files on disk and the check-out on which they are based.
305 **
306 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
307 ** command zDiffCmd to do the diffing.
308 **
309 ** When using an external diff program, zBinGlob contains the GLOB patterns
310 ** for file names to treat as binary. If fIncludeBinary is zero, these files
311 ** will be skipped in addition to files that may contain binary content.
312 */
313 static void diff_all_against_disk(
314 const char *zFrom, /* Version to difference from */
315 const char *zDiffCmd, /* Use this diff command. NULL for built-in */
316 const char *zBinGlob, /* Treat file names matching this as binary */
317 int fIncludeBinary, /* Treat file names matching this as binary */
318 u64 diffFlags /* Flags controlling diff output */
319 ){
320 int vid;
321 Blob sql;
322 Stmt q;
@@ -307,24 +389,26 @@
389 srcid = 0;
390 if( !asNewFile ){ showDiff = 0; }
391 }
392 if( showDiff ){
393 Blob content;
394 int isBin;
395 if( !isLink != !file_wd_islink(zFullName) ){
396 diff_print_index(zPathname, diffFlags);
397 diff_print_filenames(zPathname, zPathname, diffFlags);
398 fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK);
 
399 continue;
400 }
401 if( srcid>0 ){
402 content_get(srcid, &content);
403 }else{
404 blob_zero(&content);
405 }
406 isBin = fIncludeBinary ? 0 : looks_like_binary(&content);
407 diff_print_index(zPathname, diffFlags);
408 diff_file(&content, isBin, zFullName, zPathname, zDiffCmd,
409 zBinGlob, fIncludeBinary, diffFlags);
410 blob_reset(&content);
411 }
412 free(zToFree);
413 }
414 db_finalize(&q);
@@ -332,50 +416,72 @@
416 }
417
418 /*
419 ** Output the differences between two versions of a single file.
420 ** zFrom and zTo are the check-ins containing the two file versions.
421 **
422 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
423 ** command zDiffCmd to do the diffing.
424 **
425 ** When using an external diff program, zBinGlob contains the GLOB patterns
426 ** for file names to treat as binary. If fIncludeBinary is zero, these files
427 ** will be skipped in addition to files that may contain binary content.
428 */
429 static void diff_one_two_versions(
430 const char *zFrom,
431 const char *zTo,
432 const char *zDiffCmd,
433 const char *zBinGlob,
434 int fIncludeBinary,
435 u64 diffFlags,
436 const char *zFileTreeName
437 ){
438 char *zName;
439 Blob fname;
440 Blob v1, v2;
441 int isLink1, isLink2;
442 int isBin1, isBin2;
443 if( diffFlags & DIFF_BRIEF ) return;
444 file_tree_name(zFileTreeName, &fname, 1);
445 zName = blob_str(&fname);
446 historical_version_of_file(zFrom, zName, &v1, &isLink1, 0,
447 fIncludeBinary ? 0 : &isBin1, 0);
448 historical_version_of_file(zTo, zName, &v2, &isLink2, 0,
449 fIncludeBinary ? 0 : &isBin2, 0);
450 if( isLink1 != isLink2 ){
451 diff_print_filenames(zName, zName, diffFlags);
452 fossil_print(DIFF_CANNOT_COMPUTE_SYMLINK);
 
453 }else{
454 diff_file_mem(&v1, &v2, isBin1, isBin2, zName, zDiffCmd,
455 zBinGlob, fIncludeBinary, diffFlags);
456 }
457 blob_reset(&v1);
458 blob_reset(&v2);
459 blob_reset(&fname);
460 }
461
462 /*
463 ** Show the difference between two files identified by ManifestFile
464 ** entries.
465 **
466 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
467 ** command zDiffCmd to do the diffing.
468 **
469 ** When using an external diff program, zBinGlob contains the GLOB patterns
470 ** for file names to treat as binary. If fIncludeBinary is zero, these files
471 ** will be skipped in addition to files that may contain binary content.
472 */
473 static void diff_manifest_entry(
474 struct ManifestFile *pFrom,
475 struct ManifestFile *pTo,
476 const char *zDiffCmd,
477 const char *zBinGlob,
478 int fIncludeBinary,
479 u64 diffFlags
480 ){
481 Blob f1, f2;
482 int isBin1, isBin2;
483 int rid;
484 const char *zName = pFrom ? pFrom->zName : pTo->zName;
485 if( diffFlags & DIFF_BRIEF ) return;
486 diff_print_index(zName, diffFlags);
487 if( pFrom ){
@@ -388,22 +494,34 @@
494 rid = uuid_to_rid(pTo->zUuid, 0);
495 content_get(rid, &f2);
496 }else{
497 blob_zero(&f2);
498 }
499 isBin1 = fIncludeBinary ? 0 : looks_like_binary(&f1);
500 isBin2 = fIncludeBinary ? 0 : looks_like_binary(&f2);
501 diff_file_mem(&f1, &f2, isBin1, isBin2, zName, zDiffCmd,
502 zBinGlob, fIncludeBinary, diffFlags);
503 blob_reset(&f1);
504 blob_reset(&f2);
505 }
506
507 /*
508 ** Output the differences between two check-ins.
509 **
510 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
511 ** command zDiffCmd to do the diffing.
512 **
513 ** When using an external diff program, zBinGlob contains the GLOB patterns
514 ** for file names to treat as binary. If fIncludeBinary is zero, these files
515 ** will be skipped in addition to files that may contain binary content.
516 */
517 static void diff_all_two_versions(
518 const char *zFrom,
519 const char *zTo,
520 const char *zDiffCmd,
521 const char *zBinGlob,
522 int fIncludeBinary,
523 u64 diffFlags
524 ){
525 Manifest *pFrom, *pTo;
526 ManifestFile *pFromFile, *pToFile;
527 int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0;
@@ -425,17 +543,19 @@
543 cmp = fossil_strcmp(pFromFile->zName, pToFile->zName);
544 }
545 if( cmp<0 ){
546 fossil_print("DELETED %s\n", pFromFile->zName);
547 if( asNewFlag ){
548 diff_manifest_entry(pFromFile, 0, zDiffCmd, zBinGlob,
549 fIncludeBinary, diffFlags);
550 }
551 pFromFile = manifest_file_next(pFrom,0);
552 }else if( cmp>0 ){
553 fossil_print("ADDED %s\n", pToFile->zName);
554 if( asNewFlag ){
555 diff_manifest_entry(0, pToFile, zDiffCmd, zBinGlob,
556 fIncludeBinary, diffFlags);
557 }
558 pToFile = manifest_file_next(pTo,0);
559 }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
560 /* No changes */
561 pFromFile = manifest_file_next(pFrom,0);
@@ -442,11 +562,12 @@
562 pToFile = manifest_file_next(pTo,0);
563 }else{
564 if( diffFlags & DIFF_BRIEF ){
565 fossil_print("CHANGED %s\n", pFromFile->zName);
566 }else{
567 diff_manifest_entry(pFromFile, pToFile, zDiffCmd, zBinGlob,
568 fIncludeBinary, diffFlags);
569 }
570 pFromFile = manifest_file_next(pFrom,0);
571 pToFile = manifest_file_next(pTo,0);
572 }
573 }
@@ -493,10 +614,11 @@
614 @ .t tag config add -background {#c0ffc0}
615 @ .t tag config rm -background {#ffc0c0}
616 @ proc dehtml {x} {
617 @ return [string map {&amp; & &lt; < &gt; > &#39; ' &quot; \"} $x]
618 @ }
619 @ # puts $cmd
620 @ set in [open $cmd r]
621 @ while {![eof $in]} {
622 @ set line [gets $in]
623 @ if {[regexp {^<a name="chunk.*"></a>} $line]} continue
624 @ if {[regexp {^===} $line]} {
@@ -528,25 +650,52 @@
650 ** Steps:
651 ** (1) Write the Tcl/Tk script used for rendering into a temp file.
652 ** (2) Invoke "wish" on the temp file using fossil_system().
653 ** (3) Delete the temp file.
654 */
655 void diff_tk(const char *zSubCmd, int firstArg){
656 int i;
657 Blob script;
658 char *zTempFile;
659 char *zCmd;
660 blob_zero(&script);
661 blob_appendf(&script, "set cmd {| \"%/\" %s --html -y -i",
662 g.nameOfExe, zSubCmd);
663 for(i=firstArg; i<g.argc; i++){
664 blob_append(&script, " ", 1);
665 shell_escape(&script, g.argv[i]);
666 }
667 blob_appendf(&script, "}\n%s", zDiffScript);
668 zTempFile = write_blob_to_temp_file(&script);
669 zCmd = mprintf("tclsh \"%s\"", zTempFile);
670 fossil_system(zCmd);
671 file_delete(zTempFile);
672 fossil_free(zCmd);
673 }
674
675 /*
676 ** Returns non-zero if files that may be binary should be used with external
677 ** diff programs.
678 */
679 int diff_include_binary_files(void){
680 if( is_truth(find_option("diff-binary", 0, 1)) ){
681 return 1;
682 }
683 if( db_get_boolean("diff-binary", 1) ){
684 return 1;
685 }
686 return 0;
687 }
688
689 /*
690 ** Returns the GLOB pattern for file names that should be treated as binary
691 ** by the diff subsystem, if any.
692 */
693 const char *diff_get_binary_glob(void){
694 const char *zBinGlob = find_option("binary", 0, 1);
695 if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
696 return zBinGlob;
697 }
698
699 /*
700 ** COMMAND: diff
701 ** COMMAND: gdiff
@@ -573,10 +722,17 @@
722 ** the "-i" option is a no-op. The "-i" option converts "gdiff" into "diff".
723 **
724 ** The "-N" or "--new-file" option causes the complete text of added or
725 ** deleted files to be displayed.
726 **
727 ** The "--diff-binary" option enables or disables the inclusion of binary files
728 ** when using an external diff program.
729 **
730 ** The "--binary" option causes files matching the glob PATTERN to be treated
731 ** as binary when considering if they should be used with external diff program.
732 ** This option overrides the "binary-glob" setting.
733 **
734 ** Options:
735 ** --branch BRANCH Show diff of all changes on BRANCH
736 ** --brief Show filenames only
737 ** --context|-c N Use N lines of context
738 ** --from|-r VERSION select VERSION as source for the diff
@@ -585,24 +741,28 @@
741 ** --tk Launch a Tcl/Tk GUI for display
742 ** --to VERSION select VERSION as target for the diff
743 ** --side-by-side|-y side-by-side diff
744 ** --unified unified diff
745 ** --width|-W N Width of lines in side-by-side diff
746 ** --diff-binary BOOL Include binary files when using external commands
747 ** --binary PATTERN Treat files that match the glob PATTERN as binary
748 */
749 void diff_cmd(void){
750 int isGDiff; /* True for gdiff. False for normal diff */
751 int isInternDiff; /* True for internal diff */
752 int hasNFlag; /* True if -N or --new-file flag is used */
753 const char *zFrom; /* Source version number */
754 const char *zTo; /* Target version number */
755 const char *zBranch; /* Branch to diff */
756 const char *zDiffCmd = 0; /* External diff command. NULL for internal diff */
757 const char *zBinGlob = 0; /* Treat file names matching this as binary */
758 int fIncludeBinary = 0; /* Include binary files for external diff */
759 u64 diffFlags = 0; /* Flags to control the DIFF */
760 int f;
761
762 if( find_option("tk",0,0)!=0 ){
763 diff_tk("diff", 2);
764 return;
765 }
766 isGDiff = g.argv[1][0]=='g';
767 isInternDiff = find_option("internal","i",0)!=0;
768 zFrom = find_option("from", "r", 1);
@@ -619,35 +779,43 @@
779 zTo = zBranch;
780 zFrom = mprintf("root:%s", zBranch);
781 }
782 if( zTo==0 ){
783 db_must_be_within_tree();
 
784 if( !isInternDiff ){
785 zDiffCmd = diff_command_external(isGDiff);
786 }
787 zBinGlob = diff_get_binary_glob();
788 fIncludeBinary = diff_include_binary_files();
789 verify_all_options();
790 if( g.argc>=3 ){
791 for(f=2; f<g.argc; ++f){
792 diff_one_against_disk(zFrom, zDiffCmd, zBinGlob, fIncludeBinary,
793 diffFlags, g.argv[f]);
794 }
795 }else{
796 diff_all_against_disk(zFrom, zDiffCmd, zBinGlob, fIncludeBinary,
797 diffFlags);
798 }
799 }else if( zFrom==0 ){
800 fossil_fatal("must use --from if --to is present");
801 }else{
802 db_find_and_open_repository(0, 0);
 
803 if( !isInternDiff ){
804 zDiffCmd = diff_command_external(isGDiff);
805 }
806 zBinGlob = diff_get_binary_glob();
807 fIncludeBinary = diff_include_binary_files();
808 verify_all_options();
809 if( g.argc>=3 ){
810 for(f=2; f<g.argc; ++f){
811 diff_one_two_versions(zFrom, zTo, zDiffCmd, zBinGlob, fIncludeBinary,
812 diffFlags, g.argv[f]);
813 }
814 }else{
815 diff_all_two_versions(zFrom, zTo, zDiffCmd, zBinGlob, fIncludeBinary,
816 diffFlags);
817 }
818 }
819 }
820
821 /*
@@ -660,7 +828,7 @@
828 login_check_credentials();
829 if( !g.perm.Read ){ login_needed(); return; }
830 if( zFrom==0 || zTo==0 ) fossil_redirect_home();
831
832 cgi_set_content_type("text/plain");
833 diff_all_two_versions(zFrom, zTo, 0, 0, 0, DIFF_NEWFILE);
834 }
835
+1 -1
--- src/finfo.c
+++ src/finfo.c
@@ -115,11 +115,11 @@
115115
Blob fname;
116116
const char *zRevision = find_option("revision", "r", 1);
117117
118118
file_tree_name(g.argv[2], &fname, 1);
119119
if( zRevision ){
120
- historical_version_of_file(zRevision, blob_str(&fname), &record, 0, 0, 0);
120
+ historical_version_of_file(zRevision, blob_str(&fname), &record, 0,0,0,0);
121121
}else{
122122
int rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B %s",
123123
&fname, filename_collation());
124124
if( rid==0 ){
125125
fossil_fatal("no history for file: %b", &fname);
126126
--- src/finfo.c
+++ src/finfo.c
@@ -115,11 +115,11 @@
115 Blob fname;
116 const char *zRevision = find_option("revision", "r", 1);
117
118 file_tree_name(g.argv[2], &fname, 1);
119 if( zRevision ){
120 historical_version_of_file(zRevision, blob_str(&fname), &record, 0, 0, 0);
121 }else{
122 int rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B %s",
123 &fname, filename_collation());
124 if( rid==0 ){
125 fossil_fatal("no history for file: %b", &fname);
126
--- src/finfo.c
+++ src/finfo.c
@@ -115,11 +115,11 @@
115 Blob fname;
116 const char *zRevision = find_option("revision", "r", 1);
117
118 file_tree_name(g.argv[2], &fname, 1);
119 if( zRevision ){
120 historical_version_of_file(zRevision, blob_str(&fname), &record, 0,0,0,0);
121 }else{
122 int rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B %s",
123 &fname, filename_collation());
124 if( rid==0 ){
125 fossil_fatal("no history for file: %b", &fname);
126
+27 -5
--- src/main.c
+++ src/main.c
@@ -88,13 +88,21 @@
8888
** All Tcl related context information is in this structure. This structure
8989
** definition has been copied from and should be kept in sync with the one in
9090
** "th_tcl.c".
9191
*/
9292
struct TclContext {
93
- int argc;
94
- char **argv;
95
- Tcl_Interp *interp;
93
+ int argc; /* Number of original (expanded) arguments. */
94
+ char **argv; /* Full copy of the original (expanded) arguments. */
95
+ void *library; /* The Tcl library module handle. */
96
+ void *xFindExecutable; /* See tcl_FindExecutableProc in th_tcl.c. */
97
+ void *xCreateInterp; /* See tcl_CreateInterpProc in th_tcl.c. */
98
+ Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */
99
+ char *setup; /* The optional Tcl setup script. */
100
+ void *xPreEval; /* Optional, called before Tcl_Eval*(). */
101
+ void *pPreContext; /* Optional, provided to xPreEval(). */
102
+ void *xPostEval; /* Optional, called after Tcl_Eval*(). */
103
+ void *pPostContext; /* Optional, provided to xPostEval(). */
96104
};
97105
#endif
98106
99107
/*
100108
** All global variables are in this structure.
@@ -546,10 +554,24 @@
546554
while( i<g.argc ) newArgv[j++] = g.argv[i++];
547555
newArgv[j] = 0;
548556
g.argc = j;
549557
g.argv = newArgv;
550558
}
559
+
560
+/*
561
+** Make a deep copy of the provided argument array and return it.
562
+*/
563
+static char **copy_args(int argc, char **argv){
564
+ char **zNewArgv;
565
+ int i;
566
+ zNewArgv = fossil_malloc( sizeof(char*)*(argc+1) );
567
+ memset(zNewArgv, 0, sizeof(char*)*(argc+1));
568
+ for(i=0; i<argc; i++){
569
+ zNewArgv[i] = fossil_strdup(argv[i]);
570
+ }
571
+ return zNewArgv;
572
+}
551573
552574
/*
553575
** This procedure runs first.
554576
*/
555577
int main(int argc, char **argv)
@@ -574,13 +596,13 @@
574596
g.json.outOpt.addNewline = 1;
575597
g.json.outOpt.indentation = 1 /* in CGI/server mode this can be configured */;
576598
#endif /* FOSSIL_ENABLE_JSON */
577599
expand_args_option(argc, argv);
578600
#ifdef FOSSIL_ENABLE_TCL
601
+ memset(&g.tcl, 0, sizeof(TclContext));
579602
g.tcl.argc = g.argc;
580
- g.tcl.argv = g.argv;
581
- g.tcl.interp = 0;
603
+ g.tcl.argv = copy_args(g.argc, g.argv); /* save full arguments */
582604
#endif
583605
if( fossil_getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){
584606
zCmdName = "cgi";
585607
g.isHTTP = 1;
586608
}else if( g.argc<2 ){
587609
--- src/main.c
+++ src/main.c
@@ -88,13 +88,21 @@
88 ** All Tcl related context information is in this structure. This structure
89 ** definition has been copied from and should be kept in sync with the one in
90 ** "th_tcl.c".
91 */
92 struct TclContext {
93 int argc;
94 char **argv;
95 Tcl_Interp *interp;
 
 
 
 
 
 
 
 
96 };
97 #endif
98
99 /*
100 ** All global variables are in this structure.
@@ -546,10 +554,24 @@
546 while( i<g.argc ) newArgv[j++] = g.argv[i++];
547 newArgv[j] = 0;
548 g.argc = j;
549 g.argv = newArgv;
550 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
551
552 /*
553 ** This procedure runs first.
554 */
555 int main(int argc, char **argv)
@@ -574,13 +596,13 @@
574 g.json.outOpt.addNewline = 1;
575 g.json.outOpt.indentation = 1 /* in CGI/server mode this can be configured */;
576 #endif /* FOSSIL_ENABLE_JSON */
577 expand_args_option(argc, argv);
578 #ifdef FOSSIL_ENABLE_TCL
 
579 g.tcl.argc = g.argc;
580 g.tcl.argv = g.argv;
581 g.tcl.interp = 0;
582 #endif
583 if( fossil_getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){
584 zCmdName = "cgi";
585 g.isHTTP = 1;
586 }else if( g.argc<2 ){
587
--- src/main.c
+++ src/main.c
@@ -88,13 +88,21 @@
88 ** All Tcl related context information is in this structure. This structure
89 ** definition has been copied from and should be kept in sync with the one in
90 ** "th_tcl.c".
91 */
92 struct TclContext {
93 int argc; /* Number of original (expanded) arguments. */
94 char **argv; /* Full copy of the original (expanded) arguments. */
95 void *library; /* The Tcl library module handle. */
96 void *xFindExecutable; /* See tcl_FindExecutableProc in th_tcl.c. */
97 void *xCreateInterp; /* See tcl_CreateInterpProc in th_tcl.c. */
98 Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */
99 char *setup; /* The optional Tcl setup script. */
100 void *xPreEval; /* Optional, called before Tcl_Eval*(). */
101 void *pPreContext; /* Optional, provided to xPreEval(). */
102 void *xPostEval; /* Optional, called after Tcl_Eval*(). */
103 void *pPostContext; /* Optional, provided to xPostEval(). */
104 };
105 #endif
106
107 /*
108 ** All global variables are in this structure.
@@ -546,10 +554,24 @@
554 while( i<g.argc ) newArgv[j++] = g.argv[i++];
555 newArgv[j] = 0;
556 g.argc = j;
557 g.argv = newArgv;
558 }
559
560 /*
561 ** Make a deep copy of the provided argument array and return it.
562 */
563 static char **copy_args(int argc, char **argv){
564 char **zNewArgv;
565 int i;
566 zNewArgv = fossil_malloc( sizeof(char*)*(argc+1) );
567 memset(zNewArgv, 0, sizeof(char*)*(argc+1));
568 for(i=0; i<argc; i++){
569 zNewArgv[i] = fossil_strdup(argv[i]);
570 }
571 return zNewArgv;
572 }
573
574 /*
575 ** This procedure runs first.
576 */
577 int main(int argc, char **argv)
@@ -574,13 +596,13 @@
596 g.json.outOpt.addNewline = 1;
597 g.json.outOpt.indentation = 1 /* in CGI/server mode this can be configured */;
598 #endif /* FOSSIL_ENABLE_JSON */
599 expand_args_option(argc, argv);
600 #ifdef FOSSIL_ENABLE_TCL
601 memset(&g.tcl, 0, sizeof(TclContext));
602 g.tcl.argc = g.argc;
603 g.tcl.argv = copy_args(g.argc, g.argv); /* save full arguments */
 
604 #endif
605 if( fossil_getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){
606 zCmdName = "cgi";
607 g.isHTTP = 1;
608 }else if( g.argc<2 ){
609
+22 -2
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -375,10 +375,14 @@
375375
# FOSSIL_ENABLE_SSL = 1
376376
377377
#### Enable scripting support via Tcl/Tk
378378
#
379379
# FOSSIL_ENABLE_TCL = 1
380
+
381
+#### Load Tcl using the stubs mechanism
382
+#
383
+# FOSSIL_ENABLE_TCL_STUBS = 1
380384
381385
#### Use the Tcl source directory instead of the install directory?
382386
# This is useful when Tcl has been compiled statically with MinGW.
383387
#
384388
FOSSIL_TCL_SOURCE = 1
@@ -424,11 +428,15 @@
424428
TCLINCDIR = $(TCLDIR)/include
425429
TCLLIBDIR = $(TCLDIR)/lib
426430
427431
#### Tcl: Which Tcl library do we want to use (8.4, 8.5, 8.6, etc)?
428432
#
433
+ifdef FOSSIL_ENABLE_TCL_STUBS
434
+LIBTCL = -ltclstub86
435
+else
429436
LIBTCL = -ltcl86
437
+endif
430438
431439
#### C Compile and options for use in building executables that
432440
# will run on the target platform. This is usually the same
433441
# as BCC, unless you are cross-compiling. This C compiler builds
434442
# the finished binary for fossil. The BCC compiler above is used
@@ -462,14 +470,22 @@
462470
ifdef FOSSIL_ENABLE_SSL
463471
TCC += -DFOSSIL_ENABLE_SSL=1
464472
RCC += -DFOSSIL_ENABLE_SSL=1
465473
endif
466474
467
-# With Tcl support (statically linked)
475
+# With Tcl support
468476
ifdef FOSSIL_ENABLE_TCL
469
-TCC += -DFOSSIL_ENABLE_TCL=1 -DSTATIC_BUILD
477
+TCC += -DFOSSIL_ENABLE_TCL=1
470478
RCC += -DFOSSIL_ENABLE_TCL=1
479
+# Either statically linked or via stubs
480
+ifdef FOSSIL_ENABLE_TCL_STUBS
481
+TCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS
482
+RCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS
483
+else
484
+TCC += -DSTATIC_BUILD
485
+RCC += -DSTATIC_BUILD
486
+endif
471487
endif
472488
473489
# With JSON support
474490
ifdef FOSSIL_ENABLE_JSON
475491
TCC += -DFOSSIL_ENABLE_JSON=1
@@ -499,11 +515,15 @@
499515
500516
#### These libraries MUST appear in the same order as they do for Tcl
501517
# or linking with it will not work (exact reason unknown).
502518
#
503519
ifdef FOSSIL_ENABLE_TCL
520
+ifdef FOSSIL_ENABLE_TCL_STUBS
521
+LIB += -lkernel32 -lws2_32
522
+else
504523
LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32
524
+endif
505525
else
506526
LIB += -lkernel32 -lws2_32
507527
endif
508528
509529
#### Tcl shell for use in running the fossil test suite. This is only
510530
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -375,10 +375,14 @@
375 # FOSSIL_ENABLE_SSL = 1
376
377 #### Enable scripting support via Tcl/Tk
378 #
379 # FOSSIL_ENABLE_TCL = 1
 
 
 
 
380
381 #### Use the Tcl source directory instead of the install directory?
382 # This is useful when Tcl has been compiled statically with MinGW.
383 #
384 FOSSIL_TCL_SOURCE = 1
@@ -424,11 +428,15 @@
424 TCLINCDIR = $(TCLDIR)/include
425 TCLLIBDIR = $(TCLDIR)/lib
426
427 #### Tcl: Which Tcl library do we want to use (8.4, 8.5, 8.6, etc)?
428 #
 
 
 
429 LIBTCL = -ltcl86
 
430
431 #### C Compile and options for use in building executables that
432 # will run on the target platform. This is usually the same
433 # as BCC, unless you are cross-compiling. This C compiler builds
434 # the finished binary for fossil. The BCC compiler above is used
@@ -462,14 +470,22 @@
462 ifdef FOSSIL_ENABLE_SSL
463 TCC += -DFOSSIL_ENABLE_SSL=1
464 RCC += -DFOSSIL_ENABLE_SSL=1
465 endif
466
467 # With Tcl support (statically linked)
468 ifdef FOSSIL_ENABLE_TCL
469 TCC += -DFOSSIL_ENABLE_TCL=1 -DSTATIC_BUILD
470 RCC += -DFOSSIL_ENABLE_TCL=1
 
 
 
 
 
 
 
 
471 endif
472
473 # With JSON support
474 ifdef FOSSIL_ENABLE_JSON
475 TCC += -DFOSSIL_ENABLE_JSON=1
@@ -499,11 +515,15 @@
499
500 #### These libraries MUST appear in the same order as they do for Tcl
501 # or linking with it will not work (exact reason unknown).
502 #
503 ifdef FOSSIL_ENABLE_TCL
 
 
 
504 LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32
 
505 else
506 LIB += -lkernel32 -lws2_32
507 endif
508
509 #### Tcl shell for use in running the fossil test suite. This is only
510
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -375,10 +375,14 @@
375 # FOSSIL_ENABLE_SSL = 1
376
377 #### Enable scripting support via Tcl/Tk
378 #
379 # FOSSIL_ENABLE_TCL = 1
380
381 #### Load Tcl using the stubs mechanism
382 #
383 # FOSSIL_ENABLE_TCL_STUBS = 1
384
385 #### Use the Tcl source directory instead of the install directory?
386 # This is useful when Tcl has been compiled statically with MinGW.
387 #
388 FOSSIL_TCL_SOURCE = 1
@@ -424,11 +428,15 @@
428 TCLINCDIR = $(TCLDIR)/include
429 TCLLIBDIR = $(TCLDIR)/lib
430
431 #### Tcl: Which Tcl library do we want to use (8.4, 8.5, 8.6, etc)?
432 #
433 ifdef FOSSIL_ENABLE_TCL_STUBS
434 LIBTCL = -ltclstub86
435 else
436 LIBTCL = -ltcl86
437 endif
438
439 #### C Compile and options for use in building executables that
440 # will run on the target platform. This is usually the same
441 # as BCC, unless you are cross-compiling. This C compiler builds
442 # the finished binary for fossil. The BCC compiler above is used
@@ -462,14 +470,22 @@
470 ifdef FOSSIL_ENABLE_SSL
471 TCC += -DFOSSIL_ENABLE_SSL=1
472 RCC += -DFOSSIL_ENABLE_SSL=1
473 endif
474
475 # With Tcl support
476 ifdef FOSSIL_ENABLE_TCL
477 TCC += -DFOSSIL_ENABLE_TCL=1
478 RCC += -DFOSSIL_ENABLE_TCL=1
479 # Either statically linked or via stubs
480 ifdef FOSSIL_ENABLE_TCL_STUBS
481 TCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS
482 RCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS
483 else
484 TCC += -DSTATIC_BUILD
485 RCC += -DSTATIC_BUILD
486 endif
487 endif
488
489 # With JSON support
490 ifdef FOSSIL_ENABLE_JSON
491 TCC += -DFOSSIL_ENABLE_JSON=1
@@ -499,11 +515,15 @@
515
516 #### These libraries MUST appear in the same order as they do for Tcl
517 # or linking with it will not work (exact reason unknown).
518 #
519 ifdef FOSSIL_ENABLE_TCL
520 ifdef FOSSIL_ENABLE_TCL_STUBS
521 LIB += -lkernel32 -lws2_32
522 else
523 LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32
524 endif
525 else
526 LIB += -lkernel32 -lws2_32
527 endif
528
529 #### Tcl shell for use in running the fossil test suite. This is only
530
+2 -2
--- src/manifest.c
+++ src/manifest.c
@@ -1879,12 +1879,12 @@
18791879
for(i=0; i<p->nTag; i++){
18801880
zUuid = p->aTag[i].zUuid;
18811881
if( i==0 || fossil_strcmp(zUuid, p->aTag[i-1].zUuid)!=0 ){
18821882
if( i>0 ) blob_append(&comment, " ", 1);
18831883
blob_appendf(&comment,
1884
- "Edit &#91;[/info/%S | %S]&#93;:",
1885
- zUuid, zUuid);
1884
+ "Edit [%S]:",
1885
+ zUuid);
18861886
branchMove = 0;
18871887
}
18881888
zName = p->aTag[i].zName;
18891889
zValue = p->aTag[i].zValue;
18901890
if( strcmp(zName, "*branch")==0 ){
18911891
--- src/manifest.c
+++ src/manifest.c
@@ -1879,12 +1879,12 @@
1879 for(i=0; i<p->nTag; i++){
1880 zUuid = p->aTag[i].zUuid;
1881 if( i==0 || fossil_strcmp(zUuid, p->aTag[i-1].zUuid)!=0 ){
1882 if( i>0 ) blob_append(&comment, " ", 1);
1883 blob_appendf(&comment,
1884 "Edit &#91;[/info/%S | %S]&#93;:",
1885 zUuid, zUuid);
1886 branchMove = 0;
1887 }
1888 zName = p->aTag[i].zName;
1889 zValue = p->aTag[i].zValue;
1890 if( strcmp(zName, "*branch")==0 ){
1891
--- src/manifest.c
+++ src/manifest.c
@@ -1879,12 +1879,12 @@
1879 for(i=0; i<p->nTag; i++){
1880 zUuid = p->aTag[i].zUuid;
1881 if( i==0 || fossil_strcmp(zUuid, p->aTag[i-1].zUuid)!=0 ){
1882 if( i>0 ) blob_append(&comment, " ", 1);
1883 blob_appendf(&comment,
1884 "Edit [%S]:",
1885 zUuid);
1886 branchMove = 0;
1887 }
1888 zName = p->aTag[i].zName;
1889 zValue = p->aTag[i].zValue;
1890 if( strcmp(zName, "*branch")==0 ){
1891
+1
--- src/path.c
+++ src/path.c
@@ -26,10 +26,11 @@
2626
*/
2727
struct PathNode {
2828
int rid; /* ID for this node */
2929
u8 fromIsParent; /* True if pFrom is the parent of rid */
3030
u8 isPrim; /* True if primary side of common ancestor */
31
+ u8 isHidden; /* Abbreviate output in "fossil bisect ls" */
3132
PathNode *pFrom; /* Node we came from */
3233
union {
3334
PathNode *pPeer; /* List of nodes of the same generation */
3435
PathNode *pTo; /* Next on path from beginning to end */
3536
} u;
3637
--- src/path.c
+++ src/path.c
@@ -26,10 +26,11 @@
26 */
27 struct PathNode {
28 int rid; /* ID for this node */
29 u8 fromIsParent; /* True if pFrom is the parent of rid */
30 u8 isPrim; /* True if primary side of common ancestor */
 
31 PathNode *pFrom; /* Node we came from */
32 union {
33 PathNode *pPeer; /* List of nodes of the same generation */
34 PathNode *pTo; /* Next on path from beginning to end */
35 } u;
36
--- src/path.c
+++ src/path.c
@@ -26,10 +26,11 @@
26 */
27 struct PathNode {
28 int rid; /* ID for this node */
29 u8 fromIsParent; /* True if pFrom is the parent of rid */
30 u8 isPrim; /* True if primary side of common ancestor */
31 u8 isHidden; /* Abbreviate output in "fossil bisect ls" */
32 PathNode *pFrom; /* Node we came from */
33 union {
34 PathNode *pPeer; /* List of nodes of the same generation */
35 PathNode *pTo; /* Next on path from beginning to end */
36 } u;
37
+1 -1
--- src/setup.c
+++ src/setup.c
@@ -952,11 +952,11 @@
952952
@ </p>
953953
@
954954
@ <hr />
955955
entry_attribute("Login expiration time", 6, "cookie-expire", "cex", "8766");
956956
@ <p>The number of hours for which a login is valid. This must be a
957
- @ positive number. The default is 8760 hours which is approximately equal
957
+ @ positive number. The default is 8766 hours which is approximately equal
958958
@ to a year.</p>
959959
960960
@ <hr />
961961
entry_attribute("Download packet limit", 10, "max-download", "mxdwn",
962962
"5000000");
963963
--- src/setup.c
+++ src/setup.c
@@ -952,11 +952,11 @@
952 @ </p>
953 @
954 @ <hr />
955 entry_attribute("Login expiration time", 6, "cookie-expire", "cex", "8766");
956 @ <p>The number of hours for which a login is valid. This must be a
957 @ positive number. The default is 8760 hours which is approximately equal
958 @ to a year.</p>
959
960 @ <hr />
961 entry_attribute("Download packet limit", 10, "max-download", "mxdwn",
962 "5000000");
963
--- src/setup.c
+++ src/setup.c
@@ -952,11 +952,11 @@
952 @ </p>
953 @
954 @ <hr />
955 entry_attribute("Login expiration time", 6, "cookie-expire", "cex", "8766");
956 @ <p>The number of hours for which a login is valid. This must be a
957 @ positive number. The default is 8766 hours which is approximately equal
958 @ to a year.</p>
959
960 @ <hr />
961 entry_attribute("Download packet limit", 10, "max-download", "mxdwn",
962 "5000000");
963
+409 -302
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -673,11 +673,11 @@
673673
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
674674
** [sqlite_version()] and [sqlite_source_id()].
675675
*/
676676
#define SQLITE_VERSION "3.7.15"
677677
#define SQLITE_VERSION_NUMBER 3007015
678
-#define SQLITE_SOURCE_ID "2012-09-28 00:44:28 1e874629d7cf568368b912b295bd3001147d0b52"
678
+#define SQLITE_SOURCE_ID "2012-10-05 07:36:34 43155b1543bddbb84a8bc13a5b7344b228ddacb9"
679679
680680
/*
681681
** CAPI3REF: Run-Time Library Version Numbers
682682
** KEYWORDS: sqlite3_version, sqlite3_sourceid
683683
**
@@ -1421,10 +1421,21 @@
14211421
** that the VFS encountered an error while handling the [PRAGMA] and the
14221422
** compilation of the PRAGMA fails with an error. ^The [SQLITE_FCNTL_PRAGMA]
14231423
** file control occurs at the beginning of pragma statement analysis and so
14241424
** it is able to override built-in [PRAGMA] statements.
14251425
** </ul>
1426
+**
1427
+** <li>[[SQLITE_FCNTL_BUSYHANDLER]]
1428
+** ^This file-control may be invoked by SQLite on the database file handle
1429
+** shortly after it is opened in order to provide a custom VFS with access
1430
+** to the connections busy-handler callback. The argument is of type (void **)
1431
+** - an array of two (void *) values. The first (void *) actually points
1432
+** to a function of type (int (*)(void *)). In order to invoke the connections
1433
+** busy-handler, this function should be invoked with the second (void *) in
1434
+** the array as the only argument. If it returns non-zero, then the operation
1435
+** should be retried. If it returns zero, the custom VFS should abandon the
1436
+** current operation.
14261437
*/
14271438
#define SQLITE_FCNTL_LOCKSTATE 1
14281439
#define SQLITE_GET_LOCKPROXYFILE 2
14291440
#define SQLITE_SET_LOCKPROXYFILE 3
14301441
#define SQLITE_LAST_ERRNO 4
@@ -1436,10 +1447,11 @@
14361447
#define SQLITE_FCNTL_PERSIST_WAL 10
14371448
#define SQLITE_FCNTL_OVERWRITE 11
14381449
#define SQLITE_FCNTL_VFSNAME 12
14391450
#define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13
14401451
#define SQLITE_FCNTL_PRAGMA 14
1452
+#define SQLITE_FCNTL_BUSYHANDLER 15
14411453
14421454
/*
14431455
** CAPI3REF: Mutex Handle
14441456
**
14451457
** The mutex module within SQLite defines [sqlite3_mutex] to be an
@@ -8381,10 +8393,13 @@
83818393
SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*);
83828394
SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int);
83838395
SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree*);
83848396
SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int);
83858397
SQLITE_PRIVATE int sqlite3BtreeGetReserve(Btree*);
8398
+#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_DEBUG)
8399
+SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p);
8400
+#endif
83868401
SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int);
83878402
SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *);
83888403
SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int);
83898404
SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
83908405
SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree*, int);
@@ -9150,10 +9165,11 @@
91509165
SQLITE_PRIVATE int sqlite3PagerNosync(Pager*);
91519166
SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*);
91529167
SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*);
91539168
SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *);
91549169
SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *);
9170
+SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *);
91559171
91569172
/* Functions used to truncate the database file. */
91579173
SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno);
91589174
91599175
#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL)
@@ -25825,10 +25841,12 @@
2582525841
int prior = 0;
2582625842
#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
2582725843
i64 newOffset;
2582825844
#endif
2582925845
TIMER_START;
25846
+ assert( cnt==(cnt&0x1ffff) );
25847
+ cnt &= 0x1ffff;
2583025848
do{
2583125849
#if defined(USE_PREAD)
2583225850
got = osPread(id->h, pBuf, cnt, offset);
2583325851
SimulateIOError( got = -1 );
2583425852
#elif defined(USE_PREAD64)
@@ -25914,10 +25932,12 @@
2591425932
static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){
2591525933
int got;
2591625934
#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
2591725935
i64 newOffset;
2591825936
#endif
25937
+ assert( cnt==(cnt&0x1ffff) );
25938
+ cnt &= 0x1ffff;
2591925939
TIMER_START;
2592025940
#if defined(USE_PREAD)
2592125941
do{ got = osPwrite(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR );
2592225942
#elif defined(USE_PREAD64)
2592325943
do{ got = osPwrite64(id->h, pBuf, cnt, offset);}while( got<0 && errno==EINTR);
@@ -39558,10 +39578,25 @@
3955839578
}
3955939579
}
3956039580
}
3956139581
return rc;
3956239582
}
39583
+
39584
+/*
39585
+** Return a sanitized version of the sector-size of OS file pFile. The
39586
+** return value is guaranteed to lie between 32 and MAX_SECTOR_SIZE.
39587
+*/
39588
+SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *pFile){
39589
+ int iRet = sqlite3OsSectorSize(pFile);
39590
+ if( iRet<32 ){
39591
+ iRet = 512;
39592
+ }else if( iRet>MAX_SECTOR_SIZE ){
39593
+ assert( MAX_SECTOR_SIZE>=512 );
39594
+ iRet = MAX_SECTOR_SIZE;
39595
+ }
39596
+ return iRet;
39597
+}
3956339598
3956439599
/*
3956539600
** Set the value of the Pager.sectorSize variable for the given
3956639601
** pager based on the value returned by the xSectorSize method
3956739602
** of the open database file. The sector size will be used used
@@ -39594,18 +39629,11 @@
3959439629
/* Sector size doesn't matter for temporary files. Also, the file
3959539630
** may not have been opened yet, in which case the OsSectorSize()
3959639631
** call will segfault. */
3959739632
pPager->sectorSize = 512;
3959839633
}else{
39599
- pPager->sectorSize = sqlite3OsSectorSize(pPager->fd);
39600
- if( pPager->sectorSize<32 ){
39601
- pPager->sectorSize = 512;
39602
- }
39603
- if( pPager->sectorSize>MAX_SECTOR_SIZE ){
39604
- assert( MAX_SECTOR_SIZE>=512 );
39605
- pPager->sectorSize = MAX_SECTOR_SIZE;
39606
- }
39634
+ pPager->sectorSize = sqlite3SectorSize(pPager->fd);
3960739635
}
3960839636
}
3960939637
3961039638
/*
3961139639
** Playback the journal and thus restore the database file to
@@ -40518,13 +40546,20 @@
4051840546
*/
4051940547
SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(
4052040548
Pager *pPager, /* Pager object */
4052140549
int (*xBusyHandler)(void *), /* Pointer to busy-handler function */
4052240550
void *pBusyHandlerArg /* Argument to pass to xBusyHandler */
40523
-){
40551
+){
4052440552
pPager->xBusyHandler = xBusyHandler;
4052540553
pPager->pBusyHandlerArg = pBusyHandlerArg;
40554
+
40555
+ if( isOpen(pPager->fd) ){
40556
+ void **ap = (void **)&pPager->xBusyHandler;
40557
+ assert( ((int(*)(void *))(ap[0]))==xBusyHandler );
40558
+ assert( ap[1]==pBusyHandlerArg );
40559
+ sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_BUSYHANDLER, (void *)ap);
40560
+ }
4052640561
}
4052740562
4052840563
/*
4052940564
** Change the page size used by the Pager object. The new page size
4053040565
** is passed in *pPageSize.
@@ -46815,11 +46850,11 @@
4681546850
** sector boundary is synced; the part of the last frame that extends
4681646851
** past the sector boundary is written after the sync.
4681746852
*/
4681846853
if( isCommit && (sync_flags & WAL_SYNC_TRANSACTIONS)!=0 ){
4681946854
if( pWal->padToSectorBoundary ){
46820
- int sectorSize = sqlite3OsSectorSize(pWal->pWalFd);
46855
+ int sectorSize = sqlite3SectorSize(pWal->pWalFd);
4682146856
w.iSyncPoint = ((iOffset+sectorSize-1)/sectorSize)*sectorSize;
4682246857
while( iOffset<w.iSyncPoint ){
4682346858
rc = walWriteOneFrame(&w, pLast, nTruncate, iOffset);
4682446859
if( rc ) return rc;
4682546860
iOffset += szFrame;
@@ -50227,10 +50262,28 @@
5022750262
** Return the currently defined page size
5022850263
*/
5022950264
SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree *p){
5023050265
return p->pBt->pageSize;
5023150266
}
50267
+
50268
+#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_DEBUG)
50269
+/*
50270
+** This function is similar to sqlite3BtreeGetReserve(), except that it
50271
+** may only be called if it is guaranteed that the b-tree mutex is already
50272
+** held.
50273
+**
50274
+** This is useful in one special case in the backup API code where it is
50275
+** known that the shared b-tree mutex is held, but the mutex on the
50276
+** database handle that owns *p is not. In this case if sqlite3BtreeEnter()
50277
+** were to be called, it might collide with some other operation on the
50278
+** database handle that owns *p, causing undefined behaviour.
50279
+*/
50280
+SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p){
50281
+ assert( sqlite3_mutex_held(p->pBt->mutex) );
50282
+ return p->pBt->pageSize - p->pBt->usableSize;
50283
+}
50284
+#endif /* SQLITE_HAS_CODEC || SQLITE_DEBUG */
5023250285
5023350286
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM)
5023450287
/*
5023550288
** Return the number of bytes of space at the end of every page that
5023650289
** are intentually left unused. This is the "reserved" space that is
@@ -53284,11 +53337,11 @@
5328453337
btreeParseCellPtr(pPage, pCell, &info);
5328553338
if( info.iOverflow==0 ){
5328653339
return SQLITE_OK; /* No overflow pages. Return without doing anything */
5328753340
}
5328853341
if( pCell+info.iOverflow+3 > pPage->aData+pPage->maskPage ){
53289
- return SQLITE_CORRUPT; /* Cell extends past end of page */
53342
+ return SQLITE_CORRUPT_BKPT; /* Cell extends past end of page */
5329053343
}
5329153344
ovflPgno = get4byte(&pCell[info.iOverflow]);
5329253345
assert( pBt->usableSize > 4 );
5329353346
ovflPageSize = pBt->usableSize - 4;
5329453347
nOvfl = (info.nPayload - info.nLocal + ovflPageSize - 1)/ovflPageSize;
@@ -53950,10 +54003,13 @@
5395054003
** enough for all overflow cells.
5395154004
**
5395254005
** If aOvflSpace is set to a null pointer, this function returns
5395354006
** SQLITE_NOMEM.
5395454007
*/
54008
+#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_M_ARM)
54009
+#pragma optimize("", off)
54010
+#endif
5395554011
static int balance_nonroot(
5395654012
MemPage *pParent, /* Parent page of siblings being balanced */
5395754013
int iParentIdx, /* Index of "the page" in pParent */
5395854014
u8 *aOvflSpace, /* page-size bytes of space for parent ovfl */
5395954015
int isRoot, /* True if pParent is a root-page */
@@ -54580,10 +54636,13 @@
5458054636
releasePage(apNew[i]);
5458154637
}
5458254638
5458354639
return rc;
5458454640
}
54641
+#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_M_ARM)
54642
+#pragma optimize("", on)
54643
+#endif
5458554644
5458654645
5458754646
/*
5458854647
** This function is called when the root page of a b-tree structure is
5458954648
** overfull (has one or more overflow pages).
@@ -56558,17 +56617,20 @@
5655856617
const int nSrcPgsz = sqlite3BtreeGetPageSize(p->pSrc);
5655956618
int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest);
5656056619
const int nCopy = MIN(nSrcPgsz, nDestPgsz);
5656156620
const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz;
5656256621
#ifdef SQLITE_HAS_CODEC
56563
- int nSrcReserve = sqlite3BtreeGetReserve(p->pSrc);
56622
+ /* Use BtreeGetReserveNoMutex() for the source b-tree, as although it is
56623
+ ** guaranteed that the shared-mutex is held by this thread, handle
56624
+ ** p->pSrc may not actually be the owner. */
56625
+ int nSrcReserve = sqlite3BtreeGetReserveNoMutex(p->pSrc);
5656456626
int nDestReserve = sqlite3BtreeGetReserve(p->pDest);
5656556627
#endif
56566
-
5656756628
int rc = SQLITE_OK;
5656856629
i64 iOff;
5656956630
56631
+ assert( sqlite3BtreeGetReserveNoMutex(p->pSrc)>=0 );
5657056632
assert( p->bDestLocked );
5657156633
assert( !isFatalError(p->rc) );
5657256634
assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) );
5657356635
assert( zSrcData );
5657456636
@@ -91600,10 +91662,11 @@
9160091662
*/
9160191663
aFcntl[0] = 0;
9160291664
aFcntl[1] = zLeft;
9160391665
aFcntl[2] = zRight;
9160491666
aFcntl[3] = 0;
91667
+ db->busyHandler.nBusy = 0;
9160591668
rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_PRAGMA, (void*)aFcntl);
9160691669
if( rc==SQLITE_OK ){
9160791670
if( aFcntl[0] ){
9160891671
int mem = ++pParse->nMem;
9160991672
sqlite3VdbeAddOp4(v, OP_String8, 0, mem, 0, aFcntl[0], 0);
@@ -102123,14 +102186,15 @@
102123102186
#define WHERE_NOT_FULLSCAN 0x100f3000 /* Does not do a full table scan */
102124102187
#define WHERE_IN_ABLE 0x000f1000 /* Able to support an IN operator */
102125102188
#define WHERE_TOP_LIMIT 0x00100000 /* x<EXPR or x<=EXPR constraint */
102126102189
#define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */
102127102190
#define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and x<EXPR */
102128
-#define WHERE_IDX_ONLY 0x00800000 /* Use index only - omit table */
102129
-#define WHERE_ORDERBY 0x01000000 /* Output will appear in correct order */
102130
-#define WHERE_REVERSE 0x02000000 /* Scan in reverse order */
102131
-#define WHERE_UNIQUE 0x04000000 /* Selects no more than one row */
102191
+#define WHERE_IDX_ONLY 0x00400000 /* Use index only - omit table */
102192
+#define WHERE_ORDERED 0x00800000 /* Output will appear in correct order */
102193
+#define WHERE_REVERSE 0x01000000 /* Scan in reverse order */
102194
+#define WHERE_UNIQUE 0x02000000 /* Selects no more than one row */
102195
+#define WHERE_ALL_UNIQUE 0x04000000 /* This and all prior have one row */
102132102196
#define WHERE_VIRTUALTABLE 0x08000000 /* Use virtual-table processing */
102133102197
#define WHERE_MULTI_OR 0x10000000 /* OR using multiple indices */
102134102198
#define WHERE_TEMP_INDEX 0x20000000 /* Uses an ephemeral index */
102135102199
#define WHERE_DISTINCT 0x40000000 /* Correct order for DISTINCT */
102136102200
#define WHERE_COVER_SCAN 0x80000000 /* Full scan of a covering index */
@@ -102154,10 +102218,21 @@
102154102218
sqlite3_index_info **ppIdxInfo; /* Index information passed to xBestIndex */
102155102219
int i, n; /* Which loop is being coded; # of loops */
102156102220
WhereLevel *aLevel; /* Info about outer loops */
102157102221
WhereCost cost; /* Lowest cost query plan */
102158102222
};
102223
+
102224
+/*
102225
+** Return TRUE if the probe cost is less than the baseline cost
102226
+*/
102227
+static int compareCost(const WhereCost *pProbe, const WhereCost *pBaseline){
102228
+ if( pProbe->rCost<pBaseline->rCost ) return 1;
102229
+ if( pProbe->rCost>pBaseline->rCost ) return 0;
102230
+ if( pProbe->plan.nOBSat>pBaseline->plan.nOBSat ) return 1;
102231
+ if( pProbe->plan.nRow<pBaseline->plan.nRow ) return 1;
102232
+ return 0;
102233
+}
102159102234
102160102235
/*
102161102236
** Initialize a preallocated WhereClause structure.
102162102237
*/
102163102238
static void whereClauseInit(
@@ -103306,11 +103381,12 @@
103306103381
Table *pTab = pIdx->pTable;
103307103382
int i;
103308103383
if( pIdx->onError==OE_None ) return 0;
103309103384
for(i=nSkip; i<pIdx->nColumn; i++){
103310103385
int j = pIdx->aiColumn[i];
103311
- if( j>=0 && pTab->aCol[j].notNull==0 ) return 0;
103386
+ assert( j>=0 && j<pTab->nCol );
103387
+ if( pTab->aCol[j].notNull==0 ) return 0;
103312103388
}
103313103389
return 1;
103314103390
}
103315103391
103316103392
/*
@@ -103370,11 +103446,12 @@
103370103446
int nEqCol /* Number of index columns with == */
103371103447
){
103372103448
Bitmask mask = 0; /* Mask of unaccounted for pDistinct exprs */
103373103449
int i; /* Iterator variable */
103374103450
103375
- if( pIdx->zName==0 || pDistinct==0 || pDistinct->nExpr>=BMS ) return 0;
103451
+ assert( pDistinct!=0 );
103452
+ if( pIdx->zName==0 || pDistinct->nExpr>=BMS ) return 0;
103376103453
testcase( pDistinct->nExpr==BMS-1 );
103377103454
103378103455
/* Loop through all the expressions in the distinct list. If any of them
103379103456
** are not simple column references, return early. Otherwise, test if the
103380103457
** WHERE clause contains a "col=X" clause. If it does, the expression
@@ -103472,170 +103549,10 @@
103472103549
}
103473103550
103474103551
return 0;
103475103552
}
103476103553
103477
-/*
103478
-** This routine decides if pIdx can be used to satisfy the ORDER BY
103479
-** clause, either in whole or in part. The return value is the
103480
-** cumulative number of terms in the ORDER BY clause that are satisfied
103481
-** by the index pIdx and other indices in outer loops.
103482
-**
103483
-** The table being queried has a cursor number of "base". pIdx is the
103484
-** index that is postulated for use to access the table.
103485
-**
103486
-** nEqCol is the number of columns of pIdx that are used as equality
103487
-** constraints and where the other side of the == is an ordered column
103488
-** or constant. An "order column" in the previous sentence means a column
103489
-** in table from an outer loop whose values will always appear in the
103490
-** correct order due to othre index, or because the outer loop generates
103491
-** a unique result. Any of the first nEqCol columns of pIdx may be missing
103492
-** from the ORDER BY clause and the match can still be a success.
103493
-**
103494
-** The *pbRev value is set to 0 order 1 depending on whether or not
103495
-** pIdx should be run in the forward order or in reverse order.
103496
-*/
103497
-static int isSortingIndex(
103498
- WhereBestIdx *p, /* Best index search context */
103499
- Index *pIdx, /* The index we are testing */
103500
- int base, /* Cursor number for the table to be sorted */
103501
- int nEqCol, /* Number of index columns with ordered == constraints */
103502
- int wsFlags, /* Index usages flags */
103503
- int bOuterRev, /* True if outer loops scan in reverse order */
103504
- int *pbRev /* Set to 1 for reverse-order scan of pIdx */
103505
-){
103506
- int i; /* Number of pIdx terms used */
103507
- int j; /* Number of ORDER BY terms satisfied */
103508
- int sortOrder = 0; /* XOR of index and ORDER BY sort direction */
103509
- int nTerm; /* Number of ORDER BY terms */
103510
- struct ExprList_item *pTerm; /* A term of the ORDER BY clause */
103511
- ExprList *pOrderBy; /* The ORDER BY clause */
103512
- Parse *pParse = p->pParse; /* Parser context */
103513
- sqlite3 *db = pParse->db; /* Database connection */
103514
- int nPriorSat; /* ORDER BY terms satisfied by outer loops */
103515
- int seenRowid = 0; /* True if an ORDER BY rowid term is seen */
103516
- int nEqOneRow; /* Idx columns that ref unique values */
103517
-
103518
- if( p->i==0 ){
103519
- nPriorSat = 0;
103520
- nEqOneRow = nEqCol;
103521
- }else{
103522
- if( OptimizationDisabled(db, SQLITE_OrderByIdxJoin) ) return 0;
103523
- nPriorSat = p->aLevel[p->i-1].plan.nOBSat;
103524
- sortOrder = bOuterRev;
103525
- nEqOneRow = 0;
103526
- }
103527
- if( p->i>0 && nEqCol==0 /*&& !allOuterLoopsUnique(p)*/ ) return nPriorSat;
103528
- pOrderBy = p->pOrderBy;
103529
- if( !pOrderBy ) return nPriorSat;
103530
- if( wsFlags & WHERE_COLUMN_IN ) return nPriorSat;
103531
- if( pIdx->bUnordered ) return nPriorSat;
103532
- nTerm = pOrderBy->nExpr;
103533
- assert( nTerm>0 );
103534
-
103535
- /* Argument pIdx must either point to a 'real' named index structure,
103536
- ** or an index structure allocated on the stack by bestBtreeIndex() to
103537
- ** represent the rowid index that is part of every table. */
103538
- assert( pIdx->zName || (pIdx->nColumn==1 && pIdx->aiColumn[0]==-1) );
103539
-
103540
- /* Match terms of the ORDER BY clause against columns of
103541
- ** the index.
103542
- **
103543
- ** Note that indices have pIdx->nColumn regular columns plus
103544
- ** one additional column containing the rowid. The rowid column
103545
- ** of the index is also allowed to match against the ORDER BY
103546
- ** clause.
103547
- */
103548
- for(i=0,j=nPriorSat,pTerm=&pOrderBy->a[j]; j<nTerm && i<=pIdx->nColumn; i++){
103549
- Expr *pExpr; /* The expression of the ORDER BY pTerm */
103550
- CollSeq *pColl; /* The collating sequence of pExpr */
103551
- int termSortOrder; /* Sort order for this term */
103552
- int iColumn; /* The i-th column of the index. -1 for rowid */
103553
- int iSortOrder; /* 1 for DESC, 0 for ASC on the i-th index term */
103554
- const char *zColl; /* Name of the collating sequence for i-th index term */
103555
-
103556
- pExpr = pTerm->pExpr;
103557
- if( pExpr->op!=TK_COLUMN || pExpr->iTable!=base ){
103558
- /* Can not use an index sort on anything that is not a column in the
103559
- ** left-most table of the FROM clause */
103560
- break;
103561
- }
103562
- pColl = sqlite3ExprCollSeq(pParse, pExpr);
103563
- if( !pColl ){
103564
- pColl = db->pDfltColl;
103565
- }
103566
- if( pIdx->zName && i<pIdx->nColumn ){
103567
- iColumn = pIdx->aiColumn[i];
103568
- if( iColumn==pIdx->pTable->iPKey ){
103569
- iColumn = -1;
103570
- }
103571
- iSortOrder = pIdx->aSortOrder[i];
103572
- zColl = pIdx->azColl[i];
103573
- }else{
103574
- iColumn = -1;
103575
- iSortOrder = 0;
103576
- zColl = pColl->zName;
103577
- }
103578
- if( pExpr->iColumn!=iColumn || sqlite3StrICmp(pColl->zName, zColl) ){
103579
- /* Term j of the ORDER BY clause does not match column i of the index */
103580
- if( i<nEqCol ){
103581
- /* If an index column that is constrained by == fails to match an
103582
- ** ORDER BY term, that is OK. Just ignore that column of the index
103583
- */
103584
- continue;
103585
- }else if( i==pIdx->nColumn ){
103586
- /* Index column i is the rowid. All other terms match. */
103587
- break;
103588
- }else{
103589
- /* If an index column fails to match and is not constrained by ==
103590
- ** then the index cannot satisfy the ORDER BY constraint.
103591
- */
103592
- return nPriorSat;
103593
- }
103594
- }
103595
- assert( pIdx->aSortOrder!=0 || iColumn==-1 );
103596
- assert( pTerm->sortOrder==0 || pTerm->sortOrder==1 );
103597
- assert( iSortOrder==0 || iSortOrder==1 );
103598
- termSortOrder = iSortOrder ^ pTerm->sortOrder;
103599
- if( i>nEqOneRow ){
103600
- if( termSortOrder!=sortOrder ){
103601
- /* Indices can only be used if all ORDER BY terms past the
103602
- ** equality constraints are all either DESC or ASC. */
103603
- break;
103604
- }
103605
- }else{
103606
- sortOrder = termSortOrder;
103607
- }
103608
- j++;
103609
- pTerm++;
103610
- if( iColumn<0 ){
103611
- seenRowid = 1;
103612
- break;
103613
- }
103614
- }
103615
- *pbRev = sortOrder;
103616
-
103617
- /* If there was an "ORDER BY rowid" term that matched, or it is only
103618
- ** possible for a single row from this table to match, then skip over
103619
- ** any additional ORDER BY terms dealing with this table.
103620
- */
103621
- if( seenRowid ||
103622
- ( (wsFlags & WHERE_COLUMN_NULL)==0
103623
- && i>=pIdx->nColumn
103624
- && indexIsUniqueNotNull(pIdx, nEqCol)
103625
- )
103626
- ){
103627
- /* Advance j over additional ORDER BY terms associated with base */
103628
- WhereMaskSet *pMS = p->pWC->pMaskSet;
103629
- Bitmask m = ~getMask(pMS, base);
103630
- while( j<nTerm && (exprTableUsage(pMS, pOrderBy->a[j].pExpr)&m)==0 ){
103631
- j++;
103632
- }
103633
- }
103634
- return j;
103635
-}
103636
-
103637103554
/*
103638103555
** Prepare a crude estimate of the logarithm of the input value.
103639103556
** The results need not be exact. This is only used for estimating
103640103557
** the total cost of performing operations with O(logN) or O(NlogN)
103641103558
** complexity. Because N is just a guess, it is no great tragedy if
@@ -103785,10 +103702,11 @@
103785103702
WHERETRACE(("... multi-index OR cost=%.9g nrow=%.9g\n", rTotal, nRow));
103786103703
if( rTotal<p->cost.rCost ){
103787103704
p->cost.rCost = rTotal;
103788103705
p->cost.used = used;
103789103706
p->cost.plan.nRow = nRow;
103707
+ p->cost.plan.nOBSat = p->i ? p->aLevel[p->i-1].plan.nOBSat : 0;
103790103708
p->cost.plan.wsFlags = flags;
103791103709
p->cost.plan.u.pTerm = pTerm;
103792103710
}
103793103711
}
103794103712
}
@@ -104327,11 +104245,14 @@
104327104245
}else{
104328104246
p->cost.rCost = rCost;
104329104247
}
104330104248
p->cost.plan.u.pVtabIdx = pIdxInfo;
104331104249
if( pIdxInfo->orderByConsumed ){
104332
- p->cost.plan.wsFlags |= WHERE_ORDERBY;
104250
+ p->cost.plan.wsFlags |= WHERE_ORDERED;
104251
+ p->cost.plan.nOBSat = nOrderBy;
104252
+ }else{
104253
+ p->cost.plan.nOBSat = p->i ? p->aLevel[p->i-1].plan.nOBSat : 0;
104333104254
}
104334104255
p->cost.plan.nEq = 0;
104335104256
pIdxInfo->nOrderBy = nOrderBy;
104336104257
104337104258
/* Try to find a more efficient access pattern by using multiple indexes
@@ -104750,20 +104671,26 @@
104750104671
WhereLevel *pLevel = &p->aLevel[p->i-1];
104751104672
Index *pIdx;
104752104673
u8 sortOrder;
104753104674
for(i=p->i-1; i>=0; i--, pLevel--){
104754104675
if( pLevel->iTabCur!=iTab ) continue;
104755
- if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){
104756
- pIdx = pLevel->plan.u.pIdx;
104676
+ if( (pLevel->plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){
104677
+ return 1;
104678
+ }
104679
+ if( (pLevel->plan.wsFlags & WHERE_ORDERED)==0 ){
104680
+ return 0;
104681
+ }
104682
+ if( (pIdx = pLevel->plan.u.pIdx)!=0 ){
104757104683
if( iCol<0 ){
104758104684
sortOrder = 0;
104759104685
testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 );
104760104686
}else{
104761
- for(j=0; j<pIdx->nColumn; j++){
104687
+ int n = pIdx->nColumn;
104688
+ for(j=0; j<n; j++){
104762104689
if( iCol==pIdx->aiColumn[j] ) break;
104763104690
}
104764
- if( j>=pIdx->nColumn ) return 0;
104691
+ if( j>=n ) return 0;
104765104692
sortOrder = pIdx->aSortOrder[j];
104766104693
testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 );
104767104694
}
104768104695
}else{
104769104696
if( iCol!=(-1) ) return 0;
@@ -104792,13 +104719,10 @@
104792104719
static int isOrderedTerm(WhereBestIdx *p, WhereTerm *pTerm, int *pbRev){
104793104720
Expr *pExpr = pTerm->pExpr;
104794104721
assert( pExpr->op==TK_EQ );
104795104722
assert( pExpr->pLeft!=0 && pExpr->pLeft->op==TK_COLUMN );
104796104723
assert( pExpr->pRight!=0 );
104797
- if( p->i==0 ){
104798
- return 1; /* All == are ordered in the outer loop */
104799
- }
104800104724
if( pTerm->prereqRight==0 ){
104801104725
return 1; /* RHS of the == is a constant */
104802104726
}
104803104727
if( pExpr->pRight->op==TK_COLUMN
104804104728
&& isOrderedColumn(p, pExpr->pRight->iTable, pExpr->pRight->iColumn, pbRev)
@@ -104808,10 +104732,177 @@
104808104732
104809104733
/* If we cannot prove that the constraint is ordered, assume it is not */
104810104734
return 0;
104811104735
}
104812104736
104737
+/*
104738
+** This routine decides if pIdx can be used to satisfy the ORDER BY
104739
+** clause, either in whole or in part. The return value is the
104740
+** cumulative number of terms in the ORDER BY clause that are satisfied
104741
+** by the index pIdx and other indices in outer loops.
104742
+**
104743
+** The table being queried has a cursor number of "base". pIdx is the
104744
+** index that is postulated for use to access the table.
104745
+**
104746
+** nEqCol is the number of columns of pIdx that are used as equality
104747
+** constraints and where the other side of the == is an ordered column
104748
+** or constant. An "order column" in the previous sentence means a column
104749
+** in table from an outer loop whose values will always appear in the
104750
+** correct order due to othre index, or because the outer loop generates
104751
+** a unique result. Any of the first nEqCol columns of pIdx may be missing
104752
+** from the ORDER BY clause and the match can still be a success.
104753
+**
104754
+** The *pbRev value is set to 0 order 1 depending on whether or not
104755
+** pIdx should be run in the forward order or in reverse order.
104756
+*/
104757
+static int isSortingIndex(
104758
+ WhereBestIdx *p, /* Best index search context */
104759
+ Index *pIdx, /* The index we are testing */
104760
+ int base, /* Cursor number for the table to be sorted */
104761
+ int nEqCol, /* Number of index columns with ordered == constraints */
104762
+ int wsFlags, /* Index usages flags */
104763
+ int bOuterRev, /* True if outer loops scan in reverse order */
104764
+ int *pbRev /* Set to 1 for reverse-order scan of pIdx */
104765
+){
104766
+ int i; /* Number of pIdx terms used */
104767
+ int j; /* Number of ORDER BY terms satisfied */
104768
+ int sortOrder = 0; /* XOR of index and ORDER BY sort direction */
104769
+ int nTerm; /* Number of ORDER BY terms */
104770
+ struct ExprList_item *pTerm; /* A term of the ORDER BY clause */
104771
+ ExprList *pOrderBy; /* The ORDER BY clause */
104772
+ Parse *pParse = p->pParse; /* Parser context */
104773
+ sqlite3 *db = pParse->db; /* Database connection */
104774
+ int nPriorSat; /* ORDER BY terms satisfied by outer loops */
104775
+ int seenRowid = 0; /* True if an ORDER BY rowid term is seen */
104776
+ int nEqOneRow; /* Idx columns that ref unique values */
104777
+
104778
+ if( p->i==0 ){
104779
+ nPriorSat = 0;
104780
+ }else{
104781
+ nPriorSat = p->aLevel[p->i-1].plan.nOBSat;
104782
+ if( OptimizationDisabled(db, SQLITE_OrderByIdxJoin) ) return nPriorSat;
104783
+ }
104784
+ if( nEqCol==0 ){
104785
+ if( p->i && (p->aLevel[p->i-1].plan.wsFlags & WHERE_ORDERED)==0 ){
104786
+ return nPriorSat;
104787
+ }
104788
+ nEqOneRow = 0;
104789
+ }else if( p->i==0 || (p->aLevel[p->i-1].plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){
104790
+ nEqOneRow = nEqCol;
104791
+ }else{
104792
+ sortOrder = bOuterRev;
104793
+ nEqOneRow = -1;
104794
+ }
104795
+ pOrderBy = p->pOrderBy;
104796
+ assert( pOrderBy!=0 );
104797
+ if( wsFlags & WHERE_COLUMN_IN ) return nPriorSat;
104798
+ if( pIdx->bUnordered ) return nPriorSat;
104799
+ nTerm = pOrderBy->nExpr;
104800
+ assert( nTerm>0 );
104801
+
104802
+ /* Argument pIdx must either point to a 'real' named index structure,
104803
+ ** or an index structure allocated on the stack by bestBtreeIndex() to
104804
+ ** represent the rowid index that is part of every table. */
104805
+ assert( pIdx->zName || (pIdx->nColumn==1 && pIdx->aiColumn[0]==-1) );
104806
+
104807
+ /* Match terms of the ORDER BY clause against columns of
104808
+ ** the index.
104809
+ **
104810
+ ** Note that indices have pIdx->nColumn regular columns plus
104811
+ ** one additional column containing the rowid. The rowid column
104812
+ ** of the index is also allowed to match against the ORDER BY
104813
+ ** clause.
104814
+ */
104815
+ for(i=0,j=nPriorSat,pTerm=&pOrderBy->a[j]; j<nTerm; i++){
104816
+ Expr *pExpr; /* The expression of the ORDER BY pTerm */
104817
+ CollSeq *pColl; /* The collating sequence of pExpr */
104818
+ int termSortOrder; /* Sort order for this term */
104819
+ int iColumn; /* The i-th column of the index. -1 for rowid */
104820
+ int iSortOrder; /* 1 for DESC, 0 for ASC on the i-th index term */
104821
+ const char *zColl; /* Name of the collating sequence for i-th index term */
104822
+
104823
+ assert( i<=pIdx->nColumn );
104824
+ pExpr = pTerm->pExpr;
104825
+ if( pExpr->op!=TK_COLUMN || pExpr->iTable!=base ){
104826
+ /* Can not use an index sort on anything that is not a column in the
104827
+ ** left-most table of the FROM clause */
104828
+ break;
104829
+ }
104830
+ pColl = sqlite3ExprCollSeq(pParse, pExpr);
104831
+ if( !pColl ){
104832
+ pColl = db->pDfltColl;
104833
+ }
104834
+ if( pIdx->zName && i<pIdx->nColumn ){
104835
+ iColumn = pIdx->aiColumn[i];
104836
+ if( iColumn==pIdx->pTable->iPKey ){
104837
+ iColumn = -1;
104838
+ }
104839
+ iSortOrder = pIdx->aSortOrder[i];
104840
+ zColl = pIdx->azColl[i];
104841
+ }else{
104842
+ iColumn = -1;
104843
+ iSortOrder = 0;
104844
+ zColl = pColl->zName;
104845
+ }
104846
+ if( pExpr->iColumn!=iColumn || sqlite3StrICmp(pColl->zName, zColl) ){
104847
+ /* Term j of the ORDER BY clause does not match column i of the index */
104848
+ if( i<nEqCol ){
104849
+ /* If an index column that is constrained by == fails to match an
104850
+ ** ORDER BY term, that is OK. Just ignore that column of the index
104851
+ */
104852
+ continue;
104853
+ }else if( i==pIdx->nColumn ){
104854
+ /* Index column i is the rowid. All other terms match. */
104855
+ break;
104856
+ }else{
104857
+ /* If an index column fails to match and is not constrained by ==
104858
+ ** then the index cannot satisfy the ORDER BY constraint.
104859
+ */
104860
+ return nPriorSat;
104861
+ }
104862
+ }
104863
+ assert( pIdx->aSortOrder!=0 || iColumn==-1 );
104864
+ assert( pTerm->sortOrder==0 || pTerm->sortOrder==1 );
104865
+ assert( iSortOrder==0 || iSortOrder==1 );
104866
+ termSortOrder = iSortOrder ^ pTerm->sortOrder;
104867
+ if( i>nEqOneRow ){
104868
+ if( termSortOrder!=sortOrder ){
104869
+ /* Indices can only be used if all ORDER BY terms past the
104870
+ ** equality constraints have the correct DESC or ASC. */
104871
+ break;
104872
+ }
104873
+ }else{
104874
+ sortOrder = termSortOrder;
104875
+ }
104876
+ j++;
104877
+ pTerm++;
104878
+ if( iColumn<0 ){
104879
+ seenRowid = 1;
104880
+ break;
104881
+ }
104882
+ }
104883
+ *pbRev = sortOrder;
104884
+
104885
+ /* If there was an "ORDER BY rowid" term that matched, or it is only
104886
+ ** possible for a single row from this table to match, then skip over
104887
+ ** any additional ORDER BY terms dealing with this table.
104888
+ */
104889
+ if( seenRowid ||
104890
+ ( (wsFlags & WHERE_COLUMN_NULL)==0
104891
+ && i>=pIdx->nColumn
104892
+ && indexIsUniqueNotNull(pIdx, nEqCol)
104893
+ )
104894
+ ){
104895
+ /* Advance j over additional ORDER BY terms associated with base */
104896
+ WhereMaskSet *pMS = p->pWC->pMaskSet;
104897
+ Bitmask m = ~getMask(pMS, base);
104898
+ while( j<nTerm && (exprTableUsage(pMS, pOrderBy->a[j].pExpr)&m)==0 ){
104899
+ j++;
104900
+ }
104901
+ }
104902
+ return j;
104903
+}
104813104904
104814104905
/*
104815104906
** Find the best query plan for accessing a particular table. Write the
104816104907
** best query plan and its cost into the p->cost.
104817104908
**
@@ -104902,22 +104993,19 @@
104902104993
104903104994
/* Loop over all indices looking for the best one to use
104904104995
*/
104905104996
for(; pProbe; pIdx=pProbe=pProbe->pNext){
104906104997
const tRowcnt * const aiRowEst = pProbe->aiRowEst;
104907
- double cost; /* Cost of using pProbe */
104908
- double nRow; /* Estimated number of rows in result set */
104998
+ WhereCost pc; /* Cost of using pProbe */
104909104999
double log10N = (double)1; /* base-10 logarithm of nRow (inexact) */
104910105000
int bRev = 2; /* 0=forward scan. 1=reverse. 2=undecided */
104911
- int wsFlags = 0;
104912
- Bitmask used = 0;
104913105001
104914105002
/* The following variables are populated based on the properties of
104915105003
** index being evaluated. They are then used to determine the expected
104916105004
** cost and number of rows returned.
104917105005
**
104918
- ** nEq:
105006
+ ** pc.plan.nEq:
104919105007
** Number of equality terms that can be implemented using the index.
104920105008
** In other words, the number of initial fields in the index that
104921105009
** are used in == or IN or NOT NULL constraints of the WHERE clause.
104922105010
**
104923105011
** nInMul:
@@ -104960,11 +105048,11 @@
104960105048
** bSort:
104961105049
** Boolean. True if there is an ORDER BY clause that will require an
104962105050
** external sort (i.e. scanning the index being evaluated will not
104963105051
** correctly order records).
104964105052
**
104965
- ** bDistinct:
105053
+ ** bDist:
104966105054
** Boolean. True if there is a DISTINCT clause that will require an
104967105055
** external btree.
104968105056
**
104969105057
** bLookup:
104970105058
** Boolean. True if a table lookup is required for each index entry
@@ -104979,132 +105067,148 @@
104979105067
** both available in the index.
104980105068
**
104981105069
** SELECT a, b FROM tbl WHERE a = 1;
104982105070
** SELECT a, b, c FROM tbl WHERE a = 1;
104983105071
*/
104984
- int nEq; /* Number of == or IN terms matching index */
104985105072
int nOrdered; /* Number of ordered terms matching index */
104986105073
int bInEst = 0; /* True if "x IN (SELECT...)" seen */
104987105074
int nInMul = 1; /* Number of distinct equalities to lookup */
104988105075
double rangeDiv = (double)1; /* Estimated reduction in search space */
104989105076
int nBound = 0; /* Number of range constraints seen */
104990105077
int bSort; /* True if external sort required */
104991105078
int bDist; /* True if index cannot help with DISTINCT */
104992105079
int bLookup = 0; /* True if not a covering index */
104993
- int nOBSat = 0; /* Number of ORDER BY terms satisfied */
105080
+ int nPriorSat; /* ORDER BY terms satisfied by outer loops */
104994105081
int nOrderBy; /* Number of ORDER BY terms */
104995105082
WhereTerm *pTerm; /* A single term of the WHERE clause */
104996105083
#ifdef SQLITE_ENABLE_STAT3
104997105084
WhereTerm *pFirstTerm = 0; /* First term matching the index */
104998105085
#endif
104999105086
105087
+ memset(&pc, 0, sizeof(pc));
105000105088
nOrderBy = p->pOrderBy ? p->pOrderBy->nExpr : 0;
105001
- bSort = nOrderBy>0 && (p->i==0 || p->aLevel[p->i-1].plan.nOBSat<nOrderBy);
105002
- bDist = p->i==0 && p->pDistinct!=0;
105089
+ if( p->i ){
105090
+ nPriorSat = pc.plan.nOBSat = p->aLevel[p->i-1].plan.nOBSat;
105091
+ bSort = nPriorSat<nOrderBy;
105092
+ bDist = 0;
105093
+ }else{
105094
+ nPriorSat = pc.plan.nOBSat = 0;
105095
+ bSort = nOrderBy>0;
105096
+ bDist = p->pDistinct!=0;
105097
+ }
105003105098
105004
- /* Determine the values of nEq and nInMul */
105005
- for(nEq=nOrdered=0; nEq<pProbe->nColumn; nEq++){
105006
- int j = pProbe->aiColumn[nEq];
105099
+ /* Determine the values of pc.plan.nEq and nInMul */
105100
+ for(pc.plan.nEq=nOrdered=0; pc.plan.nEq<pProbe->nColumn; pc.plan.nEq++){
105101
+ int j = pProbe->aiColumn[pc.plan.nEq];
105007105102
pTerm = findTerm(pWC, iCur, j, p->notReady, eqTermMask, pIdx);
105008105103
if( pTerm==0 ) break;
105009
- wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ);
105104
+ pc.plan.wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ);
105010105105
testcase( pTerm->pWC!=pWC );
105011105106
if( pTerm->eOperator & WO_IN ){
105012105107
Expr *pExpr = pTerm->pExpr;
105013
- wsFlags |= WHERE_COLUMN_IN;
105108
+ pc.plan.wsFlags |= WHERE_COLUMN_IN;
105014105109
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
105015105110
/* "x IN (SELECT ...)": Assume the SELECT returns 25 rows */
105016105111
nInMul *= 25;
105017105112
bInEst = 1;
105018105113
}else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){
105019105114
/* "x IN (value, value, ...)" */
105020105115
nInMul *= pExpr->x.pList->nExpr;
105021105116
}
105022105117
}else if( pTerm->eOperator & WO_ISNULL ){
105023
- wsFlags |= WHERE_COLUMN_NULL;
105024
- if( nEq==nOrdered ) nOrdered++;
105025
- }else if( bSort && nEq==nOrdered && isOrderedTerm(p, pTerm, &bRev) ){
105118
+ pc.plan.wsFlags |= WHERE_COLUMN_NULL;
105119
+ if( pc.plan.nEq==nOrdered ) nOrdered++;
105120
+ }else if( bSort && pc.plan.nEq==nOrdered
105121
+ && isOrderedTerm(p,pTerm,&bRev) ){
105026105122
nOrdered++;
105027105123
}
105028105124
#ifdef SQLITE_ENABLE_STAT3
105029
- if( nEq==0 && pProbe->aSample ) pFirstTerm = pTerm;
105125
+ if( pc.plan.nEq==0 && pProbe->aSample ) pFirstTerm = pTerm;
105030105126
#endif
105031
- used |= pTerm->prereqRight;
105127
+ pc.used |= pTerm->prereqRight;
105032105128
}
105033105129
105034105130
/* If the index being considered is UNIQUE, and there is an equality
105035105131
** constraint for all columns in the index, then this search will find
105036105132
** at most a single row. In this case set the WHERE_UNIQUE flag to
105037105133
** indicate this to the caller.
105038105134
**
105039105135
** Otherwise, if the search may find more than one row, test to see if
105040
- ** there is a range constraint on indexed column (nEq+1) that can be
105136
+ ** there is a range constraint on indexed column (pc.plan.nEq+1) that can be
105041105137
** optimized using the index.
105042105138
*/
105043
- if( nEq==pProbe->nColumn && pProbe->onError!=OE_None ){
105044
- testcase( wsFlags & WHERE_COLUMN_IN );
105045
- testcase( wsFlags & WHERE_COLUMN_NULL );
105046
- if( (wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){
105047
- wsFlags |= WHERE_UNIQUE;
105139
+ if( pc.plan.nEq==pProbe->nColumn && pProbe->onError!=OE_None ){
105140
+ testcase( pc.plan.wsFlags & WHERE_COLUMN_IN );
105141
+ testcase( pc.plan.wsFlags & WHERE_COLUMN_NULL );
105142
+ if( (pc.plan.wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){
105143
+ pc.plan.wsFlags |= WHERE_UNIQUE;
105144
+ if( p->i==0 || (p->aLevel[p->i-1].plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){
105145
+ pc.plan.wsFlags |= WHERE_ALL_UNIQUE;
105146
+ }
105048105147
}
105049105148
}else if( pProbe->bUnordered==0 ){
105050
- int j = (nEq==pProbe->nColumn ? -1 : pProbe->aiColumn[nEq]);
105149
+ int j;
105150
+ j = (pc.plan.nEq==pProbe->nColumn ? -1 : pProbe->aiColumn[pc.plan.nEq]);
105051105151
if( findTerm(pWC, iCur, j, p->notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){
105052105152
WhereTerm *pTop, *pBtm;
105053105153
pTop = findTerm(pWC, iCur, j, p->notReady, WO_LT|WO_LE, pIdx);
105054105154
pBtm = findTerm(pWC, iCur, j, p->notReady, WO_GT|WO_GE, pIdx);
105055
- whereRangeScanEst(pParse, pProbe, nEq, pBtm, pTop, &rangeDiv);
105155
+ whereRangeScanEst(pParse, pProbe, pc.plan.nEq, pBtm, pTop, &rangeDiv);
105056105156
if( pTop ){
105057105157
nBound = 1;
105058
- wsFlags |= WHERE_TOP_LIMIT;
105059
- used |= pTop->prereqRight;
105158
+ pc.plan.wsFlags |= WHERE_TOP_LIMIT;
105159
+ pc.used |= pTop->prereqRight;
105060105160
testcase( pTop->pWC!=pWC );
105061105161
}
105062105162
if( pBtm ){
105063105163
nBound++;
105064
- wsFlags |= WHERE_BTM_LIMIT;
105065
- used |= pBtm->prereqRight;
105164
+ pc.plan.wsFlags |= WHERE_BTM_LIMIT;
105165
+ pc.used |= pBtm->prereqRight;
105066105166
testcase( pBtm->pWC!=pWC );
105067105167
}
105068
- wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE);
105168
+ pc.plan.wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE);
105069105169
}
105070105170
}
105071105171
105072105172
/* If there is an ORDER BY clause and the index being considered will
105073105173
** naturally scan rows in the required order, set the appropriate flags
105074
- ** in wsFlags. Otherwise, if there is an ORDER BY clause but the index
105075
- ** will scan rows in a different order, set the bSort variable. */
105174
+ ** in pc.plan.wsFlags. Otherwise, if there is an ORDER BY clause but
105175
+ ** the index will scan rows in a different order, set the bSort
105176
+ ** variable. */
105076105177
assert( bRev>=0 && bRev<=2 );
105077105178
if( bSort ){
105078105179
testcase( bRev==0 );
105079105180
testcase( bRev==1 );
105080105181
testcase( bRev==2 );
105081
- nOBSat = isSortingIndex(p, pProbe, iCur, nOrdered,
105082
- wsFlags, bRev&1, &bRev);
105083
- if( nOrderBy==nOBSat ){
105182
+ pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, nOrdered,
105183
+ pc.plan.wsFlags, bRev&1, &bRev);
105184
+ if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_UNIQUE)!=0 ){
105185
+ pc.plan.wsFlags |= WHERE_ORDERED;
105186
+ }
105187
+ if( nOrderBy==pc.plan.nOBSat ){
105084105188
bSort = 0;
105085
- wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_ORDERBY;
105189
+ pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE;
105086105190
}
105087
- if( bRev & 1 ) wsFlags |= WHERE_REVERSE;
105191
+ if( bRev & 1 ) pc.plan.wsFlags |= WHERE_REVERSE;
105088105192
}
105089105193
105090105194
/* If there is a DISTINCT qualifier and this index will scan rows in
105091105195
** order of the DISTINCT expressions, clear bDist and set the appropriate
105092
- ** flags in wsFlags. */
105196
+ ** flags in pc.plan.wsFlags. */
105093105197
if( bDist
105094
- && isDistinctIndex(pParse, pWC, pProbe, iCur, p->pDistinct, nEq)
105095
- && (wsFlags & WHERE_COLUMN_IN)==0
105198
+ && isDistinctIndex(pParse, pWC, pProbe, iCur, p->pDistinct, pc.plan.nEq)
105199
+ && (pc.plan.wsFlags & WHERE_COLUMN_IN)==0
105096105200
){
105097105201
bDist = 0;
105098
- wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_DISTINCT;
105202
+ pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_DISTINCT;
105099105203
}
105100105204
105101105205
/* If currently calculating the cost of using an index (not the IPK
105102105206
** index), determine if all required column data may be obtained without
105103105207
** using the main table (i.e. if the index is a covering
105104105208
** index for this query). If it is, set the WHERE_IDX_ONLY flag in
105105
- ** wsFlags. Otherwise, set the bLookup variable to true. */
105209
+ ** pc.plan.wsFlags. Otherwise, set the bLookup variable to true. */
105106105210
if( pIdx ){
105107105211
Bitmask m = pSrc->colUsed;
105108105212
int j;
105109105213
for(j=0; j<pIdx->nColumn; j++){
105110105214
int x = pIdx->aiColumn[j];
@@ -105111,51 +105215,54 @@
105111105215
if( x<BMS-1 ){
105112105216
m &= ~(((Bitmask)1)<<x);
105113105217
}
105114105218
}
105115105219
if( m==0 ){
105116
- wsFlags |= WHERE_IDX_ONLY;
105220
+ pc.plan.wsFlags |= WHERE_IDX_ONLY;
105117105221
}else{
105118105222
bLookup = 1;
105119105223
}
105120105224
}
105121105225
105122105226
/*
105123105227
** Estimate the number of rows of output. For an "x IN (SELECT...)"
105124105228
** constraint, do not let the estimate exceed half the rows in the table.
105125105229
*/
105126
- nRow = (double)(aiRowEst[nEq] * nInMul);
105127
- if( bInEst && nRow*2>aiRowEst[0] ){
105128
- nRow = aiRowEst[0]/2;
105129
- nInMul = (int)(nRow / aiRowEst[nEq]);
105230
+ pc.plan.nRow = (double)(aiRowEst[pc.plan.nEq] * nInMul);
105231
+ if( bInEst && pc.plan.nRow*2>aiRowEst[0] ){
105232
+ pc.plan.nRow = aiRowEst[0]/2;
105233
+ nInMul = (int)(pc.plan.nRow / aiRowEst[pc.plan.nEq]);
105130105234
}
105131105235
105132105236
#ifdef SQLITE_ENABLE_STAT3
105133105237
/* If the constraint is of the form x=VALUE or x IN (E1,E2,...)
105134105238
** and we do not think that values of x are unique and if histogram
105135105239
** data is available for column x, then it might be possible
105136105240
** to get a better estimate on the number of rows based on
105137105241
** VALUE and how common that value is according to the histogram.
105138105242
*/
105139
- if( nRow>(double)1 && nEq==1 && pFirstTerm!=0 && aiRowEst[1]>1 ){
105243
+ if( pc.plan.nRow>(double)1 && pc.plan.nEq==1
105244
+ && pFirstTerm!=0 && aiRowEst[1]>1 ){
105140105245
assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 );
105141105246
if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){
105142105247
testcase( pFirstTerm->eOperator==WO_EQ );
105143105248
testcase( pFirstTerm->eOperator==WO_ISNULL );
105144
- whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, &nRow);
105249
+ whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight,
105250
+ &pc.plan.nRow);
105145105251
}else if( bInEst==0 ){
105146105252
assert( pFirstTerm->eOperator==WO_IN );
105147
- whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, &nRow);
105253
+ whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList,
105254
+ &pc.plan.nRow);
105148105255
}
105149105256
}
105150105257
#endif /* SQLITE_ENABLE_STAT3 */
105151105258
105152105259
/* Adjust the number of output rows and downward to reflect rows
105153105260
** that are excluded by range constraints.
105154105261
*/
105155
- nRow = nRow/rangeDiv;
105156
- if( nRow<1 ) nRow = 1;
105262
+ pc.plan.nRow = pc.plan.nRow/rangeDiv;
105263
+ if( pc.plan.nRow<1 ) pc.plan.nRow = 1;
105157105264
105158105265
/* Experiments run on real SQLite databases show that the time needed
105159105266
** to do a binary search to locate a row in a table or index is roughly
105160105267
** log10(N) times the time to move from one row to the next row within
105161105268
** a table or index. The actual times can vary, with the size of
@@ -105166,57 +105273,58 @@
105166105273
** The ANALYZE command and the sqlite_stat1 and sqlite_stat3 tables do
105167105274
** not give us data on the relative sizes of table and index records.
105168105275
** So this computation assumes table records are about twice as big
105169105276
** as index records
105170105277
*/
105171
- if( (wsFlags&~WHERE_REVERSE)==WHERE_IDX_ONLY
105278
+ if( (pc.plan.wsFlags&~(WHERE_REVERSE|WHERE_ORDERED))==WHERE_IDX_ONLY
105172105279
&& (pWC->wctrlFlags & WHERE_ONEPASS_DESIRED)==0
105173105280
&& sqlite3GlobalConfig.bUseCis
105174105281
&& OptimizationEnabled(pParse->db, SQLITE_CoverIdxScan)
105175105282
){
105176105283
/* This index is not useful for indexing, but it is a covering index.
105177105284
** A full-scan of the index might be a little faster than a full-scan
105178105285
** of the table, so give this case a cost slightly less than a table
105179105286
** scan. */
105180
- cost = aiRowEst[0]*3 + pProbe->nColumn;
105181
- wsFlags |= WHERE_COVER_SCAN|WHERE_COLUMN_RANGE;
105182
- }else if( (wsFlags & WHERE_NOT_FULLSCAN)==0 ){
105287
+ pc.rCost = aiRowEst[0]*3 + pProbe->nColumn;
105288
+ pc.plan.wsFlags |= WHERE_COVER_SCAN|WHERE_COLUMN_RANGE;
105289
+ }else if( (pc.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){
105183105290
/* The cost of a full table scan is a number of move operations equal
105184105291
** to the number of rows in the table.
105185105292
**
105186105293
** We add an additional 4x penalty to full table scans. This causes
105187105294
** the cost function to err on the side of choosing an index over
105188105295
** choosing a full scan. This 4x full-scan penalty is an arguable
105189105296
** decision and one which we expect to revisit in the future. But
105190105297
** it seems to be working well enough at the moment.
105191105298
*/
105192
- cost = aiRowEst[0]*4;
105193
- wsFlags &= ~WHERE_IDX_ONLY;
105299
+ pc.rCost = aiRowEst[0]*4;
105300
+ pc.plan.wsFlags &= ~WHERE_IDX_ONLY;
105301
+ if( pIdx ) pc.plan.wsFlags &= ~WHERE_ORDERED;
105194105302
}else{
105195105303
log10N = estLog(aiRowEst[0]);
105196
- cost = nRow;
105304
+ pc.rCost = pc.plan.nRow;
105197105305
if( pIdx ){
105198105306
if( bLookup ){
105199105307
/* For an index lookup followed by a table lookup:
105200105308
** nInMul index searches to find the start of each index range
105201105309
** + nRow steps through the index
105202105310
** + nRow table searches to lookup the table entry using the rowid
105203105311
*/
105204
- cost += (nInMul + nRow)*log10N;
105312
+ pc.rCost += (nInMul + pc.plan.nRow)*log10N;
105205105313
}else{
105206105314
/* For a covering index:
105207105315
** nInMul index searches to find the initial entry
105208105316
** + nRow steps through the index
105209105317
*/
105210
- cost += nInMul*log10N;
105318
+ pc.rCost += nInMul*log10N;
105211105319
}
105212105320
}else{
105213105321
/* For a rowid primary key lookup:
105214105322
** nInMult table searches to find the initial entry for each range
105215105323
** + nRow steps through the table
105216105324
*/
105217
- cost += nInMul*log10N;
105325
+ pc.rCost += nInMul*log10N;
105218105326
}
105219105327
}
105220105328
105221105329
/* Add in the estimated cost of sorting the result. Actual experimental
105222105330
** measurements of sorting performance in SQLite show that sorting time
@@ -105223,14 +105331,16 @@
105223105331
** adds C*N*log10(N) to the cost, where N is the number of rows to be
105224105332
** sorted and C is a factor between 1.95 and 4.3. We will split the
105225105333
** difference and select C of 3.0.
105226105334
*/
105227105335
if( bSort ){
105228
- cost += nRow*estLog(nRow*(nOrderBy - nOBSat)/nOrderBy)*3;
105336
+ double m = estLog(pc.plan.nRow*(nOrderBy - pc.plan.nOBSat)/nOrderBy);
105337
+ m *= (double)(pc.plan.nOBSat ? 2 : 3);
105338
+ pc.rCost += pc.plan.nRow*m;
105229105339
}
105230105340
if( bDist ){
105231
- cost += nRow*estLog(nRow)*3;
105341
+ pc.rCost += pc.plan.nRow*estLog(pc.plan.nRow)*3;
105232105342
}
105233105343
105234105344
/**** Cost of using this index has now been computed ****/
105235105345
105236105346
/* If there are additional constraints on this table that cannot
@@ -105247,29 +105357,29 @@
105247105357
** tables that are not in outer loops. If notReady is used here instead
105248105358
** of notValid, then a optimal index that depends on inner joins loops
105249105359
** might be selected even when there exists an optimal index that has
105250105360
** no such dependency.
105251105361
*/
105252
- if( nRow>2 && cost<=p->cost.rCost ){
105362
+ if( pc.plan.nRow>2 && pc.rCost<=p->cost.rCost ){
105253105363
int k; /* Loop counter */
105254
- int nSkipEq = nEq; /* Number of == constraints to skip */
105364
+ int nSkipEq = pc.plan.nEq; /* Number of == constraints to skip */
105255105365
int nSkipRange = nBound; /* Number of < constraints to skip */
105256105366
Bitmask thisTab; /* Bitmap for pSrc */
105257105367
105258105368
thisTab = getMask(pWC->pMaskSet, iCur);
105259
- for(pTerm=pWC->a, k=pWC->nTerm; nRow>2 && k; k--, pTerm++){
105369
+ for(pTerm=pWC->a, k=pWC->nTerm; pc.plan.nRow>2 && k; k--, pTerm++){
105260105370
if( pTerm->wtFlags & TERM_VIRTUAL ) continue;
105261105371
if( (pTerm->prereqAll & p->notValid)!=thisTab ) continue;
105262105372
if( pTerm->eOperator & (WO_EQ|WO_IN|WO_ISNULL) ){
105263105373
if( nSkipEq ){
105264
- /* Ignore the first nEq equality matches since the index
105374
+ /* Ignore the first pc.plan.nEq equality matches since the index
105265105375
** has already accounted for these */
105266105376
nSkipEq--;
105267105377
}else{
105268105378
/* Assume each additional equality match reduces the result
105269105379
** set size by a factor of 10 */
105270
- nRow /= 10;
105380
+ pc.plan.nRow /= 10;
105271105381
}
105272105382
}else if( pTerm->eOperator & (WO_LT|WO_LE|WO_GT|WO_GE) ){
105273105383
if( nSkipRange ){
105274105384
/* Ignore the first nSkipRange range constraints since the index
105275105385
** has already accounted for these */
@@ -105279,43 +105389,38 @@
105279105389
** set size by a factor of 3. Indexed range constraints reduce
105280105390
** the search space by a larger factor: 4. We make indexed range
105281105391
** more selective intentionally because of the subjective
105282105392
** observation that indexed range constraints really are more
105283105393
** selective in practice, on average. */
105284
- nRow /= 3;
105394
+ pc.plan.nRow /= 3;
105285105395
}
105286105396
}else if( pTerm->eOperator!=WO_NOOP ){
105287105397
/* Any other expression lowers the output row count by half */
105288
- nRow /= 2;
105398
+ pc.plan.nRow /= 2;
105289105399
}
105290105400
}
105291
- if( nRow<2 ) nRow = 2;
105401
+ if( pc.plan.nRow<2 ) pc.plan.nRow = 2;
105292105402
}
105293105403
105294105404
105295105405
WHERETRACE((
105296105406
"%s(%s):\n"
105297105407
" nEq=%d nInMul=%d rangeDiv=%d bSort=%d bLookup=%d wsFlags=0x%08x\n"
105298105408
" notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f\n"
105299105409
" used=0x%llx nOrdered=%d nOBSat=%d\n",
105300105410
pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk"),
105301
- nEq, nInMul, (int)rangeDiv, bSort, bLookup, wsFlags,
105302
- p->notReady, log10N, nRow, cost, used, nOrdered, nOBSat
105411
+ pc.plan.nEq, nInMul, (int)rangeDiv, bSort, bLookup, pc.plan.wsFlags,
105412
+ p->notReady, log10N, pc.plan.nRow, pc.rCost, pc.used, nOrdered,
105413
+ pc.plan.nOBSat
105303105414
));
105304105415
105305105416
/* If this index is the best we have seen so far, then record this
105306
- ** index and its cost in the pCost structure.
105417
+ ** index and its cost in the p->cost structure.
105307105418
*/
105308
- if( (!pIdx || wsFlags)
105309
- && (cost<p->cost.rCost || (cost<=p->cost.rCost && nRow<p->cost.plan.nRow))
105310
- ){
105311
- p->cost.rCost = cost;
105312
- p->cost.used = used;
105313
- p->cost.plan.nRow = nRow;
105314
- p->cost.plan.wsFlags = (wsFlags&wsFlagMask);
105315
- p->cost.plan.nEq = nEq;
105316
- p->cost.plan.nOBSat = nOBSat;
105419
+ if( (!pIdx || pc.plan.wsFlags) && compareCost(&pc, &p->cost) ){
105420
+ p->cost = pc;
105421
+ p->cost.plan.wsFlags &= wsFlagMask;
105317105422
p->cost.plan.u.pIdx = pIdx;
105318105423
}
105319105424
105320105425
/* If there was an INDEXED BY clause, then only that one index is
105321105426
** considered. */
@@ -105333,21 +105438,19 @@
105333105438
** SQLite outputs rows in in the absence of an ORDER BY clause. */
105334105439
if( !p->pOrderBy && pParse->db->flags & SQLITE_ReverseOrder ){
105335105440
p->cost.plan.wsFlags |= WHERE_REVERSE;
105336105441
}
105337105442
105338
- assert( p->pOrderBy || (p->cost.plan.wsFlags&WHERE_ORDERBY)==0 );
105443
+ assert( p->pOrderBy || (p->cost.plan.wsFlags&WHERE_ORDERED)==0 );
105339105444
assert( p->cost.plan.u.pIdx==0 || (p->cost.plan.wsFlags&WHERE_ROWID_EQ)==0 );
105340105445
assert( pSrc->pIndex==0
105341105446
|| p->cost.plan.u.pIdx==0
105342105447
|| p->cost.plan.u.pIdx==pSrc->pIndex
105343105448
);
105344105449
105345
- WHERETRACE(("best index is: %s\n",
105346
- ((p->cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ? "none" :
105347
- p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk")
105348
- ));
105450
+ WHERETRACE(("best index is: %s\n",
105451
+ p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk"));
105349105452
105350105453
bestOrClauseIndex(p);
105351105454
bestAutomaticIndex(p);
105352105455
p->cost.plan.wsFlags |= eqTermMask;
105353105456
}
@@ -106071,11 +106174,11 @@
106071106174
** should not have a NULL value stored in 'x'. If column 'x' is
106072106175
** the first one after the nEq equality constraints in the index,
106073106176
** this requires some special handling.
106074106177
*/
106075106178
if( (wctrlFlags&WHERE_ORDERBY_MIN)!=0
106076
- && (pLevel->plan.wsFlags&WHERE_ORDERBY)
106179
+ && (pLevel->plan.wsFlags&WHERE_ORDERED)
106077106180
&& (pIdx->nColumn>nEq)
106078106181
){
106079106182
/* assert( pOrderBy->nExpr==1 ); */
106080106183
/* assert( pOrderBy->a[0].pExpr->iColumn==pIdx->aiColumn[nEq] ); */
106081106184
isMinQuery = 1;
@@ -106892,12 +106995,12 @@
106892106995
continue;
106893106996
}
106894106997
sWBI.notReady = (isOptimal ? m : sWBI.notValid);
106895106998
if( sWBI.pSrc->pIndex==0 ) nUnconstrained++;
106896106999
106897
- WHERETRACE(("=== trying table %d with isOptimal=%d ===\n",
106898
- j, isOptimal));
107000
+ WHERETRACE(("=== trying table %d (%s) with isOptimal=%d ===\n",
107001
+ j, sWBI.pSrc->pTab->zName, isOptimal));
106899107002
assert( sWBI.pSrc->pTab );
106900107003
#ifndef SQLITE_OMIT_VIRTUALTABLE
106901107004
if( IsVirtual(sWBI.pSrc->pTab) ){
106902107005
sWBI.ppIdxInfo = &pWInfo->a[j].pIdxInfo;
106903107006
bestVirtualIndex(&sWBI);
@@ -106934,48 +107037,46 @@
106934107037
** combination of INDEXED BY clauses are given. The error
106935107038
** will be detected and relayed back to the application later.
106936107039
** The NEVER() comes about because rule (2) above prevents
106937107040
** An indexable full-table-scan from reaching rule (3).
106938107041
**
106939
- ** (4) The plan cost must be lower than prior plans or else the
106940
- ** cost must be the same and the number of rows must be lower.
107042
+ ** (4) The plan cost must be lower than prior plans, where "cost"
107043
+ ** is defined by the compareCost() function above.
106941107044
*/
106942107045
if( (sWBI.cost.used&sWBI.notValid)==0 /* (1) */
106943107046
&& (bestJ<0 || (notIndexed&m)!=0 /* (2) */
106944107047
|| (bestPlan.plan.wsFlags & WHERE_NOT_FULLSCAN)==0
106945107048
|| (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)
106946107049
&& (nUnconstrained==0 || sWBI.pSrc->pIndex==0 /* (3) */
106947107050
|| NEVER((sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0))
106948
- && (bestJ<0 || sWBI.cost.rCost<bestPlan.rCost /* (4) */
106949
- || (sWBI.cost.rCost<=bestPlan.rCost
106950
- && sWBI.cost.plan.nRow<bestPlan.plan.nRow))
107051
+ && (bestJ<0 || compareCost(&sWBI.cost, &bestPlan)) /* (4) */
106951107052
){
106952
- WHERETRACE(("=== table %d is best so far"
106953
- " with cost=%.1f, nRow=%.1f, nOBSat=%d\n",
106954
- j, sWBI.cost.rCost, sWBI.cost.plan.nRow,
106955
- sWBI.cost.plan.nOBSat));
107053
+ WHERETRACE(("=== table %d (%s) is best so far\n"
107054
+ " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=%08x\n",
107055
+ j, sWBI.pSrc->pTab->zName,
107056
+ sWBI.cost.rCost, sWBI.cost.plan.nRow,
107057
+ sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags));
106956107058
bestPlan = sWBI.cost;
106957107059
bestJ = j;
106958107060
}
106959107061
if( doNotReorder ) break;
106960107062
}
106961107063
}
106962107064
assert( bestJ>=0 );
106963107065
assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) );
106964
- WHERETRACE(("*** Optimizer selects table %d for loop %d with:\n"
106965
- " cost=%.1f, nRow=%.1f, nOBSat=%d wsFlags=0x%08x\n",
106966
- bestJ, pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow,
107066
+ WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n"
107067
+ " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n",
107068
+ bestJ, pTabList->a[bestJ].pTab->zName,
107069
+ pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow,
106967107070
bestPlan.plan.nOBSat, bestPlan.plan.wsFlags));
106968
- if( (bestPlan.plan.wsFlags & WHERE_ORDERBY)!=0 ){
106969
- pWInfo->nOBSat = pOrderBy->nExpr;
106970
- }
106971107071
if( (bestPlan.plan.wsFlags & WHERE_DISTINCT)!=0 ){
106972107072
assert( pWInfo->eDistinct==0 );
106973107073
pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
106974107074
}
106975107075
andFlags &= bestPlan.plan.wsFlags;
106976107076
pLevel->plan = bestPlan.plan;
107077
+ pLevel->iTabCur = pTabList->a[bestJ].iCursor;
106977107078
testcase( bestPlan.plan.wsFlags & WHERE_INDEXED );
106978107079
testcase( bestPlan.plan.wsFlags & WHERE_TEMP_INDEX );
106979107080
if( bestPlan.plan.wsFlags & (WHERE_INDEXED|WHERE_TEMP_INDEX) ){
106980107081
if( (wctrlFlags & WHERE_ONETABLE_ONLY)
106981107082
&& (bestPlan.plan.wsFlags & WHERE_TEMP_INDEX)==0
@@ -107013,15 +107114,22 @@
107013107114
}
107014107115
WHERETRACE(("*** Optimizer Finished ***\n"));
107015107116
if( pParse->nErr || db->mallocFailed ){
107016107117
goto whereBeginError;
107017107118
}
107119
+ if( nTabList ){
107120
+ pLevel--;
107121
+ pWInfo->nOBSat = pLevel->plan.nOBSat;
107122
+ }else{
107123
+ pWInfo->nOBSat = 0;
107124
+ }
107018107125
107019107126
/* If the total query only selects a single row, then the ORDER BY
107020107127
** clause is irrelevant.
107021107128
*/
107022107129
if( (andFlags & WHERE_UNIQUE)!=0 && pOrderBy ){
107130
+ assert( nTabList==0 || (pLevel->plan.wsFlags & WHERE_ALL_UNIQUE)!=0 );
107023107131
pWInfo->nOBSat = pOrderBy->nExpr;
107024107132
}
107025107133
107026107134
/* If the caller is an UPDATE or DELETE statement that is requesting
107027107135
** to use a one-pass algorithm, determine if this is appropriate.
@@ -107045,11 +107153,10 @@
107045107153
int iDb; /* Index of database containing table/index */
107046107154
struct SrcList_item *pTabItem;
107047107155
107048107156
pTabItem = &pTabList->a[pLevel->iFrom];
107049107157
pTab = pTabItem->pTab;
107050
- pLevel->iTabCur = pTabItem->iCursor;
107051107158
pWInfo->nRowOut *= pLevel->plan.nRow;
107052107159
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
107053107160
if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ){
107054107161
/* Do nothing */
107055107162
}else
@@ -114978,11 +115085,11 @@
114978115085
** with various optimizations disabled to verify that the same answer
114979115086
** is obtained in every case.
114980115087
*/
114981115088
case SQLITE_TESTCTRL_OPTIMIZATIONS: {
114982115089
sqlite3 *db = va_arg(ap, sqlite3*);
114983
- db->dbOptFlags = (u16)(va_arg(ap, int) & 0xffff);
115090
+ db->dbOptFlags = (u8)(va_arg(ap, int) & 0xff);
114984115091
break;
114985115092
}
114986115093
114987115094
#ifdef SQLITE_N_KEYWORD
114988115095
/* sqlite3_test_control(SQLITE_TESTCTRL_ISKEYWORD, const char *zWord)
@@ -135232,11 +135339,11 @@
135232135339
/*
135233135340
** Remove the entry with rowid=iDelete from the r-tree structure.
135234135341
*/
135235135342
static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){
135236135343
int rc; /* Return code */
135237
- RtreeNode *pLeaf; /* Leaf node containing record iDelete */
135344
+ RtreeNode *pLeaf = 0; /* Leaf node containing record iDelete */
135238135345
int iCell; /* Index of iDelete cell in pLeaf */
135239135346
RtreeNode *pRoot; /* Root node of rtree structure */
135240135347
135241135348
135242135349
/* Obtain a reference to the root node to initialise Rtree.iDepth */
@@ -135435,11 +135542,11 @@
135435135542
** (azData[2]..azData[argc-1]) contain a new record to insert into
135436135543
** the r-tree structure.
135437135544
*/
135438135545
if( rc==SQLITE_OK && nData>1 ){
135439135546
/* Insert the new record into the r-tree */
135440
- RtreeNode *pLeaf;
135547
+ RtreeNode *pLeaf = 0;
135441135548
135442135549
/* Figure out the rowid of the new row. */
135443135550
if( bHaveRowid==0 ){
135444135551
rc = newRowid(pRtree, &cell.iRowid);
135445135552
}
135446135553
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -673,11 +673,11 @@
673 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
674 ** [sqlite_version()] and [sqlite_source_id()].
675 */
676 #define SQLITE_VERSION "3.7.15"
677 #define SQLITE_VERSION_NUMBER 3007015
678 #define SQLITE_SOURCE_ID "2012-09-28 00:44:28 1e874629d7cf568368b912b295bd3001147d0b52"
679
680 /*
681 ** CAPI3REF: Run-Time Library Version Numbers
682 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
683 **
@@ -1421,10 +1421,21 @@
1421 ** that the VFS encountered an error while handling the [PRAGMA] and the
1422 ** compilation of the PRAGMA fails with an error. ^The [SQLITE_FCNTL_PRAGMA]
1423 ** file control occurs at the beginning of pragma statement analysis and so
1424 ** it is able to override built-in [PRAGMA] statements.
1425 ** </ul>
 
 
 
 
 
 
 
 
 
 
 
1426 */
1427 #define SQLITE_FCNTL_LOCKSTATE 1
1428 #define SQLITE_GET_LOCKPROXYFILE 2
1429 #define SQLITE_SET_LOCKPROXYFILE 3
1430 #define SQLITE_LAST_ERRNO 4
@@ -1436,10 +1447,11 @@
1436 #define SQLITE_FCNTL_PERSIST_WAL 10
1437 #define SQLITE_FCNTL_OVERWRITE 11
1438 #define SQLITE_FCNTL_VFSNAME 12
1439 #define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13
1440 #define SQLITE_FCNTL_PRAGMA 14
 
1441
1442 /*
1443 ** CAPI3REF: Mutex Handle
1444 **
1445 ** The mutex module within SQLite defines [sqlite3_mutex] to be an
@@ -8381,10 +8393,13 @@
8381 SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*);
8382 SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int);
8383 SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree*);
8384 SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int);
8385 SQLITE_PRIVATE int sqlite3BtreeGetReserve(Btree*);
 
 
 
8386 SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int);
8387 SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *);
8388 SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int);
8389 SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
8390 SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree*, int);
@@ -9150,10 +9165,11 @@
9150 SQLITE_PRIVATE int sqlite3PagerNosync(Pager*);
9151 SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*);
9152 SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*);
9153 SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *);
9154 SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *);
 
9155
9156 /* Functions used to truncate the database file. */
9157 SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno);
9158
9159 #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL)
@@ -25825,10 +25841,12 @@
25825 int prior = 0;
25826 #if (!defined(USE_PREAD) && !defined(USE_PREAD64))
25827 i64 newOffset;
25828 #endif
25829 TIMER_START;
 
 
25830 do{
25831 #if defined(USE_PREAD)
25832 got = osPread(id->h, pBuf, cnt, offset);
25833 SimulateIOError( got = -1 );
25834 #elif defined(USE_PREAD64)
@@ -25914,10 +25932,12 @@
25914 static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){
25915 int got;
25916 #if (!defined(USE_PREAD) && !defined(USE_PREAD64))
25917 i64 newOffset;
25918 #endif
 
 
25919 TIMER_START;
25920 #if defined(USE_PREAD)
25921 do{ got = osPwrite(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR );
25922 #elif defined(USE_PREAD64)
25923 do{ got = osPwrite64(id->h, pBuf, cnt, offset);}while( got<0 && errno==EINTR);
@@ -39558,10 +39578,25 @@
39558 }
39559 }
39560 }
39561 return rc;
39562 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39563
39564 /*
39565 ** Set the value of the Pager.sectorSize variable for the given
39566 ** pager based on the value returned by the xSectorSize method
39567 ** of the open database file. The sector size will be used used
@@ -39594,18 +39629,11 @@
39594 /* Sector size doesn't matter for temporary files. Also, the file
39595 ** may not have been opened yet, in which case the OsSectorSize()
39596 ** call will segfault. */
39597 pPager->sectorSize = 512;
39598 }else{
39599 pPager->sectorSize = sqlite3OsSectorSize(pPager->fd);
39600 if( pPager->sectorSize<32 ){
39601 pPager->sectorSize = 512;
39602 }
39603 if( pPager->sectorSize>MAX_SECTOR_SIZE ){
39604 assert( MAX_SECTOR_SIZE>=512 );
39605 pPager->sectorSize = MAX_SECTOR_SIZE;
39606 }
39607 }
39608 }
39609
39610 /*
39611 ** Playback the journal and thus restore the database file to
@@ -40518,13 +40546,20 @@
40518 */
40519 SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(
40520 Pager *pPager, /* Pager object */
40521 int (*xBusyHandler)(void *), /* Pointer to busy-handler function */
40522 void *pBusyHandlerArg /* Argument to pass to xBusyHandler */
40523 ){
40524 pPager->xBusyHandler = xBusyHandler;
40525 pPager->pBusyHandlerArg = pBusyHandlerArg;
 
 
 
 
 
 
 
40526 }
40527
40528 /*
40529 ** Change the page size used by the Pager object. The new page size
40530 ** is passed in *pPageSize.
@@ -46815,11 +46850,11 @@
46815 ** sector boundary is synced; the part of the last frame that extends
46816 ** past the sector boundary is written after the sync.
46817 */
46818 if( isCommit && (sync_flags & WAL_SYNC_TRANSACTIONS)!=0 ){
46819 if( pWal->padToSectorBoundary ){
46820 int sectorSize = sqlite3OsSectorSize(pWal->pWalFd);
46821 w.iSyncPoint = ((iOffset+sectorSize-1)/sectorSize)*sectorSize;
46822 while( iOffset<w.iSyncPoint ){
46823 rc = walWriteOneFrame(&w, pLast, nTruncate, iOffset);
46824 if( rc ) return rc;
46825 iOffset += szFrame;
@@ -50227,10 +50262,28 @@
50227 ** Return the currently defined page size
50228 */
50229 SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree *p){
50230 return p->pBt->pageSize;
50231 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50232
50233 #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM)
50234 /*
50235 ** Return the number of bytes of space at the end of every page that
50236 ** are intentually left unused. This is the "reserved" space that is
@@ -53284,11 +53337,11 @@
53284 btreeParseCellPtr(pPage, pCell, &info);
53285 if( info.iOverflow==0 ){
53286 return SQLITE_OK; /* No overflow pages. Return without doing anything */
53287 }
53288 if( pCell+info.iOverflow+3 > pPage->aData+pPage->maskPage ){
53289 return SQLITE_CORRUPT; /* Cell extends past end of page */
53290 }
53291 ovflPgno = get4byte(&pCell[info.iOverflow]);
53292 assert( pBt->usableSize > 4 );
53293 ovflPageSize = pBt->usableSize - 4;
53294 nOvfl = (info.nPayload - info.nLocal + ovflPageSize - 1)/ovflPageSize;
@@ -53950,10 +54003,13 @@
53950 ** enough for all overflow cells.
53951 **
53952 ** If aOvflSpace is set to a null pointer, this function returns
53953 ** SQLITE_NOMEM.
53954 */
 
 
 
53955 static int balance_nonroot(
53956 MemPage *pParent, /* Parent page of siblings being balanced */
53957 int iParentIdx, /* Index of "the page" in pParent */
53958 u8 *aOvflSpace, /* page-size bytes of space for parent ovfl */
53959 int isRoot, /* True if pParent is a root-page */
@@ -54580,10 +54636,13 @@
54580 releasePage(apNew[i]);
54581 }
54582
54583 return rc;
54584 }
 
 
 
54585
54586
54587 /*
54588 ** This function is called when the root page of a b-tree structure is
54589 ** overfull (has one or more overflow pages).
@@ -56558,17 +56617,20 @@
56558 const int nSrcPgsz = sqlite3BtreeGetPageSize(p->pSrc);
56559 int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest);
56560 const int nCopy = MIN(nSrcPgsz, nDestPgsz);
56561 const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz;
56562 #ifdef SQLITE_HAS_CODEC
56563 int nSrcReserve = sqlite3BtreeGetReserve(p->pSrc);
 
 
 
56564 int nDestReserve = sqlite3BtreeGetReserve(p->pDest);
56565 #endif
56566
56567 int rc = SQLITE_OK;
56568 i64 iOff;
56569
 
56570 assert( p->bDestLocked );
56571 assert( !isFatalError(p->rc) );
56572 assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) );
56573 assert( zSrcData );
56574
@@ -91600,10 +91662,11 @@
91600 */
91601 aFcntl[0] = 0;
91602 aFcntl[1] = zLeft;
91603 aFcntl[2] = zRight;
91604 aFcntl[3] = 0;
 
91605 rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_PRAGMA, (void*)aFcntl);
91606 if( rc==SQLITE_OK ){
91607 if( aFcntl[0] ){
91608 int mem = ++pParse->nMem;
91609 sqlite3VdbeAddOp4(v, OP_String8, 0, mem, 0, aFcntl[0], 0);
@@ -102123,14 +102186,15 @@
102123 #define WHERE_NOT_FULLSCAN 0x100f3000 /* Does not do a full table scan */
102124 #define WHERE_IN_ABLE 0x000f1000 /* Able to support an IN operator */
102125 #define WHERE_TOP_LIMIT 0x00100000 /* x<EXPR or x<=EXPR constraint */
102126 #define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */
102127 #define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and x<EXPR */
102128 #define WHERE_IDX_ONLY 0x00800000 /* Use index only - omit table */
102129 #define WHERE_ORDERBY 0x01000000 /* Output will appear in correct order */
102130 #define WHERE_REVERSE 0x02000000 /* Scan in reverse order */
102131 #define WHERE_UNIQUE 0x04000000 /* Selects no more than one row */
 
102132 #define WHERE_VIRTUALTABLE 0x08000000 /* Use virtual-table processing */
102133 #define WHERE_MULTI_OR 0x10000000 /* OR using multiple indices */
102134 #define WHERE_TEMP_INDEX 0x20000000 /* Uses an ephemeral index */
102135 #define WHERE_DISTINCT 0x40000000 /* Correct order for DISTINCT */
102136 #define WHERE_COVER_SCAN 0x80000000 /* Full scan of a covering index */
@@ -102154,10 +102218,21 @@
102154 sqlite3_index_info **ppIdxInfo; /* Index information passed to xBestIndex */
102155 int i, n; /* Which loop is being coded; # of loops */
102156 WhereLevel *aLevel; /* Info about outer loops */
102157 WhereCost cost; /* Lowest cost query plan */
102158 };
 
 
 
 
 
 
 
 
 
 
 
102159
102160 /*
102161 ** Initialize a preallocated WhereClause structure.
102162 */
102163 static void whereClauseInit(
@@ -103306,11 +103381,12 @@
103306 Table *pTab = pIdx->pTable;
103307 int i;
103308 if( pIdx->onError==OE_None ) return 0;
103309 for(i=nSkip; i<pIdx->nColumn; i++){
103310 int j = pIdx->aiColumn[i];
103311 if( j>=0 && pTab->aCol[j].notNull==0 ) return 0;
 
103312 }
103313 return 1;
103314 }
103315
103316 /*
@@ -103370,11 +103446,12 @@
103370 int nEqCol /* Number of index columns with == */
103371 ){
103372 Bitmask mask = 0; /* Mask of unaccounted for pDistinct exprs */
103373 int i; /* Iterator variable */
103374
103375 if( pIdx->zName==0 || pDistinct==0 || pDistinct->nExpr>=BMS ) return 0;
 
103376 testcase( pDistinct->nExpr==BMS-1 );
103377
103378 /* Loop through all the expressions in the distinct list. If any of them
103379 ** are not simple column references, return early. Otherwise, test if the
103380 ** WHERE clause contains a "col=X" clause. If it does, the expression
@@ -103472,170 +103549,10 @@
103472 }
103473
103474 return 0;
103475 }
103476
103477 /*
103478 ** This routine decides if pIdx can be used to satisfy the ORDER BY
103479 ** clause, either in whole or in part. The return value is the
103480 ** cumulative number of terms in the ORDER BY clause that are satisfied
103481 ** by the index pIdx and other indices in outer loops.
103482 **
103483 ** The table being queried has a cursor number of "base". pIdx is the
103484 ** index that is postulated for use to access the table.
103485 **
103486 ** nEqCol is the number of columns of pIdx that are used as equality
103487 ** constraints and where the other side of the == is an ordered column
103488 ** or constant. An "order column" in the previous sentence means a column
103489 ** in table from an outer loop whose values will always appear in the
103490 ** correct order due to othre index, or because the outer loop generates
103491 ** a unique result. Any of the first nEqCol columns of pIdx may be missing
103492 ** from the ORDER BY clause and the match can still be a success.
103493 **
103494 ** The *pbRev value is set to 0 order 1 depending on whether or not
103495 ** pIdx should be run in the forward order or in reverse order.
103496 */
103497 static int isSortingIndex(
103498 WhereBestIdx *p, /* Best index search context */
103499 Index *pIdx, /* The index we are testing */
103500 int base, /* Cursor number for the table to be sorted */
103501 int nEqCol, /* Number of index columns with ordered == constraints */
103502 int wsFlags, /* Index usages flags */
103503 int bOuterRev, /* True if outer loops scan in reverse order */
103504 int *pbRev /* Set to 1 for reverse-order scan of pIdx */
103505 ){
103506 int i; /* Number of pIdx terms used */
103507 int j; /* Number of ORDER BY terms satisfied */
103508 int sortOrder = 0; /* XOR of index and ORDER BY sort direction */
103509 int nTerm; /* Number of ORDER BY terms */
103510 struct ExprList_item *pTerm; /* A term of the ORDER BY clause */
103511 ExprList *pOrderBy; /* The ORDER BY clause */
103512 Parse *pParse = p->pParse; /* Parser context */
103513 sqlite3 *db = pParse->db; /* Database connection */
103514 int nPriorSat; /* ORDER BY terms satisfied by outer loops */
103515 int seenRowid = 0; /* True if an ORDER BY rowid term is seen */
103516 int nEqOneRow; /* Idx columns that ref unique values */
103517
103518 if( p->i==0 ){
103519 nPriorSat = 0;
103520 nEqOneRow = nEqCol;
103521 }else{
103522 if( OptimizationDisabled(db, SQLITE_OrderByIdxJoin) ) return 0;
103523 nPriorSat = p->aLevel[p->i-1].plan.nOBSat;
103524 sortOrder = bOuterRev;
103525 nEqOneRow = 0;
103526 }
103527 if( p->i>0 && nEqCol==0 /*&& !allOuterLoopsUnique(p)*/ ) return nPriorSat;
103528 pOrderBy = p->pOrderBy;
103529 if( !pOrderBy ) return nPriorSat;
103530 if( wsFlags & WHERE_COLUMN_IN ) return nPriorSat;
103531 if( pIdx->bUnordered ) return nPriorSat;
103532 nTerm = pOrderBy->nExpr;
103533 assert( nTerm>0 );
103534
103535 /* Argument pIdx must either point to a 'real' named index structure,
103536 ** or an index structure allocated on the stack by bestBtreeIndex() to
103537 ** represent the rowid index that is part of every table. */
103538 assert( pIdx->zName || (pIdx->nColumn==1 && pIdx->aiColumn[0]==-1) );
103539
103540 /* Match terms of the ORDER BY clause against columns of
103541 ** the index.
103542 **
103543 ** Note that indices have pIdx->nColumn regular columns plus
103544 ** one additional column containing the rowid. The rowid column
103545 ** of the index is also allowed to match against the ORDER BY
103546 ** clause.
103547 */
103548 for(i=0,j=nPriorSat,pTerm=&pOrderBy->a[j]; j<nTerm && i<=pIdx->nColumn; i++){
103549 Expr *pExpr; /* The expression of the ORDER BY pTerm */
103550 CollSeq *pColl; /* The collating sequence of pExpr */
103551 int termSortOrder; /* Sort order for this term */
103552 int iColumn; /* The i-th column of the index. -1 for rowid */
103553 int iSortOrder; /* 1 for DESC, 0 for ASC on the i-th index term */
103554 const char *zColl; /* Name of the collating sequence for i-th index term */
103555
103556 pExpr = pTerm->pExpr;
103557 if( pExpr->op!=TK_COLUMN || pExpr->iTable!=base ){
103558 /* Can not use an index sort on anything that is not a column in the
103559 ** left-most table of the FROM clause */
103560 break;
103561 }
103562 pColl = sqlite3ExprCollSeq(pParse, pExpr);
103563 if( !pColl ){
103564 pColl = db->pDfltColl;
103565 }
103566 if( pIdx->zName && i<pIdx->nColumn ){
103567 iColumn = pIdx->aiColumn[i];
103568 if( iColumn==pIdx->pTable->iPKey ){
103569 iColumn = -1;
103570 }
103571 iSortOrder = pIdx->aSortOrder[i];
103572 zColl = pIdx->azColl[i];
103573 }else{
103574 iColumn = -1;
103575 iSortOrder = 0;
103576 zColl = pColl->zName;
103577 }
103578 if( pExpr->iColumn!=iColumn || sqlite3StrICmp(pColl->zName, zColl) ){
103579 /* Term j of the ORDER BY clause does not match column i of the index */
103580 if( i<nEqCol ){
103581 /* If an index column that is constrained by == fails to match an
103582 ** ORDER BY term, that is OK. Just ignore that column of the index
103583 */
103584 continue;
103585 }else if( i==pIdx->nColumn ){
103586 /* Index column i is the rowid. All other terms match. */
103587 break;
103588 }else{
103589 /* If an index column fails to match and is not constrained by ==
103590 ** then the index cannot satisfy the ORDER BY constraint.
103591 */
103592 return nPriorSat;
103593 }
103594 }
103595 assert( pIdx->aSortOrder!=0 || iColumn==-1 );
103596 assert( pTerm->sortOrder==0 || pTerm->sortOrder==1 );
103597 assert( iSortOrder==0 || iSortOrder==1 );
103598 termSortOrder = iSortOrder ^ pTerm->sortOrder;
103599 if( i>nEqOneRow ){
103600 if( termSortOrder!=sortOrder ){
103601 /* Indices can only be used if all ORDER BY terms past the
103602 ** equality constraints are all either DESC or ASC. */
103603 break;
103604 }
103605 }else{
103606 sortOrder = termSortOrder;
103607 }
103608 j++;
103609 pTerm++;
103610 if( iColumn<0 ){
103611 seenRowid = 1;
103612 break;
103613 }
103614 }
103615 *pbRev = sortOrder;
103616
103617 /* If there was an "ORDER BY rowid" term that matched, or it is only
103618 ** possible for a single row from this table to match, then skip over
103619 ** any additional ORDER BY terms dealing with this table.
103620 */
103621 if( seenRowid ||
103622 ( (wsFlags & WHERE_COLUMN_NULL)==0
103623 && i>=pIdx->nColumn
103624 && indexIsUniqueNotNull(pIdx, nEqCol)
103625 )
103626 ){
103627 /* Advance j over additional ORDER BY terms associated with base */
103628 WhereMaskSet *pMS = p->pWC->pMaskSet;
103629 Bitmask m = ~getMask(pMS, base);
103630 while( j<nTerm && (exprTableUsage(pMS, pOrderBy->a[j].pExpr)&m)==0 ){
103631 j++;
103632 }
103633 }
103634 return j;
103635 }
103636
103637 /*
103638 ** Prepare a crude estimate of the logarithm of the input value.
103639 ** The results need not be exact. This is only used for estimating
103640 ** the total cost of performing operations with O(logN) or O(NlogN)
103641 ** complexity. Because N is just a guess, it is no great tragedy if
@@ -103785,10 +103702,11 @@
103785 WHERETRACE(("... multi-index OR cost=%.9g nrow=%.9g\n", rTotal, nRow));
103786 if( rTotal<p->cost.rCost ){
103787 p->cost.rCost = rTotal;
103788 p->cost.used = used;
103789 p->cost.plan.nRow = nRow;
 
103790 p->cost.plan.wsFlags = flags;
103791 p->cost.plan.u.pTerm = pTerm;
103792 }
103793 }
103794 }
@@ -104327,11 +104245,14 @@
104327 }else{
104328 p->cost.rCost = rCost;
104329 }
104330 p->cost.plan.u.pVtabIdx = pIdxInfo;
104331 if( pIdxInfo->orderByConsumed ){
104332 p->cost.plan.wsFlags |= WHERE_ORDERBY;
 
 
 
104333 }
104334 p->cost.plan.nEq = 0;
104335 pIdxInfo->nOrderBy = nOrderBy;
104336
104337 /* Try to find a more efficient access pattern by using multiple indexes
@@ -104750,20 +104671,26 @@
104750 WhereLevel *pLevel = &p->aLevel[p->i-1];
104751 Index *pIdx;
104752 u8 sortOrder;
104753 for(i=p->i-1; i>=0; i--, pLevel--){
104754 if( pLevel->iTabCur!=iTab ) continue;
104755 if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){
104756 pIdx = pLevel->plan.u.pIdx;
 
 
 
 
 
104757 if( iCol<0 ){
104758 sortOrder = 0;
104759 testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 );
104760 }else{
104761 for(j=0; j<pIdx->nColumn; j++){
 
104762 if( iCol==pIdx->aiColumn[j] ) break;
104763 }
104764 if( j>=pIdx->nColumn ) return 0;
104765 sortOrder = pIdx->aSortOrder[j];
104766 testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 );
104767 }
104768 }else{
104769 if( iCol!=(-1) ) return 0;
@@ -104792,13 +104719,10 @@
104792 static int isOrderedTerm(WhereBestIdx *p, WhereTerm *pTerm, int *pbRev){
104793 Expr *pExpr = pTerm->pExpr;
104794 assert( pExpr->op==TK_EQ );
104795 assert( pExpr->pLeft!=0 && pExpr->pLeft->op==TK_COLUMN );
104796 assert( pExpr->pRight!=0 );
104797 if( p->i==0 ){
104798 return 1; /* All == are ordered in the outer loop */
104799 }
104800 if( pTerm->prereqRight==0 ){
104801 return 1; /* RHS of the == is a constant */
104802 }
104803 if( pExpr->pRight->op==TK_COLUMN
104804 && isOrderedColumn(p, pExpr->pRight->iTable, pExpr->pRight->iColumn, pbRev)
@@ -104808,10 +104732,177 @@
104808
104809 /* If we cannot prove that the constraint is ordered, assume it is not */
104810 return 0;
104811 }
104812
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104813
104814 /*
104815 ** Find the best query plan for accessing a particular table. Write the
104816 ** best query plan and its cost into the p->cost.
104817 **
@@ -104902,22 +104993,19 @@
104902
104903 /* Loop over all indices looking for the best one to use
104904 */
104905 for(; pProbe; pIdx=pProbe=pProbe->pNext){
104906 const tRowcnt * const aiRowEst = pProbe->aiRowEst;
104907 double cost; /* Cost of using pProbe */
104908 double nRow; /* Estimated number of rows in result set */
104909 double log10N = (double)1; /* base-10 logarithm of nRow (inexact) */
104910 int bRev = 2; /* 0=forward scan. 1=reverse. 2=undecided */
104911 int wsFlags = 0;
104912 Bitmask used = 0;
104913
104914 /* The following variables are populated based on the properties of
104915 ** index being evaluated. They are then used to determine the expected
104916 ** cost and number of rows returned.
104917 **
104918 ** nEq:
104919 ** Number of equality terms that can be implemented using the index.
104920 ** In other words, the number of initial fields in the index that
104921 ** are used in == or IN or NOT NULL constraints of the WHERE clause.
104922 **
104923 ** nInMul:
@@ -104960,11 +105048,11 @@
104960 ** bSort:
104961 ** Boolean. True if there is an ORDER BY clause that will require an
104962 ** external sort (i.e. scanning the index being evaluated will not
104963 ** correctly order records).
104964 **
104965 ** bDistinct:
104966 ** Boolean. True if there is a DISTINCT clause that will require an
104967 ** external btree.
104968 **
104969 ** bLookup:
104970 ** Boolean. True if a table lookup is required for each index entry
@@ -104979,132 +105067,148 @@
104979 ** both available in the index.
104980 **
104981 ** SELECT a, b FROM tbl WHERE a = 1;
104982 ** SELECT a, b, c FROM tbl WHERE a = 1;
104983 */
104984 int nEq; /* Number of == or IN terms matching index */
104985 int nOrdered; /* Number of ordered terms matching index */
104986 int bInEst = 0; /* True if "x IN (SELECT...)" seen */
104987 int nInMul = 1; /* Number of distinct equalities to lookup */
104988 double rangeDiv = (double)1; /* Estimated reduction in search space */
104989 int nBound = 0; /* Number of range constraints seen */
104990 int bSort; /* True if external sort required */
104991 int bDist; /* True if index cannot help with DISTINCT */
104992 int bLookup = 0; /* True if not a covering index */
104993 int nOBSat = 0; /* Number of ORDER BY terms satisfied */
104994 int nOrderBy; /* Number of ORDER BY terms */
104995 WhereTerm *pTerm; /* A single term of the WHERE clause */
104996 #ifdef SQLITE_ENABLE_STAT3
104997 WhereTerm *pFirstTerm = 0; /* First term matching the index */
104998 #endif
104999
 
105000 nOrderBy = p->pOrderBy ? p->pOrderBy->nExpr : 0;
105001 bSort = nOrderBy>0 && (p->i==0 || p->aLevel[p->i-1].plan.nOBSat<nOrderBy);
105002 bDist = p->i==0 && p->pDistinct!=0;
 
 
 
 
 
 
 
105003
105004 /* Determine the values of nEq and nInMul */
105005 for(nEq=nOrdered=0; nEq<pProbe->nColumn; nEq++){
105006 int j = pProbe->aiColumn[nEq];
105007 pTerm = findTerm(pWC, iCur, j, p->notReady, eqTermMask, pIdx);
105008 if( pTerm==0 ) break;
105009 wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ);
105010 testcase( pTerm->pWC!=pWC );
105011 if( pTerm->eOperator & WO_IN ){
105012 Expr *pExpr = pTerm->pExpr;
105013 wsFlags |= WHERE_COLUMN_IN;
105014 if( ExprHasProperty(pExpr, EP_xIsSelect) ){
105015 /* "x IN (SELECT ...)": Assume the SELECT returns 25 rows */
105016 nInMul *= 25;
105017 bInEst = 1;
105018 }else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){
105019 /* "x IN (value, value, ...)" */
105020 nInMul *= pExpr->x.pList->nExpr;
105021 }
105022 }else if( pTerm->eOperator & WO_ISNULL ){
105023 wsFlags |= WHERE_COLUMN_NULL;
105024 if( nEq==nOrdered ) nOrdered++;
105025 }else if( bSort && nEq==nOrdered && isOrderedTerm(p, pTerm, &bRev) ){
 
105026 nOrdered++;
105027 }
105028 #ifdef SQLITE_ENABLE_STAT3
105029 if( nEq==0 && pProbe->aSample ) pFirstTerm = pTerm;
105030 #endif
105031 used |= pTerm->prereqRight;
105032 }
105033
105034 /* If the index being considered is UNIQUE, and there is an equality
105035 ** constraint for all columns in the index, then this search will find
105036 ** at most a single row. In this case set the WHERE_UNIQUE flag to
105037 ** indicate this to the caller.
105038 **
105039 ** Otherwise, if the search may find more than one row, test to see if
105040 ** there is a range constraint on indexed column (nEq+1) that can be
105041 ** optimized using the index.
105042 */
105043 if( nEq==pProbe->nColumn && pProbe->onError!=OE_None ){
105044 testcase( wsFlags & WHERE_COLUMN_IN );
105045 testcase( wsFlags & WHERE_COLUMN_NULL );
105046 if( (wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){
105047 wsFlags |= WHERE_UNIQUE;
 
 
 
105048 }
105049 }else if( pProbe->bUnordered==0 ){
105050 int j = (nEq==pProbe->nColumn ? -1 : pProbe->aiColumn[nEq]);
 
105051 if( findTerm(pWC, iCur, j, p->notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){
105052 WhereTerm *pTop, *pBtm;
105053 pTop = findTerm(pWC, iCur, j, p->notReady, WO_LT|WO_LE, pIdx);
105054 pBtm = findTerm(pWC, iCur, j, p->notReady, WO_GT|WO_GE, pIdx);
105055 whereRangeScanEst(pParse, pProbe, nEq, pBtm, pTop, &rangeDiv);
105056 if( pTop ){
105057 nBound = 1;
105058 wsFlags |= WHERE_TOP_LIMIT;
105059 used |= pTop->prereqRight;
105060 testcase( pTop->pWC!=pWC );
105061 }
105062 if( pBtm ){
105063 nBound++;
105064 wsFlags |= WHERE_BTM_LIMIT;
105065 used |= pBtm->prereqRight;
105066 testcase( pBtm->pWC!=pWC );
105067 }
105068 wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE);
105069 }
105070 }
105071
105072 /* If there is an ORDER BY clause and the index being considered will
105073 ** naturally scan rows in the required order, set the appropriate flags
105074 ** in wsFlags. Otherwise, if there is an ORDER BY clause but the index
105075 ** will scan rows in a different order, set the bSort variable. */
 
105076 assert( bRev>=0 && bRev<=2 );
105077 if( bSort ){
105078 testcase( bRev==0 );
105079 testcase( bRev==1 );
105080 testcase( bRev==2 );
105081 nOBSat = isSortingIndex(p, pProbe, iCur, nOrdered,
105082 wsFlags, bRev&1, &bRev);
105083 if( nOrderBy==nOBSat ){
 
 
 
105084 bSort = 0;
105085 wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_ORDERBY;
105086 }
105087 if( bRev & 1 ) wsFlags |= WHERE_REVERSE;
105088 }
105089
105090 /* If there is a DISTINCT qualifier and this index will scan rows in
105091 ** order of the DISTINCT expressions, clear bDist and set the appropriate
105092 ** flags in wsFlags. */
105093 if( bDist
105094 && isDistinctIndex(pParse, pWC, pProbe, iCur, p->pDistinct, nEq)
105095 && (wsFlags & WHERE_COLUMN_IN)==0
105096 ){
105097 bDist = 0;
105098 wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_DISTINCT;
105099 }
105100
105101 /* If currently calculating the cost of using an index (not the IPK
105102 ** index), determine if all required column data may be obtained without
105103 ** using the main table (i.e. if the index is a covering
105104 ** index for this query). If it is, set the WHERE_IDX_ONLY flag in
105105 ** wsFlags. Otherwise, set the bLookup variable to true. */
105106 if( pIdx ){
105107 Bitmask m = pSrc->colUsed;
105108 int j;
105109 for(j=0; j<pIdx->nColumn; j++){
105110 int x = pIdx->aiColumn[j];
@@ -105111,51 +105215,54 @@
105111 if( x<BMS-1 ){
105112 m &= ~(((Bitmask)1)<<x);
105113 }
105114 }
105115 if( m==0 ){
105116 wsFlags |= WHERE_IDX_ONLY;
105117 }else{
105118 bLookup = 1;
105119 }
105120 }
105121
105122 /*
105123 ** Estimate the number of rows of output. For an "x IN (SELECT...)"
105124 ** constraint, do not let the estimate exceed half the rows in the table.
105125 */
105126 nRow = (double)(aiRowEst[nEq] * nInMul);
105127 if( bInEst && nRow*2>aiRowEst[0] ){
105128 nRow = aiRowEst[0]/2;
105129 nInMul = (int)(nRow / aiRowEst[nEq]);
105130 }
105131
105132 #ifdef SQLITE_ENABLE_STAT3
105133 /* If the constraint is of the form x=VALUE or x IN (E1,E2,...)
105134 ** and we do not think that values of x are unique and if histogram
105135 ** data is available for column x, then it might be possible
105136 ** to get a better estimate on the number of rows based on
105137 ** VALUE and how common that value is according to the histogram.
105138 */
105139 if( nRow>(double)1 && nEq==1 && pFirstTerm!=0 && aiRowEst[1]>1 ){
 
105140 assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 );
105141 if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){
105142 testcase( pFirstTerm->eOperator==WO_EQ );
105143 testcase( pFirstTerm->eOperator==WO_ISNULL );
105144 whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, &nRow);
 
105145 }else if( bInEst==0 ){
105146 assert( pFirstTerm->eOperator==WO_IN );
105147 whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, &nRow);
 
105148 }
105149 }
105150 #endif /* SQLITE_ENABLE_STAT3 */
105151
105152 /* Adjust the number of output rows and downward to reflect rows
105153 ** that are excluded by range constraints.
105154 */
105155 nRow = nRow/rangeDiv;
105156 if( nRow<1 ) nRow = 1;
105157
105158 /* Experiments run on real SQLite databases show that the time needed
105159 ** to do a binary search to locate a row in a table or index is roughly
105160 ** log10(N) times the time to move from one row to the next row within
105161 ** a table or index. The actual times can vary, with the size of
@@ -105166,57 +105273,58 @@
105166 ** The ANALYZE command and the sqlite_stat1 and sqlite_stat3 tables do
105167 ** not give us data on the relative sizes of table and index records.
105168 ** So this computation assumes table records are about twice as big
105169 ** as index records
105170 */
105171 if( (wsFlags&~WHERE_REVERSE)==WHERE_IDX_ONLY
105172 && (pWC->wctrlFlags & WHERE_ONEPASS_DESIRED)==0
105173 && sqlite3GlobalConfig.bUseCis
105174 && OptimizationEnabled(pParse->db, SQLITE_CoverIdxScan)
105175 ){
105176 /* This index is not useful for indexing, but it is a covering index.
105177 ** A full-scan of the index might be a little faster than a full-scan
105178 ** of the table, so give this case a cost slightly less than a table
105179 ** scan. */
105180 cost = aiRowEst[0]*3 + pProbe->nColumn;
105181 wsFlags |= WHERE_COVER_SCAN|WHERE_COLUMN_RANGE;
105182 }else if( (wsFlags & WHERE_NOT_FULLSCAN)==0 ){
105183 /* The cost of a full table scan is a number of move operations equal
105184 ** to the number of rows in the table.
105185 **
105186 ** We add an additional 4x penalty to full table scans. This causes
105187 ** the cost function to err on the side of choosing an index over
105188 ** choosing a full scan. This 4x full-scan penalty is an arguable
105189 ** decision and one which we expect to revisit in the future. But
105190 ** it seems to be working well enough at the moment.
105191 */
105192 cost = aiRowEst[0]*4;
105193 wsFlags &= ~WHERE_IDX_ONLY;
 
105194 }else{
105195 log10N = estLog(aiRowEst[0]);
105196 cost = nRow;
105197 if( pIdx ){
105198 if( bLookup ){
105199 /* For an index lookup followed by a table lookup:
105200 ** nInMul index searches to find the start of each index range
105201 ** + nRow steps through the index
105202 ** + nRow table searches to lookup the table entry using the rowid
105203 */
105204 cost += (nInMul + nRow)*log10N;
105205 }else{
105206 /* For a covering index:
105207 ** nInMul index searches to find the initial entry
105208 ** + nRow steps through the index
105209 */
105210 cost += nInMul*log10N;
105211 }
105212 }else{
105213 /* For a rowid primary key lookup:
105214 ** nInMult table searches to find the initial entry for each range
105215 ** + nRow steps through the table
105216 */
105217 cost += nInMul*log10N;
105218 }
105219 }
105220
105221 /* Add in the estimated cost of sorting the result. Actual experimental
105222 ** measurements of sorting performance in SQLite show that sorting time
@@ -105223,14 +105331,16 @@
105223 ** adds C*N*log10(N) to the cost, where N is the number of rows to be
105224 ** sorted and C is a factor between 1.95 and 4.3. We will split the
105225 ** difference and select C of 3.0.
105226 */
105227 if( bSort ){
105228 cost += nRow*estLog(nRow*(nOrderBy - nOBSat)/nOrderBy)*3;
 
 
105229 }
105230 if( bDist ){
105231 cost += nRow*estLog(nRow)*3;
105232 }
105233
105234 /**** Cost of using this index has now been computed ****/
105235
105236 /* If there are additional constraints on this table that cannot
@@ -105247,29 +105357,29 @@
105247 ** tables that are not in outer loops. If notReady is used here instead
105248 ** of notValid, then a optimal index that depends on inner joins loops
105249 ** might be selected even when there exists an optimal index that has
105250 ** no such dependency.
105251 */
105252 if( nRow>2 && cost<=p->cost.rCost ){
105253 int k; /* Loop counter */
105254 int nSkipEq = nEq; /* Number of == constraints to skip */
105255 int nSkipRange = nBound; /* Number of < constraints to skip */
105256 Bitmask thisTab; /* Bitmap for pSrc */
105257
105258 thisTab = getMask(pWC->pMaskSet, iCur);
105259 for(pTerm=pWC->a, k=pWC->nTerm; nRow>2 && k; k--, pTerm++){
105260 if( pTerm->wtFlags & TERM_VIRTUAL ) continue;
105261 if( (pTerm->prereqAll & p->notValid)!=thisTab ) continue;
105262 if( pTerm->eOperator & (WO_EQ|WO_IN|WO_ISNULL) ){
105263 if( nSkipEq ){
105264 /* Ignore the first nEq equality matches since the index
105265 ** has already accounted for these */
105266 nSkipEq--;
105267 }else{
105268 /* Assume each additional equality match reduces the result
105269 ** set size by a factor of 10 */
105270 nRow /= 10;
105271 }
105272 }else if( pTerm->eOperator & (WO_LT|WO_LE|WO_GT|WO_GE) ){
105273 if( nSkipRange ){
105274 /* Ignore the first nSkipRange range constraints since the index
105275 ** has already accounted for these */
@@ -105279,43 +105389,38 @@
105279 ** set size by a factor of 3. Indexed range constraints reduce
105280 ** the search space by a larger factor: 4. We make indexed range
105281 ** more selective intentionally because of the subjective
105282 ** observation that indexed range constraints really are more
105283 ** selective in practice, on average. */
105284 nRow /= 3;
105285 }
105286 }else if( pTerm->eOperator!=WO_NOOP ){
105287 /* Any other expression lowers the output row count by half */
105288 nRow /= 2;
105289 }
105290 }
105291 if( nRow<2 ) nRow = 2;
105292 }
105293
105294
105295 WHERETRACE((
105296 "%s(%s):\n"
105297 " nEq=%d nInMul=%d rangeDiv=%d bSort=%d bLookup=%d wsFlags=0x%08x\n"
105298 " notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f\n"
105299 " used=0x%llx nOrdered=%d nOBSat=%d\n",
105300 pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk"),
105301 nEq, nInMul, (int)rangeDiv, bSort, bLookup, wsFlags,
105302 p->notReady, log10N, nRow, cost, used, nOrdered, nOBSat
 
105303 ));
105304
105305 /* If this index is the best we have seen so far, then record this
105306 ** index and its cost in the pCost structure.
105307 */
105308 if( (!pIdx || wsFlags)
105309 && (cost<p->cost.rCost || (cost<=p->cost.rCost && nRow<p->cost.plan.nRow))
105310 ){
105311 p->cost.rCost = cost;
105312 p->cost.used = used;
105313 p->cost.plan.nRow = nRow;
105314 p->cost.plan.wsFlags = (wsFlags&wsFlagMask);
105315 p->cost.plan.nEq = nEq;
105316 p->cost.plan.nOBSat = nOBSat;
105317 p->cost.plan.u.pIdx = pIdx;
105318 }
105319
105320 /* If there was an INDEXED BY clause, then only that one index is
105321 ** considered. */
@@ -105333,21 +105438,19 @@
105333 ** SQLite outputs rows in in the absence of an ORDER BY clause. */
105334 if( !p->pOrderBy && pParse->db->flags & SQLITE_ReverseOrder ){
105335 p->cost.plan.wsFlags |= WHERE_REVERSE;
105336 }
105337
105338 assert( p->pOrderBy || (p->cost.plan.wsFlags&WHERE_ORDERBY)==0 );
105339 assert( p->cost.plan.u.pIdx==0 || (p->cost.plan.wsFlags&WHERE_ROWID_EQ)==0 );
105340 assert( pSrc->pIndex==0
105341 || p->cost.plan.u.pIdx==0
105342 || p->cost.plan.u.pIdx==pSrc->pIndex
105343 );
105344
105345 WHERETRACE(("best index is: %s\n",
105346 ((p->cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ? "none" :
105347 p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk")
105348 ));
105349
105350 bestOrClauseIndex(p);
105351 bestAutomaticIndex(p);
105352 p->cost.plan.wsFlags |= eqTermMask;
105353 }
@@ -106071,11 +106174,11 @@
106071 ** should not have a NULL value stored in 'x'. If column 'x' is
106072 ** the first one after the nEq equality constraints in the index,
106073 ** this requires some special handling.
106074 */
106075 if( (wctrlFlags&WHERE_ORDERBY_MIN)!=0
106076 && (pLevel->plan.wsFlags&WHERE_ORDERBY)
106077 && (pIdx->nColumn>nEq)
106078 ){
106079 /* assert( pOrderBy->nExpr==1 ); */
106080 /* assert( pOrderBy->a[0].pExpr->iColumn==pIdx->aiColumn[nEq] ); */
106081 isMinQuery = 1;
@@ -106892,12 +106995,12 @@
106892 continue;
106893 }
106894 sWBI.notReady = (isOptimal ? m : sWBI.notValid);
106895 if( sWBI.pSrc->pIndex==0 ) nUnconstrained++;
106896
106897 WHERETRACE(("=== trying table %d with isOptimal=%d ===\n",
106898 j, isOptimal));
106899 assert( sWBI.pSrc->pTab );
106900 #ifndef SQLITE_OMIT_VIRTUALTABLE
106901 if( IsVirtual(sWBI.pSrc->pTab) ){
106902 sWBI.ppIdxInfo = &pWInfo->a[j].pIdxInfo;
106903 bestVirtualIndex(&sWBI);
@@ -106934,48 +107037,46 @@
106934 ** combination of INDEXED BY clauses are given. The error
106935 ** will be detected and relayed back to the application later.
106936 ** The NEVER() comes about because rule (2) above prevents
106937 ** An indexable full-table-scan from reaching rule (3).
106938 **
106939 ** (4) The plan cost must be lower than prior plans or else the
106940 ** cost must be the same and the number of rows must be lower.
106941 */
106942 if( (sWBI.cost.used&sWBI.notValid)==0 /* (1) */
106943 && (bestJ<0 || (notIndexed&m)!=0 /* (2) */
106944 || (bestPlan.plan.wsFlags & WHERE_NOT_FULLSCAN)==0
106945 || (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)
106946 && (nUnconstrained==0 || sWBI.pSrc->pIndex==0 /* (3) */
106947 || NEVER((sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0))
106948 && (bestJ<0 || sWBI.cost.rCost<bestPlan.rCost /* (4) */
106949 || (sWBI.cost.rCost<=bestPlan.rCost
106950 && sWBI.cost.plan.nRow<bestPlan.plan.nRow))
106951 ){
106952 WHERETRACE(("=== table %d is best so far"
106953 " with cost=%.1f, nRow=%.1f, nOBSat=%d\n",
106954 j, sWBI.cost.rCost, sWBI.cost.plan.nRow,
106955 sWBI.cost.plan.nOBSat));
 
106956 bestPlan = sWBI.cost;
106957 bestJ = j;
106958 }
106959 if( doNotReorder ) break;
106960 }
106961 }
106962 assert( bestJ>=0 );
106963 assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) );
106964 WHERETRACE(("*** Optimizer selects table %d for loop %d with:\n"
106965 " cost=%.1f, nRow=%.1f, nOBSat=%d wsFlags=0x%08x\n",
106966 bestJ, pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow,
 
106967 bestPlan.plan.nOBSat, bestPlan.plan.wsFlags));
106968 if( (bestPlan.plan.wsFlags & WHERE_ORDERBY)!=0 ){
106969 pWInfo->nOBSat = pOrderBy->nExpr;
106970 }
106971 if( (bestPlan.plan.wsFlags & WHERE_DISTINCT)!=0 ){
106972 assert( pWInfo->eDistinct==0 );
106973 pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
106974 }
106975 andFlags &= bestPlan.plan.wsFlags;
106976 pLevel->plan = bestPlan.plan;
 
106977 testcase( bestPlan.plan.wsFlags & WHERE_INDEXED );
106978 testcase( bestPlan.plan.wsFlags & WHERE_TEMP_INDEX );
106979 if( bestPlan.plan.wsFlags & (WHERE_INDEXED|WHERE_TEMP_INDEX) ){
106980 if( (wctrlFlags & WHERE_ONETABLE_ONLY)
106981 && (bestPlan.plan.wsFlags & WHERE_TEMP_INDEX)==0
@@ -107013,15 +107114,22 @@
107013 }
107014 WHERETRACE(("*** Optimizer Finished ***\n"));
107015 if( pParse->nErr || db->mallocFailed ){
107016 goto whereBeginError;
107017 }
 
 
 
 
 
 
107018
107019 /* If the total query only selects a single row, then the ORDER BY
107020 ** clause is irrelevant.
107021 */
107022 if( (andFlags & WHERE_UNIQUE)!=0 && pOrderBy ){
 
107023 pWInfo->nOBSat = pOrderBy->nExpr;
107024 }
107025
107026 /* If the caller is an UPDATE or DELETE statement that is requesting
107027 ** to use a one-pass algorithm, determine if this is appropriate.
@@ -107045,11 +107153,10 @@
107045 int iDb; /* Index of database containing table/index */
107046 struct SrcList_item *pTabItem;
107047
107048 pTabItem = &pTabList->a[pLevel->iFrom];
107049 pTab = pTabItem->pTab;
107050 pLevel->iTabCur = pTabItem->iCursor;
107051 pWInfo->nRowOut *= pLevel->plan.nRow;
107052 iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
107053 if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ){
107054 /* Do nothing */
107055 }else
@@ -114978,11 +115085,11 @@
114978 ** with various optimizations disabled to verify that the same answer
114979 ** is obtained in every case.
114980 */
114981 case SQLITE_TESTCTRL_OPTIMIZATIONS: {
114982 sqlite3 *db = va_arg(ap, sqlite3*);
114983 db->dbOptFlags = (u16)(va_arg(ap, int) & 0xffff);
114984 break;
114985 }
114986
114987 #ifdef SQLITE_N_KEYWORD
114988 /* sqlite3_test_control(SQLITE_TESTCTRL_ISKEYWORD, const char *zWord)
@@ -135232,11 +135339,11 @@
135232 /*
135233 ** Remove the entry with rowid=iDelete from the r-tree structure.
135234 */
135235 static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){
135236 int rc; /* Return code */
135237 RtreeNode *pLeaf; /* Leaf node containing record iDelete */
135238 int iCell; /* Index of iDelete cell in pLeaf */
135239 RtreeNode *pRoot; /* Root node of rtree structure */
135240
135241
135242 /* Obtain a reference to the root node to initialise Rtree.iDepth */
@@ -135435,11 +135542,11 @@
135435 ** (azData[2]..azData[argc-1]) contain a new record to insert into
135436 ** the r-tree structure.
135437 */
135438 if( rc==SQLITE_OK && nData>1 ){
135439 /* Insert the new record into the r-tree */
135440 RtreeNode *pLeaf;
135441
135442 /* Figure out the rowid of the new row. */
135443 if( bHaveRowid==0 ){
135444 rc = newRowid(pRtree, &cell.iRowid);
135445 }
135446
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -673,11 +673,11 @@
673 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
674 ** [sqlite_version()] and [sqlite_source_id()].
675 */
676 #define SQLITE_VERSION "3.7.15"
677 #define SQLITE_VERSION_NUMBER 3007015
678 #define SQLITE_SOURCE_ID "2012-10-05 07:36:34 43155b1543bddbb84a8bc13a5b7344b228ddacb9"
679
680 /*
681 ** CAPI3REF: Run-Time Library Version Numbers
682 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
683 **
@@ -1421,10 +1421,21 @@
1421 ** that the VFS encountered an error while handling the [PRAGMA] and the
1422 ** compilation of the PRAGMA fails with an error. ^The [SQLITE_FCNTL_PRAGMA]
1423 ** file control occurs at the beginning of pragma statement analysis and so
1424 ** it is able to override built-in [PRAGMA] statements.
1425 ** </ul>
1426 **
1427 ** <li>[[SQLITE_FCNTL_BUSYHANDLER]]
1428 ** ^This file-control may be invoked by SQLite on the database file handle
1429 ** shortly after it is opened in order to provide a custom VFS with access
1430 ** to the connections busy-handler callback. The argument is of type (void **)
1431 ** - an array of two (void *) values. The first (void *) actually points
1432 ** to a function of type (int (*)(void *)). In order to invoke the connections
1433 ** busy-handler, this function should be invoked with the second (void *) in
1434 ** the array as the only argument. If it returns non-zero, then the operation
1435 ** should be retried. If it returns zero, the custom VFS should abandon the
1436 ** current operation.
1437 */
1438 #define SQLITE_FCNTL_LOCKSTATE 1
1439 #define SQLITE_GET_LOCKPROXYFILE 2
1440 #define SQLITE_SET_LOCKPROXYFILE 3
1441 #define SQLITE_LAST_ERRNO 4
@@ -1436,10 +1447,11 @@
1447 #define SQLITE_FCNTL_PERSIST_WAL 10
1448 #define SQLITE_FCNTL_OVERWRITE 11
1449 #define SQLITE_FCNTL_VFSNAME 12
1450 #define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13
1451 #define SQLITE_FCNTL_PRAGMA 14
1452 #define SQLITE_FCNTL_BUSYHANDLER 15
1453
1454 /*
1455 ** CAPI3REF: Mutex Handle
1456 **
1457 ** The mutex module within SQLite defines [sqlite3_mutex] to be an
@@ -8381,10 +8393,13 @@
8393 SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*);
8394 SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int);
8395 SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree*);
8396 SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int);
8397 SQLITE_PRIVATE int sqlite3BtreeGetReserve(Btree*);
8398 #if defined(SQLITE_HAS_CODEC) || defined(SQLITE_DEBUG)
8399 SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p);
8400 #endif
8401 SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int);
8402 SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *);
8403 SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int);
8404 SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
8405 SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree*, int);
@@ -9150,10 +9165,11 @@
9165 SQLITE_PRIVATE int sqlite3PagerNosync(Pager*);
9166 SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*);
9167 SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*);
9168 SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *);
9169 SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *);
9170 SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *);
9171
9172 /* Functions used to truncate the database file. */
9173 SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno);
9174
9175 #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL)
@@ -25825,10 +25841,12 @@
25841 int prior = 0;
25842 #if (!defined(USE_PREAD) && !defined(USE_PREAD64))
25843 i64 newOffset;
25844 #endif
25845 TIMER_START;
25846 assert( cnt==(cnt&0x1ffff) );
25847 cnt &= 0x1ffff;
25848 do{
25849 #if defined(USE_PREAD)
25850 got = osPread(id->h, pBuf, cnt, offset);
25851 SimulateIOError( got = -1 );
25852 #elif defined(USE_PREAD64)
@@ -25914,10 +25932,12 @@
25932 static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){
25933 int got;
25934 #if (!defined(USE_PREAD) && !defined(USE_PREAD64))
25935 i64 newOffset;
25936 #endif
25937 assert( cnt==(cnt&0x1ffff) );
25938 cnt &= 0x1ffff;
25939 TIMER_START;
25940 #if defined(USE_PREAD)
25941 do{ got = osPwrite(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR );
25942 #elif defined(USE_PREAD64)
25943 do{ got = osPwrite64(id->h, pBuf, cnt, offset);}while( got<0 && errno==EINTR);
@@ -39558,10 +39578,25 @@
39578 }
39579 }
39580 }
39581 return rc;
39582 }
39583
39584 /*
39585 ** Return a sanitized version of the sector-size of OS file pFile. The
39586 ** return value is guaranteed to lie between 32 and MAX_SECTOR_SIZE.
39587 */
39588 SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *pFile){
39589 int iRet = sqlite3OsSectorSize(pFile);
39590 if( iRet<32 ){
39591 iRet = 512;
39592 }else if( iRet>MAX_SECTOR_SIZE ){
39593 assert( MAX_SECTOR_SIZE>=512 );
39594 iRet = MAX_SECTOR_SIZE;
39595 }
39596 return iRet;
39597 }
39598
39599 /*
39600 ** Set the value of the Pager.sectorSize variable for the given
39601 ** pager based on the value returned by the xSectorSize method
39602 ** of the open database file. The sector size will be used used
@@ -39594,18 +39629,11 @@
39629 /* Sector size doesn't matter for temporary files. Also, the file
39630 ** may not have been opened yet, in which case the OsSectorSize()
39631 ** call will segfault. */
39632 pPager->sectorSize = 512;
39633 }else{
39634 pPager->sectorSize = sqlite3SectorSize(pPager->fd);
 
 
 
 
 
 
 
39635 }
39636 }
39637
39638 /*
39639 ** Playback the journal and thus restore the database file to
@@ -40518,13 +40546,20 @@
40546 */
40547 SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(
40548 Pager *pPager, /* Pager object */
40549 int (*xBusyHandler)(void *), /* Pointer to busy-handler function */
40550 void *pBusyHandlerArg /* Argument to pass to xBusyHandler */
40551 ){
40552 pPager->xBusyHandler = xBusyHandler;
40553 pPager->pBusyHandlerArg = pBusyHandlerArg;
40554
40555 if( isOpen(pPager->fd) ){
40556 void **ap = (void **)&pPager->xBusyHandler;
40557 assert( ((int(*)(void *))(ap[0]))==xBusyHandler );
40558 assert( ap[1]==pBusyHandlerArg );
40559 sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_BUSYHANDLER, (void *)ap);
40560 }
40561 }
40562
40563 /*
40564 ** Change the page size used by the Pager object. The new page size
40565 ** is passed in *pPageSize.
@@ -46815,11 +46850,11 @@
46850 ** sector boundary is synced; the part of the last frame that extends
46851 ** past the sector boundary is written after the sync.
46852 */
46853 if( isCommit && (sync_flags & WAL_SYNC_TRANSACTIONS)!=0 ){
46854 if( pWal->padToSectorBoundary ){
46855 int sectorSize = sqlite3SectorSize(pWal->pWalFd);
46856 w.iSyncPoint = ((iOffset+sectorSize-1)/sectorSize)*sectorSize;
46857 while( iOffset<w.iSyncPoint ){
46858 rc = walWriteOneFrame(&w, pLast, nTruncate, iOffset);
46859 if( rc ) return rc;
46860 iOffset += szFrame;
@@ -50227,10 +50262,28 @@
50262 ** Return the currently defined page size
50263 */
50264 SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree *p){
50265 return p->pBt->pageSize;
50266 }
50267
50268 #if defined(SQLITE_HAS_CODEC) || defined(SQLITE_DEBUG)
50269 /*
50270 ** This function is similar to sqlite3BtreeGetReserve(), except that it
50271 ** may only be called if it is guaranteed that the b-tree mutex is already
50272 ** held.
50273 **
50274 ** This is useful in one special case in the backup API code where it is
50275 ** known that the shared b-tree mutex is held, but the mutex on the
50276 ** database handle that owns *p is not. In this case if sqlite3BtreeEnter()
50277 ** were to be called, it might collide with some other operation on the
50278 ** database handle that owns *p, causing undefined behaviour.
50279 */
50280 SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p){
50281 assert( sqlite3_mutex_held(p->pBt->mutex) );
50282 return p->pBt->pageSize - p->pBt->usableSize;
50283 }
50284 #endif /* SQLITE_HAS_CODEC || SQLITE_DEBUG */
50285
50286 #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM)
50287 /*
50288 ** Return the number of bytes of space at the end of every page that
50289 ** are intentually left unused. This is the "reserved" space that is
@@ -53284,11 +53337,11 @@
53337 btreeParseCellPtr(pPage, pCell, &info);
53338 if( info.iOverflow==0 ){
53339 return SQLITE_OK; /* No overflow pages. Return without doing anything */
53340 }
53341 if( pCell+info.iOverflow+3 > pPage->aData+pPage->maskPage ){
53342 return SQLITE_CORRUPT_BKPT; /* Cell extends past end of page */
53343 }
53344 ovflPgno = get4byte(&pCell[info.iOverflow]);
53345 assert( pBt->usableSize > 4 );
53346 ovflPageSize = pBt->usableSize - 4;
53347 nOvfl = (info.nPayload - info.nLocal + ovflPageSize - 1)/ovflPageSize;
@@ -53950,10 +54003,13 @@
54003 ** enough for all overflow cells.
54004 **
54005 ** If aOvflSpace is set to a null pointer, this function returns
54006 ** SQLITE_NOMEM.
54007 */
54008 #if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_M_ARM)
54009 #pragma optimize("", off)
54010 #endif
54011 static int balance_nonroot(
54012 MemPage *pParent, /* Parent page of siblings being balanced */
54013 int iParentIdx, /* Index of "the page" in pParent */
54014 u8 *aOvflSpace, /* page-size bytes of space for parent ovfl */
54015 int isRoot, /* True if pParent is a root-page */
@@ -54580,10 +54636,13 @@
54636 releasePage(apNew[i]);
54637 }
54638
54639 return rc;
54640 }
54641 #if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_M_ARM)
54642 #pragma optimize("", on)
54643 #endif
54644
54645
54646 /*
54647 ** This function is called when the root page of a b-tree structure is
54648 ** overfull (has one or more overflow pages).
@@ -56558,17 +56617,20 @@
56617 const int nSrcPgsz = sqlite3BtreeGetPageSize(p->pSrc);
56618 int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest);
56619 const int nCopy = MIN(nSrcPgsz, nDestPgsz);
56620 const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz;
56621 #ifdef SQLITE_HAS_CODEC
56622 /* Use BtreeGetReserveNoMutex() for the source b-tree, as although it is
56623 ** guaranteed that the shared-mutex is held by this thread, handle
56624 ** p->pSrc may not actually be the owner. */
56625 int nSrcReserve = sqlite3BtreeGetReserveNoMutex(p->pSrc);
56626 int nDestReserve = sqlite3BtreeGetReserve(p->pDest);
56627 #endif
 
56628 int rc = SQLITE_OK;
56629 i64 iOff;
56630
56631 assert( sqlite3BtreeGetReserveNoMutex(p->pSrc)>=0 );
56632 assert( p->bDestLocked );
56633 assert( !isFatalError(p->rc) );
56634 assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) );
56635 assert( zSrcData );
56636
@@ -91600,10 +91662,11 @@
91662 */
91663 aFcntl[0] = 0;
91664 aFcntl[1] = zLeft;
91665 aFcntl[2] = zRight;
91666 aFcntl[3] = 0;
91667 db->busyHandler.nBusy = 0;
91668 rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_PRAGMA, (void*)aFcntl);
91669 if( rc==SQLITE_OK ){
91670 if( aFcntl[0] ){
91671 int mem = ++pParse->nMem;
91672 sqlite3VdbeAddOp4(v, OP_String8, 0, mem, 0, aFcntl[0], 0);
@@ -102123,14 +102186,15 @@
102186 #define WHERE_NOT_FULLSCAN 0x100f3000 /* Does not do a full table scan */
102187 #define WHERE_IN_ABLE 0x000f1000 /* Able to support an IN operator */
102188 #define WHERE_TOP_LIMIT 0x00100000 /* x<EXPR or x<=EXPR constraint */
102189 #define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */
102190 #define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and x<EXPR */
102191 #define WHERE_IDX_ONLY 0x00400000 /* Use index only - omit table */
102192 #define WHERE_ORDERED 0x00800000 /* Output will appear in correct order */
102193 #define WHERE_REVERSE 0x01000000 /* Scan in reverse order */
102194 #define WHERE_UNIQUE 0x02000000 /* Selects no more than one row */
102195 #define WHERE_ALL_UNIQUE 0x04000000 /* This and all prior have one row */
102196 #define WHERE_VIRTUALTABLE 0x08000000 /* Use virtual-table processing */
102197 #define WHERE_MULTI_OR 0x10000000 /* OR using multiple indices */
102198 #define WHERE_TEMP_INDEX 0x20000000 /* Uses an ephemeral index */
102199 #define WHERE_DISTINCT 0x40000000 /* Correct order for DISTINCT */
102200 #define WHERE_COVER_SCAN 0x80000000 /* Full scan of a covering index */
@@ -102154,10 +102218,21 @@
102218 sqlite3_index_info **ppIdxInfo; /* Index information passed to xBestIndex */
102219 int i, n; /* Which loop is being coded; # of loops */
102220 WhereLevel *aLevel; /* Info about outer loops */
102221 WhereCost cost; /* Lowest cost query plan */
102222 };
102223
102224 /*
102225 ** Return TRUE if the probe cost is less than the baseline cost
102226 */
102227 static int compareCost(const WhereCost *pProbe, const WhereCost *pBaseline){
102228 if( pProbe->rCost<pBaseline->rCost ) return 1;
102229 if( pProbe->rCost>pBaseline->rCost ) return 0;
102230 if( pProbe->plan.nOBSat>pBaseline->plan.nOBSat ) return 1;
102231 if( pProbe->plan.nRow<pBaseline->plan.nRow ) return 1;
102232 return 0;
102233 }
102234
102235 /*
102236 ** Initialize a preallocated WhereClause structure.
102237 */
102238 static void whereClauseInit(
@@ -103306,11 +103381,12 @@
103381 Table *pTab = pIdx->pTable;
103382 int i;
103383 if( pIdx->onError==OE_None ) return 0;
103384 for(i=nSkip; i<pIdx->nColumn; i++){
103385 int j = pIdx->aiColumn[i];
103386 assert( j>=0 && j<pTab->nCol );
103387 if( pTab->aCol[j].notNull==0 ) return 0;
103388 }
103389 return 1;
103390 }
103391
103392 /*
@@ -103370,11 +103446,12 @@
103446 int nEqCol /* Number of index columns with == */
103447 ){
103448 Bitmask mask = 0; /* Mask of unaccounted for pDistinct exprs */
103449 int i; /* Iterator variable */
103450
103451 assert( pDistinct!=0 );
103452 if( pIdx->zName==0 || pDistinct->nExpr>=BMS ) return 0;
103453 testcase( pDistinct->nExpr==BMS-1 );
103454
103455 /* Loop through all the expressions in the distinct list. If any of them
103456 ** are not simple column references, return early. Otherwise, test if the
103457 ** WHERE clause contains a "col=X" clause. If it does, the expression
@@ -103472,170 +103549,10 @@
103549 }
103550
103551 return 0;
103552 }
103553
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103554 /*
103555 ** Prepare a crude estimate of the logarithm of the input value.
103556 ** The results need not be exact. This is only used for estimating
103557 ** the total cost of performing operations with O(logN) or O(NlogN)
103558 ** complexity. Because N is just a guess, it is no great tragedy if
@@ -103785,10 +103702,11 @@
103702 WHERETRACE(("... multi-index OR cost=%.9g nrow=%.9g\n", rTotal, nRow));
103703 if( rTotal<p->cost.rCost ){
103704 p->cost.rCost = rTotal;
103705 p->cost.used = used;
103706 p->cost.plan.nRow = nRow;
103707 p->cost.plan.nOBSat = p->i ? p->aLevel[p->i-1].plan.nOBSat : 0;
103708 p->cost.plan.wsFlags = flags;
103709 p->cost.plan.u.pTerm = pTerm;
103710 }
103711 }
103712 }
@@ -104327,11 +104245,14 @@
104245 }else{
104246 p->cost.rCost = rCost;
104247 }
104248 p->cost.plan.u.pVtabIdx = pIdxInfo;
104249 if( pIdxInfo->orderByConsumed ){
104250 p->cost.plan.wsFlags |= WHERE_ORDERED;
104251 p->cost.plan.nOBSat = nOrderBy;
104252 }else{
104253 p->cost.plan.nOBSat = p->i ? p->aLevel[p->i-1].plan.nOBSat : 0;
104254 }
104255 p->cost.plan.nEq = 0;
104256 pIdxInfo->nOrderBy = nOrderBy;
104257
104258 /* Try to find a more efficient access pattern by using multiple indexes
@@ -104750,20 +104671,26 @@
104671 WhereLevel *pLevel = &p->aLevel[p->i-1];
104672 Index *pIdx;
104673 u8 sortOrder;
104674 for(i=p->i-1; i>=0; i--, pLevel--){
104675 if( pLevel->iTabCur!=iTab ) continue;
104676 if( (pLevel->plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){
104677 return 1;
104678 }
104679 if( (pLevel->plan.wsFlags & WHERE_ORDERED)==0 ){
104680 return 0;
104681 }
104682 if( (pIdx = pLevel->plan.u.pIdx)!=0 ){
104683 if( iCol<0 ){
104684 sortOrder = 0;
104685 testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 );
104686 }else{
104687 int n = pIdx->nColumn;
104688 for(j=0; j<n; j++){
104689 if( iCol==pIdx->aiColumn[j] ) break;
104690 }
104691 if( j>=n ) return 0;
104692 sortOrder = pIdx->aSortOrder[j];
104693 testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 );
104694 }
104695 }else{
104696 if( iCol!=(-1) ) return 0;
@@ -104792,13 +104719,10 @@
104719 static int isOrderedTerm(WhereBestIdx *p, WhereTerm *pTerm, int *pbRev){
104720 Expr *pExpr = pTerm->pExpr;
104721 assert( pExpr->op==TK_EQ );
104722 assert( pExpr->pLeft!=0 && pExpr->pLeft->op==TK_COLUMN );
104723 assert( pExpr->pRight!=0 );
 
 
 
104724 if( pTerm->prereqRight==0 ){
104725 return 1; /* RHS of the == is a constant */
104726 }
104727 if( pExpr->pRight->op==TK_COLUMN
104728 && isOrderedColumn(p, pExpr->pRight->iTable, pExpr->pRight->iColumn, pbRev)
@@ -104808,10 +104732,177 @@
104732
104733 /* If we cannot prove that the constraint is ordered, assume it is not */
104734 return 0;
104735 }
104736
104737 /*
104738 ** This routine decides if pIdx can be used to satisfy the ORDER BY
104739 ** clause, either in whole or in part. The return value is the
104740 ** cumulative number of terms in the ORDER BY clause that are satisfied
104741 ** by the index pIdx and other indices in outer loops.
104742 **
104743 ** The table being queried has a cursor number of "base". pIdx is the
104744 ** index that is postulated for use to access the table.
104745 **
104746 ** nEqCol is the number of columns of pIdx that are used as equality
104747 ** constraints and where the other side of the == is an ordered column
104748 ** or constant. An "order column" in the previous sentence means a column
104749 ** in table from an outer loop whose values will always appear in the
104750 ** correct order due to othre index, or because the outer loop generates
104751 ** a unique result. Any of the first nEqCol columns of pIdx may be missing
104752 ** from the ORDER BY clause and the match can still be a success.
104753 **
104754 ** The *pbRev value is set to 0 order 1 depending on whether or not
104755 ** pIdx should be run in the forward order or in reverse order.
104756 */
104757 static int isSortingIndex(
104758 WhereBestIdx *p, /* Best index search context */
104759 Index *pIdx, /* The index we are testing */
104760 int base, /* Cursor number for the table to be sorted */
104761 int nEqCol, /* Number of index columns with ordered == constraints */
104762 int wsFlags, /* Index usages flags */
104763 int bOuterRev, /* True if outer loops scan in reverse order */
104764 int *pbRev /* Set to 1 for reverse-order scan of pIdx */
104765 ){
104766 int i; /* Number of pIdx terms used */
104767 int j; /* Number of ORDER BY terms satisfied */
104768 int sortOrder = 0; /* XOR of index and ORDER BY sort direction */
104769 int nTerm; /* Number of ORDER BY terms */
104770 struct ExprList_item *pTerm; /* A term of the ORDER BY clause */
104771 ExprList *pOrderBy; /* The ORDER BY clause */
104772 Parse *pParse = p->pParse; /* Parser context */
104773 sqlite3 *db = pParse->db; /* Database connection */
104774 int nPriorSat; /* ORDER BY terms satisfied by outer loops */
104775 int seenRowid = 0; /* True if an ORDER BY rowid term is seen */
104776 int nEqOneRow; /* Idx columns that ref unique values */
104777
104778 if( p->i==0 ){
104779 nPriorSat = 0;
104780 }else{
104781 nPriorSat = p->aLevel[p->i-1].plan.nOBSat;
104782 if( OptimizationDisabled(db, SQLITE_OrderByIdxJoin) ) return nPriorSat;
104783 }
104784 if( nEqCol==0 ){
104785 if( p->i && (p->aLevel[p->i-1].plan.wsFlags & WHERE_ORDERED)==0 ){
104786 return nPriorSat;
104787 }
104788 nEqOneRow = 0;
104789 }else if( p->i==0 || (p->aLevel[p->i-1].plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){
104790 nEqOneRow = nEqCol;
104791 }else{
104792 sortOrder = bOuterRev;
104793 nEqOneRow = -1;
104794 }
104795 pOrderBy = p->pOrderBy;
104796 assert( pOrderBy!=0 );
104797 if( wsFlags & WHERE_COLUMN_IN ) return nPriorSat;
104798 if( pIdx->bUnordered ) return nPriorSat;
104799 nTerm = pOrderBy->nExpr;
104800 assert( nTerm>0 );
104801
104802 /* Argument pIdx must either point to a 'real' named index structure,
104803 ** or an index structure allocated on the stack by bestBtreeIndex() to
104804 ** represent the rowid index that is part of every table. */
104805 assert( pIdx->zName || (pIdx->nColumn==1 && pIdx->aiColumn[0]==-1) );
104806
104807 /* Match terms of the ORDER BY clause against columns of
104808 ** the index.
104809 **
104810 ** Note that indices have pIdx->nColumn regular columns plus
104811 ** one additional column containing the rowid. The rowid column
104812 ** of the index is also allowed to match against the ORDER BY
104813 ** clause.
104814 */
104815 for(i=0,j=nPriorSat,pTerm=&pOrderBy->a[j]; j<nTerm; i++){
104816 Expr *pExpr; /* The expression of the ORDER BY pTerm */
104817 CollSeq *pColl; /* The collating sequence of pExpr */
104818 int termSortOrder; /* Sort order for this term */
104819 int iColumn; /* The i-th column of the index. -1 for rowid */
104820 int iSortOrder; /* 1 for DESC, 0 for ASC on the i-th index term */
104821 const char *zColl; /* Name of the collating sequence for i-th index term */
104822
104823 assert( i<=pIdx->nColumn );
104824 pExpr = pTerm->pExpr;
104825 if( pExpr->op!=TK_COLUMN || pExpr->iTable!=base ){
104826 /* Can not use an index sort on anything that is not a column in the
104827 ** left-most table of the FROM clause */
104828 break;
104829 }
104830 pColl = sqlite3ExprCollSeq(pParse, pExpr);
104831 if( !pColl ){
104832 pColl = db->pDfltColl;
104833 }
104834 if( pIdx->zName && i<pIdx->nColumn ){
104835 iColumn = pIdx->aiColumn[i];
104836 if( iColumn==pIdx->pTable->iPKey ){
104837 iColumn = -1;
104838 }
104839 iSortOrder = pIdx->aSortOrder[i];
104840 zColl = pIdx->azColl[i];
104841 }else{
104842 iColumn = -1;
104843 iSortOrder = 0;
104844 zColl = pColl->zName;
104845 }
104846 if( pExpr->iColumn!=iColumn || sqlite3StrICmp(pColl->zName, zColl) ){
104847 /* Term j of the ORDER BY clause does not match column i of the index */
104848 if( i<nEqCol ){
104849 /* If an index column that is constrained by == fails to match an
104850 ** ORDER BY term, that is OK. Just ignore that column of the index
104851 */
104852 continue;
104853 }else if( i==pIdx->nColumn ){
104854 /* Index column i is the rowid. All other terms match. */
104855 break;
104856 }else{
104857 /* If an index column fails to match and is not constrained by ==
104858 ** then the index cannot satisfy the ORDER BY constraint.
104859 */
104860 return nPriorSat;
104861 }
104862 }
104863 assert( pIdx->aSortOrder!=0 || iColumn==-1 );
104864 assert( pTerm->sortOrder==0 || pTerm->sortOrder==1 );
104865 assert( iSortOrder==0 || iSortOrder==1 );
104866 termSortOrder = iSortOrder ^ pTerm->sortOrder;
104867 if( i>nEqOneRow ){
104868 if( termSortOrder!=sortOrder ){
104869 /* Indices can only be used if all ORDER BY terms past the
104870 ** equality constraints have the correct DESC or ASC. */
104871 break;
104872 }
104873 }else{
104874 sortOrder = termSortOrder;
104875 }
104876 j++;
104877 pTerm++;
104878 if( iColumn<0 ){
104879 seenRowid = 1;
104880 break;
104881 }
104882 }
104883 *pbRev = sortOrder;
104884
104885 /* If there was an "ORDER BY rowid" term that matched, or it is only
104886 ** possible for a single row from this table to match, then skip over
104887 ** any additional ORDER BY terms dealing with this table.
104888 */
104889 if( seenRowid ||
104890 ( (wsFlags & WHERE_COLUMN_NULL)==0
104891 && i>=pIdx->nColumn
104892 && indexIsUniqueNotNull(pIdx, nEqCol)
104893 )
104894 ){
104895 /* Advance j over additional ORDER BY terms associated with base */
104896 WhereMaskSet *pMS = p->pWC->pMaskSet;
104897 Bitmask m = ~getMask(pMS, base);
104898 while( j<nTerm && (exprTableUsage(pMS, pOrderBy->a[j].pExpr)&m)==0 ){
104899 j++;
104900 }
104901 }
104902 return j;
104903 }
104904
104905 /*
104906 ** Find the best query plan for accessing a particular table. Write the
104907 ** best query plan and its cost into the p->cost.
104908 **
@@ -104902,22 +104993,19 @@
104993
104994 /* Loop over all indices looking for the best one to use
104995 */
104996 for(; pProbe; pIdx=pProbe=pProbe->pNext){
104997 const tRowcnt * const aiRowEst = pProbe->aiRowEst;
104998 WhereCost pc; /* Cost of using pProbe */
 
104999 double log10N = (double)1; /* base-10 logarithm of nRow (inexact) */
105000 int bRev = 2; /* 0=forward scan. 1=reverse. 2=undecided */
 
 
105001
105002 /* The following variables are populated based on the properties of
105003 ** index being evaluated. They are then used to determine the expected
105004 ** cost and number of rows returned.
105005 **
105006 ** pc.plan.nEq:
105007 ** Number of equality terms that can be implemented using the index.
105008 ** In other words, the number of initial fields in the index that
105009 ** are used in == or IN or NOT NULL constraints of the WHERE clause.
105010 **
105011 ** nInMul:
@@ -104960,11 +105048,11 @@
105048 ** bSort:
105049 ** Boolean. True if there is an ORDER BY clause that will require an
105050 ** external sort (i.e. scanning the index being evaluated will not
105051 ** correctly order records).
105052 **
105053 ** bDist:
105054 ** Boolean. True if there is a DISTINCT clause that will require an
105055 ** external btree.
105056 **
105057 ** bLookup:
105058 ** Boolean. True if a table lookup is required for each index entry
@@ -104979,132 +105067,148 @@
105067 ** both available in the index.
105068 **
105069 ** SELECT a, b FROM tbl WHERE a = 1;
105070 ** SELECT a, b, c FROM tbl WHERE a = 1;
105071 */
 
105072 int nOrdered; /* Number of ordered terms matching index */
105073 int bInEst = 0; /* True if "x IN (SELECT...)" seen */
105074 int nInMul = 1; /* Number of distinct equalities to lookup */
105075 double rangeDiv = (double)1; /* Estimated reduction in search space */
105076 int nBound = 0; /* Number of range constraints seen */
105077 int bSort; /* True if external sort required */
105078 int bDist; /* True if index cannot help with DISTINCT */
105079 int bLookup = 0; /* True if not a covering index */
105080 int nPriorSat; /* ORDER BY terms satisfied by outer loops */
105081 int nOrderBy; /* Number of ORDER BY terms */
105082 WhereTerm *pTerm; /* A single term of the WHERE clause */
105083 #ifdef SQLITE_ENABLE_STAT3
105084 WhereTerm *pFirstTerm = 0; /* First term matching the index */
105085 #endif
105086
105087 memset(&pc, 0, sizeof(pc));
105088 nOrderBy = p->pOrderBy ? p->pOrderBy->nExpr : 0;
105089 if( p->i ){
105090 nPriorSat = pc.plan.nOBSat = p->aLevel[p->i-1].plan.nOBSat;
105091 bSort = nPriorSat<nOrderBy;
105092 bDist = 0;
105093 }else{
105094 nPriorSat = pc.plan.nOBSat = 0;
105095 bSort = nOrderBy>0;
105096 bDist = p->pDistinct!=0;
105097 }
105098
105099 /* Determine the values of pc.plan.nEq and nInMul */
105100 for(pc.plan.nEq=nOrdered=0; pc.plan.nEq<pProbe->nColumn; pc.plan.nEq++){
105101 int j = pProbe->aiColumn[pc.plan.nEq];
105102 pTerm = findTerm(pWC, iCur, j, p->notReady, eqTermMask, pIdx);
105103 if( pTerm==0 ) break;
105104 pc.plan.wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ);
105105 testcase( pTerm->pWC!=pWC );
105106 if( pTerm->eOperator & WO_IN ){
105107 Expr *pExpr = pTerm->pExpr;
105108 pc.plan.wsFlags |= WHERE_COLUMN_IN;
105109 if( ExprHasProperty(pExpr, EP_xIsSelect) ){
105110 /* "x IN (SELECT ...)": Assume the SELECT returns 25 rows */
105111 nInMul *= 25;
105112 bInEst = 1;
105113 }else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){
105114 /* "x IN (value, value, ...)" */
105115 nInMul *= pExpr->x.pList->nExpr;
105116 }
105117 }else if( pTerm->eOperator & WO_ISNULL ){
105118 pc.plan.wsFlags |= WHERE_COLUMN_NULL;
105119 if( pc.plan.nEq==nOrdered ) nOrdered++;
105120 }else if( bSort && pc.plan.nEq==nOrdered
105121 && isOrderedTerm(p,pTerm,&bRev) ){
105122 nOrdered++;
105123 }
105124 #ifdef SQLITE_ENABLE_STAT3
105125 if( pc.plan.nEq==0 && pProbe->aSample ) pFirstTerm = pTerm;
105126 #endif
105127 pc.used |= pTerm->prereqRight;
105128 }
105129
105130 /* If the index being considered is UNIQUE, and there is an equality
105131 ** constraint for all columns in the index, then this search will find
105132 ** at most a single row. In this case set the WHERE_UNIQUE flag to
105133 ** indicate this to the caller.
105134 **
105135 ** Otherwise, if the search may find more than one row, test to see if
105136 ** there is a range constraint on indexed column (pc.plan.nEq+1) that can be
105137 ** optimized using the index.
105138 */
105139 if( pc.plan.nEq==pProbe->nColumn && pProbe->onError!=OE_None ){
105140 testcase( pc.plan.wsFlags & WHERE_COLUMN_IN );
105141 testcase( pc.plan.wsFlags & WHERE_COLUMN_NULL );
105142 if( (pc.plan.wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){
105143 pc.plan.wsFlags |= WHERE_UNIQUE;
105144 if( p->i==0 || (p->aLevel[p->i-1].plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){
105145 pc.plan.wsFlags |= WHERE_ALL_UNIQUE;
105146 }
105147 }
105148 }else if( pProbe->bUnordered==0 ){
105149 int j;
105150 j = (pc.plan.nEq==pProbe->nColumn ? -1 : pProbe->aiColumn[pc.plan.nEq]);
105151 if( findTerm(pWC, iCur, j, p->notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){
105152 WhereTerm *pTop, *pBtm;
105153 pTop = findTerm(pWC, iCur, j, p->notReady, WO_LT|WO_LE, pIdx);
105154 pBtm = findTerm(pWC, iCur, j, p->notReady, WO_GT|WO_GE, pIdx);
105155 whereRangeScanEst(pParse, pProbe, pc.plan.nEq, pBtm, pTop, &rangeDiv);
105156 if( pTop ){
105157 nBound = 1;
105158 pc.plan.wsFlags |= WHERE_TOP_LIMIT;
105159 pc.used |= pTop->prereqRight;
105160 testcase( pTop->pWC!=pWC );
105161 }
105162 if( pBtm ){
105163 nBound++;
105164 pc.plan.wsFlags |= WHERE_BTM_LIMIT;
105165 pc.used |= pBtm->prereqRight;
105166 testcase( pBtm->pWC!=pWC );
105167 }
105168 pc.plan.wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE);
105169 }
105170 }
105171
105172 /* If there is an ORDER BY clause and the index being considered will
105173 ** naturally scan rows in the required order, set the appropriate flags
105174 ** in pc.plan.wsFlags. Otherwise, if there is an ORDER BY clause but
105175 ** the index will scan rows in a different order, set the bSort
105176 ** variable. */
105177 assert( bRev>=0 && bRev<=2 );
105178 if( bSort ){
105179 testcase( bRev==0 );
105180 testcase( bRev==1 );
105181 testcase( bRev==2 );
105182 pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, nOrdered,
105183 pc.plan.wsFlags, bRev&1, &bRev);
105184 if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_UNIQUE)!=0 ){
105185 pc.plan.wsFlags |= WHERE_ORDERED;
105186 }
105187 if( nOrderBy==pc.plan.nOBSat ){
105188 bSort = 0;
105189 pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE;
105190 }
105191 if( bRev & 1 ) pc.plan.wsFlags |= WHERE_REVERSE;
105192 }
105193
105194 /* If there is a DISTINCT qualifier and this index will scan rows in
105195 ** order of the DISTINCT expressions, clear bDist and set the appropriate
105196 ** flags in pc.plan.wsFlags. */
105197 if( bDist
105198 && isDistinctIndex(pParse, pWC, pProbe, iCur, p->pDistinct, pc.plan.nEq)
105199 && (pc.plan.wsFlags & WHERE_COLUMN_IN)==0
105200 ){
105201 bDist = 0;
105202 pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_DISTINCT;
105203 }
105204
105205 /* If currently calculating the cost of using an index (not the IPK
105206 ** index), determine if all required column data may be obtained without
105207 ** using the main table (i.e. if the index is a covering
105208 ** index for this query). If it is, set the WHERE_IDX_ONLY flag in
105209 ** pc.plan.wsFlags. Otherwise, set the bLookup variable to true. */
105210 if( pIdx ){
105211 Bitmask m = pSrc->colUsed;
105212 int j;
105213 for(j=0; j<pIdx->nColumn; j++){
105214 int x = pIdx->aiColumn[j];
@@ -105111,51 +105215,54 @@
105215 if( x<BMS-1 ){
105216 m &= ~(((Bitmask)1)<<x);
105217 }
105218 }
105219 if( m==0 ){
105220 pc.plan.wsFlags |= WHERE_IDX_ONLY;
105221 }else{
105222 bLookup = 1;
105223 }
105224 }
105225
105226 /*
105227 ** Estimate the number of rows of output. For an "x IN (SELECT...)"
105228 ** constraint, do not let the estimate exceed half the rows in the table.
105229 */
105230 pc.plan.nRow = (double)(aiRowEst[pc.plan.nEq] * nInMul);
105231 if( bInEst && pc.plan.nRow*2>aiRowEst[0] ){
105232 pc.plan.nRow = aiRowEst[0]/2;
105233 nInMul = (int)(pc.plan.nRow / aiRowEst[pc.plan.nEq]);
105234 }
105235
105236 #ifdef SQLITE_ENABLE_STAT3
105237 /* If the constraint is of the form x=VALUE or x IN (E1,E2,...)
105238 ** and we do not think that values of x are unique and if histogram
105239 ** data is available for column x, then it might be possible
105240 ** to get a better estimate on the number of rows based on
105241 ** VALUE and how common that value is according to the histogram.
105242 */
105243 if( pc.plan.nRow>(double)1 && pc.plan.nEq==1
105244 && pFirstTerm!=0 && aiRowEst[1]>1 ){
105245 assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 );
105246 if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){
105247 testcase( pFirstTerm->eOperator==WO_EQ );
105248 testcase( pFirstTerm->eOperator==WO_ISNULL );
105249 whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight,
105250 &pc.plan.nRow);
105251 }else if( bInEst==0 ){
105252 assert( pFirstTerm->eOperator==WO_IN );
105253 whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList,
105254 &pc.plan.nRow);
105255 }
105256 }
105257 #endif /* SQLITE_ENABLE_STAT3 */
105258
105259 /* Adjust the number of output rows and downward to reflect rows
105260 ** that are excluded by range constraints.
105261 */
105262 pc.plan.nRow = pc.plan.nRow/rangeDiv;
105263 if( pc.plan.nRow<1 ) pc.plan.nRow = 1;
105264
105265 /* Experiments run on real SQLite databases show that the time needed
105266 ** to do a binary search to locate a row in a table or index is roughly
105267 ** log10(N) times the time to move from one row to the next row within
105268 ** a table or index. The actual times can vary, with the size of
@@ -105166,57 +105273,58 @@
105273 ** The ANALYZE command and the sqlite_stat1 and sqlite_stat3 tables do
105274 ** not give us data on the relative sizes of table and index records.
105275 ** So this computation assumes table records are about twice as big
105276 ** as index records
105277 */
105278 if( (pc.plan.wsFlags&~(WHERE_REVERSE|WHERE_ORDERED))==WHERE_IDX_ONLY
105279 && (pWC->wctrlFlags & WHERE_ONEPASS_DESIRED)==0
105280 && sqlite3GlobalConfig.bUseCis
105281 && OptimizationEnabled(pParse->db, SQLITE_CoverIdxScan)
105282 ){
105283 /* This index is not useful for indexing, but it is a covering index.
105284 ** A full-scan of the index might be a little faster than a full-scan
105285 ** of the table, so give this case a cost slightly less than a table
105286 ** scan. */
105287 pc.rCost = aiRowEst[0]*3 + pProbe->nColumn;
105288 pc.plan.wsFlags |= WHERE_COVER_SCAN|WHERE_COLUMN_RANGE;
105289 }else if( (pc.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){
105290 /* The cost of a full table scan is a number of move operations equal
105291 ** to the number of rows in the table.
105292 **
105293 ** We add an additional 4x penalty to full table scans. This causes
105294 ** the cost function to err on the side of choosing an index over
105295 ** choosing a full scan. This 4x full-scan penalty is an arguable
105296 ** decision and one which we expect to revisit in the future. But
105297 ** it seems to be working well enough at the moment.
105298 */
105299 pc.rCost = aiRowEst[0]*4;
105300 pc.plan.wsFlags &= ~WHERE_IDX_ONLY;
105301 if( pIdx ) pc.plan.wsFlags &= ~WHERE_ORDERED;
105302 }else{
105303 log10N = estLog(aiRowEst[0]);
105304 pc.rCost = pc.plan.nRow;
105305 if( pIdx ){
105306 if( bLookup ){
105307 /* For an index lookup followed by a table lookup:
105308 ** nInMul index searches to find the start of each index range
105309 ** + nRow steps through the index
105310 ** + nRow table searches to lookup the table entry using the rowid
105311 */
105312 pc.rCost += (nInMul + pc.plan.nRow)*log10N;
105313 }else{
105314 /* For a covering index:
105315 ** nInMul index searches to find the initial entry
105316 ** + nRow steps through the index
105317 */
105318 pc.rCost += nInMul*log10N;
105319 }
105320 }else{
105321 /* For a rowid primary key lookup:
105322 ** nInMult table searches to find the initial entry for each range
105323 ** + nRow steps through the table
105324 */
105325 pc.rCost += nInMul*log10N;
105326 }
105327 }
105328
105329 /* Add in the estimated cost of sorting the result. Actual experimental
105330 ** measurements of sorting performance in SQLite show that sorting time
@@ -105223,14 +105331,16 @@
105331 ** adds C*N*log10(N) to the cost, where N is the number of rows to be
105332 ** sorted and C is a factor between 1.95 and 4.3. We will split the
105333 ** difference and select C of 3.0.
105334 */
105335 if( bSort ){
105336 double m = estLog(pc.plan.nRow*(nOrderBy - pc.plan.nOBSat)/nOrderBy);
105337 m *= (double)(pc.plan.nOBSat ? 2 : 3);
105338 pc.rCost += pc.plan.nRow*m;
105339 }
105340 if( bDist ){
105341 pc.rCost += pc.plan.nRow*estLog(pc.plan.nRow)*3;
105342 }
105343
105344 /**** Cost of using this index has now been computed ****/
105345
105346 /* If there are additional constraints on this table that cannot
@@ -105247,29 +105357,29 @@
105357 ** tables that are not in outer loops. If notReady is used here instead
105358 ** of notValid, then a optimal index that depends on inner joins loops
105359 ** might be selected even when there exists an optimal index that has
105360 ** no such dependency.
105361 */
105362 if( pc.plan.nRow>2 && pc.rCost<=p->cost.rCost ){
105363 int k; /* Loop counter */
105364 int nSkipEq = pc.plan.nEq; /* Number of == constraints to skip */
105365 int nSkipRange = nBound; /* Number of < constraints to skip */
105366 Bitmask thisTab; /* Bitmap for pSrc */
105367
105368 thisTab = getMask(pWC->pMaskSet, iCur);
105369 for(pTerm=pWC->a, k=pWC->nTerm; pc.plan.nRow>2 && k; k--, pTerm++){
105370 if( pTerm->wtFlags & TERM_VIRTUAL ) continue;
105371 if( (pTerm->prereqAll & p->notValid)!=thisTab ) continue;
105372 if( pTerm->eOperator & (WO_EQ|WO_IN|WO_ISNULL) ){
105373 if( nSkipEq ){
105374 /* Ignore the first pc.plan.nEq equality matches since the index
105375 ** has already accounted for these */
105376 nSkipEq--;
105377 }else{
105378 /* Assume each additional equality match reduces the result
105379 ** set size by a factor of 10 */
105380 pc.plan.nRow /= 10;
105381 }
105382 }else if( pTerm->eOperator & (WO_LT|WO_LE|WO_GT|WO_GE) ){
105383 if( nSkipRange ){
105384 /* Ignore the first nSkipRange range constraints since the index
105385 ** has already accounted for these */
@@ -105279,43 +105389,38 @@
105389 ** set size by a factor of 3. Indexed range constraints reduce
105390 ** the search space by a larger factor: 4. We make indexed range
105391 ** more selective intentionally because of the subjective
105392 ** observation that indexed range constraints really are more
105393 ** selective in practice, on average. */
105394 pc.plan.nRow /= 3;
105395 }
105396 }else if( pTerm->eOperator!=WO_NOOP ){
105397 /* Any other expression lowers the output row count by half */
105398 pc.plan.nRow /= 2;
105399 }
105400 }
105401 if( pc.plan.nRow<2 ) pc.plan.nRow = 2;
105402 }
105403
105404
105405 WHERETRACE((
105406 "%s(%s):\n"
105407 " nEq=%d nInMul=%d rangeDiv=%d bSort=%d bLookup=%d wsFlags=0x%08x\n"
105408 " notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f\n"
105409 " used=0x%llx nOrdered=%d nOBSat=%d\n",
105410 pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk"),
105411 pc.plan.nEq, nInMul, (int)rangeDiv, bSort, bLookup, pc.plan.wsFlags,
105412 p->notReady, log10N, pc.plan.nRow, pc.rCost, pc.used, nOrdered,
105413 pc.plan.nOBSat
105414 ));
105415
105416 /* If this index is the best we have seen so far, then record this
105417 ** index and its cost in the p->cost structure.
105418 */
105419 if( (!pIdx || pc.plan.wsFlags) && compareCost(&pc, &p->cost) ){
105420 p->cost = pc;
105421 p->cost.plan.wsFlags &= wsFlagMask;
 
 
 
 
 
 
105422 p->cost.plan.u.pIdx = pIdx;
105423 }
105424
105425 /* If there was an INDEXED BY clause, then only that one index is
105426 ** considered. */
@@ -105333,21 +105438,19 @@
105438 ** SQLite outputs rows in in the absence of an ORDER BY clause. */
105439 if( !p->pOrderBy && pParse->db->flags & SQLITE_ReverseOrder ){
105440 p->cost.plan.wsFlags |= WHERE_REVERSE;
105441 }
105442
105443 assert( p->pOrderBy || (p->cost.plan.wsFlags&WHERE_ORDERED)==0 );
105444 assert( p->cost.plan.u.pIdx==0 || (p->cost.plan.wsFlags&WHERE_ROWID_EQ)==0 );
105445 assert( pSrc->pIndex==0
105446 || p->cost.plan.u.pIdx==0
105447 || p->cost.plan.u.pIdx==pSrc->pIndex
105448 );
105449
105450 WHERETRACE(("best index is: %s\n",
105451 p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk"));
 
 
105452
105453 bestOrClauseIndex(p);
105454 bestAutomaticIndex(p);
105455 p->cost.plan.wsFlags |= eqTermMask;
105456 }
@@ -106071,11 +106174,11 @@
106174 ** should not have a NULL value stored in 'x'. If column 'x' is
106175 ** the first one after the nEq equality constraints in the index,
106176 ** this requires some special handling.
106177 */
106178 if( (wctrlFlags&WHERE_ORDERBY_MIN)!=0
106179 && (pLevel->plan.wsFlags&WHERE_ORDERED)
106180 && (pIdx->nColumn>nEq)
106181 ){
106182 /* assert( pOrderBy->nExpr==1 ); */
106183 /* assert( pOrderBy->a[0].pExpr->iColumn==pIdx->aiColumn[nEq] ); */
106184 isMinQuery = 1;
@@ -106892,12 +106995,12 @@
106995 continue;
106996 }
106997 sWBI.notReady = (isOptimal ? m : sWBI.notValid);
106998 if( sWBI.pSrc->pIndex==0 ) nUnconstrained++;
106999
107000 WHERETRACE(("=== trying table %d (%s) with isOptimal=%d ===\n",
107001 j, sWBI.pSrc->pTab->zName, isOptimal));
107002 assert( sWBI.pSrc->pTab );
107003 #ifndef SQLITE_OMIT_VIRTUALTABLE
107004 if( IsVirtual(sWBI.pSrc->pTab) ){
107005 sWBI.ppIdxInfo = &pWInfo->a[j].pIdxInfo;
107006 bestVirtualIndex(&sWBI);
@@ -106934,48 +107037,46 @@
107037 ** combination of INDEXED BY clauses are given. The error
107038 ** will be detected and relayed back to the application later.
107039 ** The NEVER() comes about because rule (2) above prevents
107040 ** An indexable full-table-scan from reaching rule (3).
107041 **
107042 ** (4) The plan cost must be lower than prior plans, where "cost"
107043 ** is defined by the compareCost() function above.
107044 */
107045 if( (sWBI.cost.used&sWBI.notValid)==0 /* (1) */
107046 && (bestJ<0 || (notIndexed&m)!=0 /* (2) */
107047 || (bestPlan.plan.wsFlags & WHERE_NOT_FULLSCAN)==0
107048 || (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)
107049 && (nUnconstrained==0 || sWBI.pSrc->pIndex==0 /* (3) */
107050 || NEVER((sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0))
107051 && (bestJ<0 || compareCost(&sWBI.cost, &bestPlan)) /* (4) */
 
 
107052 ){
107053 WHERETRACE(("=== table %d (%s) is best so far\n"
107054 " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=%08x\n",
107055 j, sWBI.pSrc->pTab->zName,
107056 sWBI.cost.rCost, sWBI.cost.plan.nRow,
107057 sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags));
107058 bestPlan = sWBI.cost;
107059 bestJ = j;
107060 }
107061 if( doNotReorder ) break;
107062 }
107063 }
107064 assert( bestJ>=0 );
107065 assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) );
107066 WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n"
107067 " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n",
107068 bestJ, pTabList->a[bestJ].pTab->zName,
107069 pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow,
107070 bestPlan.plan.nOBSat, bestPlan.plan.wsFlags));
 
 
 
107071 if( (bestPlan.plan.wsFlags & WHERE_DISTINCT)!=0 ){
107072 assert( pWInfo->eDistinct==0 );
107073 pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
107074 }
107075 andFlags &= bestPlan.plan.wsFlags;
107076 pLevel->plan = bestPlan.plan;
107077 pLevel->iTabCur = pTabList->a[bestJ].iCursor;
107078 testcase( bestPlan.plan.wsFlags & WHERE_INDEXED );
107079 testcase( bestPlan.plan.wsFlags & WHERE_TEMP_INDEX );
107080 if( bestPlan.plan.wsFlags & (WHERE_INDEXED|WHERE_TEMP_INDEX) ){
107081 if( (wctrlFlags & WHERE_ONETABLE_ONLY)
107082 && (bestPlan.plan.wsFlags & WHERE_TEMP_INDEX)==0
@@ -107013,15 +107114,22 @@
107114 }
107115 WHERETRACE(("*** Optimizer Finished ***\n"));
107116 if( pParse->nErr || db->mallocFailed ){
107117 goto whereBeginError;
107118 }
107119 if( nTabList ){
107120 pLevel--;
107121 pWInfo->nOBSat = pLevel->plan.nOBSat;
107122 }else{
107123 pWInfo->nOBSat = 0;
107124 }
107125
107126 /* If the total query only selects a single row, then the ORDER BY
107127 ** clause is irrelevant.
107128 */
107129 if( (andFlags & WHERE_UNIQUE)!=0 && pOrderBy ){
107130 assert( nTabList==0 || (pLevel->plan.wsFlags & WHERE_ALL_UNIQUE)!=0 );
107131 pWInfo->nOBSat = pOrderBy->nExpr;
107132 }
107133
107134 /* If the caller is an UPDATE or DELETE statement that is requesting
107135 ** to use a one-pass algorithm, determine if this is appropriate.
@@ -107045,11 +107153,10 @@
107153 int iDb; /* Index of database containing table/index */
107154 struct SrcList_item *pTabItem;
107155
107156 pTabItem = &pTabList->a[pLevel->iFrom];
107157 pTab = pTabItem->pTab;
 
107158 pWInfo->nRowOut *= pLevel->plan.nRow;
107159 iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
107160 if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ){
107161 /* Do nothing */
107162 }else
@@ -114978,11 +115085,11 @@
115085 ** with various optimizations disabled to verify that the same answer
115086 ** is obtained in every case.
115087 */
115088 case SQLITE_TESTCTRL_OPTIMIZATIONS: {
115089 sqlite3 *db = va_arg(ap, sqlite3*);
115090 db->dbOptFlags = (u8)(va_arg(ap, int) & 0xff);
115091 break;
115092 }
115093
115094 #ifdef SQLITE_N_KEYWORD
115095 /* sqlite3_test_control(SQLITE_TESTCTRL_ISKEYWORD, const char *zWord)
@@ -135232,11 +135339,11 @@
135339 /*
135340 ** Remove the entry with rowid=iDelete from the r-tree structure.
135341 */
135342 static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){
135343 int rc; /* Return code */
135344 RtreeNode *pLeaf = 0; /* Leaf node containing record iDelete */
135345 int iCell; /* Index of iDelete cell in pLeaf */
135346 RtreeNode *pRoot; /* Root node of rtree structure */
135347
135348
135349 /* Obtain a reference to the root node to initialise Rtree.iDepth */
@@ -135435,11 +135542,11 @@
135542 ** (azData[2]..azData[argc-1]) contain a new record to insert into
135543 ** the r-tree structure.
135544 */
135545 if( rc==SQLITE_OK && nData>1 ){
135546 /* Insert the new record into the r-tree */
135547 RtreeNode *pLeaf = 0;
135548
135549 /* Figure out the rowid of the new row. */
135550 if( bHaveRowid==0 ){
135551 rc = newRowid(pRtree, &cell.iRowid);
135552 }
135553
+13 -1
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -107,11 +107,11 @@
107107
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
108108
** [sqlite_version()] and [sqlite_source_id()].
109109
*/
110110
#define SQLITE_VERSION "3.7.15"
111111
#define SQLITE_VERSION_NUMBER 3007015
112
-#define SQLITE_SOURCE_ID "2012-09-28 00:44:28 1e874629d7cf568368b912b295bd3001147d0b52"
112
+#define SQLITE_SOURCE_ID "2012-10-05 07:36:34 43155b1543bddbb84a8bc13a5b7344b228ddacb9"
113113
114114
/*
115115
** CAPI3REF: Run-Time Library Version Numbers
116116
** KEYWORDS: sqlite3_version, sqlite3_sourceid
117117
**
@@ -855,10 +855,21 @@
855855
** that the VFS encountered an error while handling the [PRAGMA] and the
856856
** compilation of the PRAGMA fails with an error. ^The [SQLITE_FCNTL_PRAGMA]
857857
** file control occurs at the beginning of pragma statement analysis and so
858858
** it is able to override built-in [PRAGMA] statements.
859859
** </ul>
860
+**
861
+** <li>[[SQLITE_FCNTL_BUSYHANDLER]]
862
+** ^This file-control may be invoked by SQLite on the database file handle
863
+** shortly after it is opened in order to provide a custom VFS with access
864
+** to the connections busy-handler callback. The argument is of type (void **)
865
+** - an array of two (void *) values. The first (void *) actually points
866
+** to a function of type (int (*)(void *)). In order to invoke the connections
867
+** busy-handler, this function should be invoked with the second (void *) in
868
+** the array as the only argument. If it returns non-zero, then the operation
869
+** should be retried. If it returns zero, the custom VFS should abandon the
870
+** current operation.
860871
*/
861872
#define SQLITE_FCNTL_LOCKSTATE 1
862873
#define SQLITE_GET_LOCKPROXYFILE 2
863874
#define SQLITE_SET_LOCKPROXYFILE 3
864875
#define SQLITE_LAST_ERRNO 4
@@ -870,10 +881,11 @@
870881
#define SQLITE_FCNTL_PERSIST_WAL 10
871882
#define SQLITE_FCNTL_OVERWRITE 11
872883
#define SQLITE_FCNTL_VFSNAME 12
873884
#define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13
874885
#define SQLITE_FCNTL_PRAGMA 14
886
+#define SQLITE_FCNTL_BUSYHANDLER 15
875887
876888
/*
877889
** CAPI3REF: Mutex Handle
878890
**
879891
** The mutex module within SQLite defines [sqlite3_mutex] to be an
880892
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -107,11 +107,11 @@
107 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
108 ** [sqlite_version()] and [sqlite_source_id()].
109 */
110 #define SQLITE_VERSION "3.7.15"
111 #define SQLITE_VERSION_NUMBER 3007015
112 #define SQLITE_SOURCE_ID "2012-09-28 00:44:28 1e874629d7cf568368b912b295bd3001147d0b52"
113
114 /*
115 ** CAPI3REF: Run-Time Library Version Numbers
116 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
117 **
@@ -855,10 +855,21 @@
855 ** that the VFS encountered an error while handling the [PRAGMA] and the
856 ** compilation of the PRAGMA fails with an error. ^The [SQLITE_FCNTL_PRAGMA]
857 ** file control occurs at the beginning of pragma statement analysis and so
858 ** it is able to override built-in [PRAGMA] statements.
859 ** </ul>
 
 
 
 
 
 
 
 
 
 
 
860 */
861 #define SQLITE_FCNTL_LOCKSTATE 1
862 #define SQLITE_GET_LOCKPROXYFILE 2
863 #define SQLITE_SET_LOCKPROXYFILE 3
864 #define SQLITE_LAST_ERRNO 4
@@ -870,10 +881,11 @@
870 #define SQLITE_FCNTL_PERSIST_WAL 10
871 #define SQLITE_FCNTL_OVERWRITE 11
872 #define SQLITE_FCNTL_VFSNAME 12
873 #define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13
874 #define SQLITE_FCNTL_PRAGMA 14
 
875
876 /*
877 ** CAPI3REF: Mutex Handle
878 **
879 ** The mutex module within SQLite defines [sqlite3_mutex] to be an
880
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -107,11 +107,11 @@
107 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
108 ** [sqlite_version()] and [sqlite_source_id()].
109 */
110 #define SQLITE_VERSION "3.7.15"
111 #define SQLITE_VERSION_NUMBER 3007015
112 #define SQLITE_SOURCE_ID "2012-10-05 07:36:34 43155b1543bddbb84a8bc13a5b7344b228ddacb9"
113
114 /*
115 ** CAPI3REF: Run-Time Library Version Numbers
116 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
117 **
@@ -855,10 +855,21 @@
855 ** that the VFS encountered an error while handling the [PRAGMA] and the
856 ** compilation of the PRAGMA fails with an error. ^The [SQLITE_FCNTL_PRAGMA]
857 ** file control occurs at the beginning of pragma statement analysis and so
858 ** it is able to override built-in [PRAGMA] statements.
859 ** </ul>
860 **
861 ** <li>[[SQLITE_FCNTL_BUSYHANDLER]]
862 ** ^This file-control may be invoked by SQLite on the database file handle
863 ** shortly after it is opened in order to provide a custom VFS with access
864 ** to the connections busy-handler callback. The argument is of type (void **)
865 ** - an array of two (void *) values. The first (void *) actually points
866 ** to a function of type (int (*)(void *)). In order to invoke the connections
867 ** busy-handler, this function should be invoked with the second (void *) in
868 ** the array as the only argument. If it returns non-zero, then the operation
869 ** should be retried. If it returns zero, the custom VFS should abandon the
870 ** current operation.
871 */
872 #define SQLITE_FCNTL_LOCKSTATE 1
873 #define SQLITE_GET_LOCKPROXYFILE 2
874 #define SQLITE_SET_LOCKPROXYFILE 3
875 #define SQLITE_LAST_ERRNO 4
@@ -870,10 +881,11 @@
881 #define SQLITE_FCNTL_PERSIST_WAL 10
882 #define SQLITE_FCNTL_OVERWRITE 11
883 #define SQLITE_FCNTL_VFSNAME 12
884 #define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13
885 #define SQLITE_FCNTL_PRAGMA 14
886 #define SQLITE_FCNTL_BUSYHANDLER 15
887
888 /*
889 ** CAPI3REF: Mutex Handle
890 **
891 ** The mutex module within SQLite defines [sqlite3_mutex] to be an
892
+96 -29
--- src/stash.c
+++ src/stash.c
@@ -155,10 +155,24 @@
155155
int stashid; /* ID of the new stash */
156156
int vid; /* Current checkout */
157157
158158
zComment = find_option("comment", "m", 1);
159159
verify_all_options();
160
+ if( zComment==0 ){
161
+ Blob prompt; /* Prompt for stash comment */
162
+ Blob comment; /* User comment reply */
163
+ blob_zero(&prompt);
164
+ blob_append(&prompt,
165
+ "\n"
166
+ "# Enter a description of what is being stashed. Lines beginning\n"
167
+ "# with \"#\" are ignored. Stash comments are plain text except.\n"
168
+ "# newlines are not preserved.\n",
169
+ -1);
170
+ prompt_for_user_comment(&comment, &prompt);
171
+ blob_reset(&prompt);
172
+ zComment = blob_str(&comment);
173
+ }
160174
stashid = db_lget_int("stash-next", 1);
161175
db_lset_int("stash-next", stashid+1);
162176
vid = db_lget_int("checkout", 0);
163177
vfile_check_signature(vid, 0, 0);
164178
db_multi_exec(
@@ -267,11 +281,18 @@
267281
}
268282
269283
/*
270284
** Show the diffs associate with a single stash.
271285
*/
272
-static void stash_diff(int stashid, const char *zDiffCmd, u64 diffFlags){
286
+static void stash_diff(
287
+ int stashid, /* The stash entry to diff */
288
+ const char *zDiffCmd, /* Command used for diffing */
289
+ const char *zBinGlob, /* GLOB pattern to determine binary files */
290
+ int fBaseline, /* Diff against original baseline check-in if true */
291
+ int fIncludeBinary, /* Do diffs against binary files */
292
+ u64 diffFlags /* Other diff flags */
293
+){
273294
Stmt q;
274295
Blob empty;
275296
blob_zero(&empty);
276297
db_prepare(&q,
277298
"SELECT rid, isRemoved, isExec, isLink, origname, newname, delta"
@@ -280,50 +301,66 @@
280301
);
281302
while( db_step(&q)==SQLITE_ROW ){
282303
int rid = db_column_int(&q, 0);
283304
int isRemoved = db_column_int(&q, 1);
284305
int isLink = db_column_int(&q, 3);
306
+ int isBin1, isBin2;
285307
const char *zOrig = db_column_text(&q, 4);
286308
const char *zNew = db_column_text(&q, 5);
287309
char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
288
- Blob delta;
310
+ Blob delta, a, b, disk;
289311
if( rid==0 ){
290
- db_ephemeral_blob(&q, 6, &delta);
312
+ db_ephemeral_blob(&q, 6, &a);
291313
fossil_print("ADDED %s\n", zNew);
292314
diff_print_index(zNew, diffFlags);
293
- diff_file_mem(&empty, &delta, zNew, zDiffCmd, diffFlags);
315
+ isBin1 = 0;
316
+ isBin2 = fIncludeBinary ? 0 : looks_like_binary(&a);
317
+ diff_file_mem(&empty, &a, isBin1, isBin2, zNew, zDiffCmd,
318
+ zBinGlob, fIncludeBinary, diffFlags);
294319
}else if( isRemoved ){
295320
fossil_print("DELETE %s\n", zOrig);
296
- if( file_wd_islink(zOPath) ){
297
- blob_read_link(&delta, zOPath);
321
+ if( fBaseline==0 ){
322
+ if( file_wd_islink(zOPath) ){
323
+ blob_read_link(&a, zOPath);
324
+ }else{
325
+ blob_read_from_file(&a, zOPath);
326
+ }
298327
}else{
299
- blob_read_from_file(&delta, zOPath);
328
+ content_get(rid, &a);
300329
}
301330
diff_print_index(zNew, diffFlags);
302
- diff_file_mem(&delta, &empty, zOrig, zDiffCmd, diffFlags);
331
+ isBin1 = fIncludeBinary ? 0 : looks_like_binary(&a);
332
+ isBin2 = 0;
333
+ diff_file_mem(&a, &empty, isBin1, isBin2, zOrig, zDiffCmd,
334
+ zBinGlob, fIncludeBinary, diffFlags);
303335
}else{
304
- Blob a, b, disk;
305336
int isOrigLink = file_wd_islink(zOPath);
306337
db_ephemeral_blob(&q, 6, &delta);
307
- if( isOrigLink ){
308
- blob_read_link(&disk, zOPath);
309
- }else{
310
- blob_read_from_file(&disk, zOPath);
338
+ if( fBaseline==0 ){
339
+ if( isOrigLink ){
340
+ blob_read_link(&disk, zOPath);
341
+ }else{
342
+ blob_read_from_file(&disk, zOPath);
343
+ }
311344
}
312345
fossil_print("CHANGED %s\n", zNew);
313346
if( !isOrigLink != !isLink ){
314347
diff_print_index(zNew, diffFlags);
315348
diff_print_filenames(zOrig, zNew, diffFlags);
316
- printf("cannot compute difference between symlink and regular file\n");
349
+ printf(DIFF_CANNOT_COMPUTE_SYMLINK);
317350
}else{
351
+ Blob *pBase = fBaseline ? &a : &disk;
318352
content_get(rid, &a);
319353
blob_delta_apply(&a, &delta, &b);
320
- diff_file_mem(&disk, &b, zNew, zDiffCmd, diffFlags);
354
+ isBin1 = fIncludeBinary ? 0 : looks_like_binary(pBase);
355
+ isBin2 = fIncludeBinary ? 0 : looks_like_binary(&b);
356
+ diff_file_mem(fBaseline? &a : &disk, &b, isBin1, isBin2, zNew,
357
+ zDiffCmd, zBinGlob, fIncludeBinary, diffFlags);
321358
blob_reset(&a);
322359
blob_reset(&b);
323360
}
324
- blob_reset(&disk);
361
+ if( !fBaseline ) blob_reset(&disk);
325362
}
326363
blob_reset(&delta);
327364
}
328365
db_finalize(&q);
329366
}
@@ -379,10 +416,14 @@
379416
** fossil stash ls ?-l?
380417
**
381418
** List all changes sets currently stashed. Show information about
382419
** individual files in each changeset if --detail or -l is used.
383420
**
421
+** fossil stash show ?STASHID? ?DIFF-FLAGS?
422
+**
423
+** Show the content of a stash
424
+**
384425
** fossil stash pop
385426
** fossil stash apply ?STASHID?
386427
**
387428
** Apply STASHID or the most recently create stash to the current
388429
** working check-out. The "pop" command deletes that changeset from
@@ -404,19 +445,32 @@
404445
** fossil stash diff ?STASHID?
405446
** fossil stash gdiff ?STASHID?
406447
**
407448
** Show diffs of the current working directory and what that
408449
** directory would be if STASHID were applied.
450
+**
451
+** SUMMARY:
452
+** fossil stash
453
+** fossil stash save ?-m COMMENT? ?FILES...?
454
+** fossil stash snapshot ?-m COMMENT? ?FILES...?
455
+** fossil stash list|ls ?-l? ?--detail?
456
+** fossil stash show ?STASHID? ?DIFF-OPTIONS?
457
+** fossil stash pop
458
+** fossil stash apply ?STASHID?
459
+** fossil stash goto ?STASHID?
460
+** fossil stash rm|drop ?STASHID? ?--all?
461
+** fossil stash [g]diff ?STASHID? ?DIFF-OPTIONS?
409462
*/
410463
void stash_cmd(void){
411464
const char *zDb;
412465
const char *zCmd;
413466
int nCmd;
414467
int stashid;
415468
416469
undo_capture_command_line();
417470
db_must_be_within_tree();
471
+ db_open_config(0);
418472
db_begin_transaction();
419473
zDb = db_name("localdb");
420474
db_multi_exec(zStashInit, zDb, zDb);
421475
if( g.argc<=2 ){
422476
zCmd = "save";
@@ -548,23 +602,36 @@
548602
db_multi_exec("UPDATE vfile SET mtime=0 WHERE pathname IN "
549603
"(SELECT origname FROM stashfile WHERE stashid=%d)",
550604
stashid);
551605
undo_finish();
552606
}else
553
- if( memcmp(zCmd, "diff", nCmd)==0 ){
554
- const char *zDiffCmd = diff_command_external(0);
555
- u64 diffFlags = diff_options();
556
- if( g.argc>4 ) usage("diff STASHID");
557
- stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
558
- stash_diff(stashid, zDiffCmd, diffFlags);
559
- }else
560
- if( memcmp(zCmd, "gdiff", nCmd)==0 ){
561
- const char *zDiffCmd = diff_command_external(1);
562
- u64 diffFlags = diff_options();
563
- if( g.argc>4 ) usage("gdiff STASHID");
564
- stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
565
- stash_diff(stashid, zDiffCmd, diffFlags);
607
+ if( memcmp(zCmd, "diff", nCmd)==0
608
+ || memcmp(zCmd, "gdiff", nCmd)==0
609
+ || memcmp(zCmd, "show", nCmd)==0
610
+ ){
611
+ const char *zDiffCmd = 0;
612
+ const char *zBinGlob = 0;
613
+ int fIncludeBinary = 0;
614
+ u64 diffFlags;
615
+
616
+ if( find_option("tk",0,0)!=0 ){
617
+ db_close(0);
618
+ diff_tk((zCmd[0]=='s' ? "stash show" : "stash diff"), 3);
619
+ return;
620
+ }
621
+ if( find_option("internal","i",0)==0 ){
622
+ zDiffCmd = diff_command_external(0);
623
+ }
624
+ diffFlags = diff_options();
625
+ if( g.argc>4 ) usage(mprintf("%s STASHID", zCmd));
626
+ if( zDiffCmd ){
627
+ zBinGlob = diff_get_binary_glob();
628
+ fIncludeBinary = diff_include_binary_files();
629
+ }
630
+ stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
631
+ stash_diff(stashid, zDiffCmd, zBinGlob, zCmd[0]=='s', fIncludeBinary,
632
+ diffFlags);
566633
}else
567634
if( memcmp(zCmd, "help", nCmd)==0 ){
568635
g.argv[1] = "help";
569636
g.argv[2] = "stash";
570637
g.argc = 3;
571638
--- src/stash.c
+++ src/stash.c
@@ -155,10 +155,24 @@
155 int stashid; /* ID of the new stash */
156 int vid; /* Current checkout */
157
158 zComment = find_option("comment", "m", 1);
159 verify_all_options();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160 stashid = db_lget_int("stash-next", 1);
161 db_lset_int("stash-next", stashid+1);
162 vid = db_lget_int("checkout", 0);
163 vfile_check_signature(vid, 0, 0);
164 db_multi_exec(
@@ -267,11 +281,18 @@
267 }
268
269 /*
270 ** Show the diffs associate with a single stash.
271 */
272 static void stash_diff(int stashid, const char *zDiffCmd, u64 diffFlags){
 
 
 
 
 
 
 
273 Stmt q;
274 Blob empty;
275 blob_zero(&empty);
276 db_prepare(&q,
277 "SELECT rid, isRemoved, isExec, isLink, origname, newname, delta"
@@ -280,50 +301,66 @@
280 );
281 while( db_step(&q)==SQLITE_ROW ){
282 int rid = db_column_int(&q, 0);
283 int isRemoved = db_column_int(&q, 1);
284 int isLink = db_column_int(&q, 3);
 
285 const char *zOrig = db_column_text(&q, 4);
286 const char *zNew = db_column_text(&q, 5);
287 char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
288 Blob delta;
289 if( rid==0 ){
290 db_ephemeral_blob(&q, 6, &delta);
291 fossil_print("ADDED %s\n", zNew);
292 diff_print_index(zNew, diffFlags);
293 diff_file_mem(&empty, &delta, zNew, zDiffCmd, diffFlags);
 
 
 
294 }else if( isRemoved ){
295 fossil_print("DELETE %s\n", zOrig);
296 if( file_wd_islink(zOPath) ){
297 blob_read_link(&delta, zOPath);
 
 
 
 
298 }else{
299 blob_read_from_file(&delta, zOPath);
300 }
301 diff_print_index(zNew, diffFlags);
302 diff_file_mem(&delta, &empty, zOrig, zDiffCmd, diffFlags);
 
 
 
303 }else{
304 Blob a, b, disk;
305 int isOrigLink = file_wd_islink(zOPath);
306 db_ephemeral_blob(&q, 6, &delta);
307 if( isOrigLink ){
308 blob_read_link(&disk, zOPath);
309 }else{
310 blob_read_from_file(&disk, zOPath);
 
 
311 }
312 fossil_print("CHANGED %s\n", zNew);
313 if( !isOrigLink != !isLink ){
314 diff_print_index(zNew, diffFlags);
315 diff_print_filenames(zOrig, zNew, diffFlags);
316 printf("cannot compute difference between symlink and regular file\n");
317 }else{
 
318 content_get(rid, &a);
319 blob_delta_apply(&a, &delta, &b);
320 diff_file_mem(&disk, &b, zNew, zDiffCmd, diffFlags);
 
 
 
321 blob_reset(&a);
322 blob_reset(&b);
323 }
324 blob_reset(&disk);
325 }
326 blob_reset(&delta);
327 }
328 db_finalize(&q);
329 }
@@ -379,10 +416,14 @@
379 ** fossil stash ls ?-l?
380 **
381 ** List all changes sets currently stashed. Show information about
382 ** individual files in each changeset if --detail or -l is used.
383 **
 
 
 
 
384 ** fossil stash pop
385 ** fossil stash apply ?STASHID?
386 **
387 ** Apply STASHID or the most recently create stash to the current
388 ** working check-out. The "pop" command deletes that changeset from
@@ -404,19 +445,32 @@
404 ** fossil stash diff ?STASHID?
405 ** fossil stash gdiff ?STASHID?
406 **
407 ** Show diffs of the current working directory and what that
408 ** directory would be if STASHID were applied.
 
 
 
 
 
 
 
 
 
 
 
 
409 */
410 void stash_cmd(void){
411 const char *zDb;
412 const char *zCmd;
413 int nCmd;
414 int stashid;
415
416 undo_capture_command_line();
417 db_must_be_within_tree();
 
418 db_begin_transaction();
419 zDb = db_name("localdb");
420 db_multi_exec(zStashInit, zDb, zDb);
421 if( g.argc<=2 ){
422 zCmd = "save";
@@ -548,23 +602,36 @@
548 db_multi_exec("UPDATE vfile SET mtime=0 WHERE pathname IN "
549 "(SELECT origname FROM stashfile WHERE stashid=%d)",
550 stashid);
551 undo_finish();
552 }else
553 if( memcmp(zCmd, "diff", nCmd)==0 ){
554 const char *zDiffCmd = diff_command_external(0);
555 u64 diffFlags = diff_options();
556 if( g.argc>4 ) usage("diff STASHID");
557 stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
558 stash_diff(stashid, zDiffCmd, diffFlags);
559 }else
560 if( memcmp(zCmd, "gdiff", nCmd)==0 ){
561 const char *zDiffCmd = diff_command_external(1);
562 u64 diffFlags = diff_options();
563 if( g.argc>4 ) usage("gdiff STASHID");
564 stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
565 stash_diff(stashid, zDiffCmd, diffFlags);
 
 
 
 
 
 
 
 
 
 
 
 
 
566 }else
567 if( memcmp(zCmd, "help", nCmd)==0 ){
568 g.argv[1] = "help";
569 g.argv[2] = "stash";
570 g.argc = 3;
571
--- src/stash.c
+++ src/stash.c
@@ -155,10 +155,24 @@
155 int stashid; /* ID of the new stash */
156 int vid; /* Current checkout */
157
158 zComment = find_option("comment", "m", 1);
159 verify_all_options();
160 if( zComment==0 ){
161 Blob prompt; /* Prompt for stash comment */
162 Blob comment; /* User comment reply */
163 blob_zero(&prompt);
164 blob_append(&prompt,
165 "\n"
166 "# Enter a description of what is being stashed. Lines beginning\n"
167 "# with \"#\" are ignored. Stash comments are plain text except.\n"
168 "# newlines are not preserved.\n",
169 -1);
170 prompt_for_user_comment(&comment, &prompt);
171 blob_reset(&prompt);
172 zComment = blob_str(&comment);
173 }
174 stashid = db_lget_int("stash-next", 1);
175 db_lset_int("stash-next", stashid+1);
176 vid = db_lget_int("checkout", 0);
177 vfile_check_signature(vid, 0, 0);
178 db_multi_exec(
@@ -267,11 +281,18 @@
281 }
282
283 /*
284 ** Show the diffs associate with a single stash.
285 */
286 static void stash_diff(
287 int stashid, /* The stash entry to diff */
288 const char *zDiffCmd, /* Command used for diffing */
289 const char *zBinGlob, /* GLOB pattern to determine binary files */
290 int fBaseline, /* Diff against original baseline check-in if true */
291 int fIncludeBinary, /* Do diffs against binary files */
292 u64 diffFlags /* Other diff flags */
293 ){
294 Stmt q;
295 Blob empty;
296 blob_zero(&empty);
297 db_prepare(&q,
298 "SELECT rid, isRemoved, isExec, isLink, origname, newname, delta"
@@ -280,50 +301,66 @@
301 );
302 while( db_step(&q)==SQLITE_ROW ){
303 int rid = db_column_int(&q, 0);
304 int isRemoved = db_column_int(&q, 1);
305 int isLink = db_column_int(&q, 3);
306 int isBin1, isBin2;
307 const char *zOrig = db_column_text(&q, 4);
308 const char *zNew = db_column_text(&q, 5);
309 char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
310 Blob delta, a, b, disk;
311 if( rid==0 ){
312 db_ephemeral_blob(&q, 6, &a);
313 fossil_print("ADDED %s\n", zNew);
314 diff_print_index(zNew, diffFlags);
315 isBin1 = 0;
316 isBin2 = fIncludeBinary ? 0 : looks_like_binary(&a);
317 diff_file_mem(&empty, &a, isBin1, isBin2, zNew, zDiffCmd,
318 zBinGlob, fIncludeBinary, diffFlags);
319 }else if( isRemoved ){
320 fossil_print("DELETE %s\n", zOrig);
321 if( fBaseline==0 ){
322 if( file_wd_islink(zOPath) ){
323 blob_read_link(&a, zOPath);
324 }else{
325 blob_read_from_file(&a, zOPath);
326 }
327 }else{
328 content_get(rid, &a);
329 }
330 diff_print_index(zNew, diffFlags);
331 isBin1 = fIncludeBinary ? 0 : looks_like_binary(&a);
332 isBin2 = 0;
333 diff_file_mem(&a, &empty, isBin1, isBin2, zOrig, zDiffCmd,
334 zBinGlob, fIncludeBinary, diffFlags);
335 }else{
 
336 int isOrigLink = file_wd_islink(zOPath);
337 db_ephemeral_blob(&q, 6, &delta);
338 if( fBaseline==0 ){
339 if( isOrigLink ){
340 blob_read_link(&disk, zOPath);
341 }else{
342 blob_read_from_file(&disk, zOPath);
343 }
344 }
345 fossil_print("CHANGED %s\n", zNew);
346 if( !isOrigLink != !isLink ){
347 diff_print_index(zNew, diffFlags);
348 diff_print_filenames(zOrig, zNew, diffFlags);
349 printf(DIFF_CANNOT_COMPUTE_SYMLINK);
350 }else{
351 Blob *pBase = fBaseline ? &a : &disk;
352 content_get(rid, &a);
353 blob_delta_apply(&a, &delta, &b);
354 isBin1 = fIncludeBinary ? 0 : looks_like_binary(pBase);
355 isBin2 = fIncludeBinary ? 0 : looks_like_binary(&b);
356 diff_file_mem(fBaseline? &a : &disk, &b, isBin1, isBin2, zNew,
357 zDiffCmd, zBinGlob, fIncludeBinary, diffFlags);
358 blob_reset(&a);
359 blob_reset(&b);
360 }
361 if( !fBaseline ) blob_reset(&disk);
362 }
363 blob_reset(&delta);
364 }
365 db_finalize(&q);
366 }
@@ -379,10 +416,14 @@
416 ** fossil stash ls ?-l?
417 **
418 ** List all changes sets currently stashed. Show information about
419 ** individual files in each changeset if --detail or -l is used.
420 **
421 ** fossil stash show ?STASHID? ?DIFF-FLAGS?
422 **
423 ** Show the content of a stash
424 **
425 ** fossil stash pop
426 ** fossil stash apply ?STASHID?
427 **
428 ** Apply STASHID or the most recently create stash to the current
429 ** working check-out. The "pop" command deletes that changeset from
@@ -404,19 +445,32 @@
445 ** fossil stash diff ?STASHID?
446 ** fossil stash gdiff ?STASHID?
447 **
448 ** Show diffs of the current working directory and what that
449 ** directory would be if STASHID were applied.
450 **
451 ** SUMMARY:
452 ** fossil stash
453 ** fossil stash save ?-m COMMENT? ?FILES...?
454 ** fossil stash snapshot ?-m COMMENT? ?FILES...?
455 ** fossil stash list|ls ?-l? ?--detail?
456 ** fossil stash show ?STASHID? ?DIFF-OPTIONS?
457 ** fossil stash pop
458 ** fossil stash apply ?STASHID?
459 ** fossil stash goto ?STASHID?
460 ** fossil stash rm|drop ?STASHID? ?--all?
461 ** fossil stash [g]diff ?STASHID? ?DIFF-OPTIONS?
462 */
463 void stash_cmd(void){
464 const char *zDb;
465 const char *zCmd;
466 int nCmd;
467 int stashid;
468
469 undo_capture_command_line();
470 db_must_be_within_tree();
471 db_open_config(0);
472 db_begin_transaction();
473 zDb = db_name("localdb");
474 db_multi_exec(zStashInit, zDb, zDb);
475 if( g.argc<=2 ){
476 zCmd = "save";
@@ -548,23 +602,36 @@
602 db_multi_exec("UPDATE vfile SET mtime=0 WHERE pathname IN "
603 "(SELECT origname FROM stashfile WHERE stashid=%d)",
604 stashid);
605 undo_finish();
606 }else
607 if( memcmp(zCmd, "diff", nCmd)==0
608 || memcmp(zCmd, "gdiff", nCmd)==0
609 || memcmp(zCmd, "show", nCmd)==0
610 ){
611 const char *zDiffCmd = 0;
612 const char *zBinGlob = 0;
613 int fIncludeBinary = 0;
614 u64 diffFlags;
615
616 if( find_option("tk",0,0)!=0 ){
617 db_close(0);
618 diff_tk((zCmd[0]=='s' ? "stash show" : "stash diff"), 3);
619 return;
620 }
621 if( find_option("internal","i",0)==0 ){
622 zDiffCmd = diff_command_external(0);
623 }
624 diffFlags = diff_options();
625 if( g.argc>4 ) usage(mprintf("%s STASHID", zCmd));
626 if( zDiffCmd ){
627 zBinGlob = diff_get_binary_glob();
628 fIncludeBinary = diff_include_binary_files();
629 }
630 stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
631 stash_diff(stashid, zDiffCmd, zBinGlob, zCmd[0]=='s', fIncludeBinary,
632 diffFlags);
633 }else
634 if( memcmp(zCmd, "help", nCmd)==0 ){
635 g.argv[1] = "help";
636 g.argv[2] = "stash";
637 g.argc = 3;
638
+10 -3
--- src/th_main.c
+++ src/th_main.c
@@ -221,13 +221,14 @@
221221
** TH command: hasfeature STRING
222222
**
223223
** Return true if the fossil binary has the given compile-time feature
224224
** enabled. The set of features includes:
225225
**
226
-** "json" = FOSSIL_ENABLE_JSON
227
-** "tcl" = FOSSIL_ENABLE_TCL
228
-** "ssl" = FOSSIL_ENABLE_SSL
226
+** "json" = FOSSIL_ENABLE_JSON
227
+** "ssl" = FOSSIL_ENABLE_SSL
228
+** "tcl" = FOSSIL_ENABLE_TCL
229
+** "tclStubs" = FOSSIL_ENABLE_TCL_STUBS
229230
**
230231
*/
231232
static int hasfeatureCmd(
232233
Th_Interp *interp,
233234
void *p,
@@ -256,10 +257,15 @@
256257
#endif
257258
#if defined(FOSSIL_ENABLE_TCL)
258259
else if( 0 == fossil_strnicmp( zArg, "tcl", 3 ) ){
259260
rc = 1;
260261
}
262
+#endif
263
+#if defined(FOSSIL_ENABLE_TCL_STUBS)
264
+ else if( 0 == fossil_strnicmp( zArg, "tclStubs", 8 ) ){
265
+ rc = 1;
266
+ }
261267
#endif
262268
if( g.thTrace ){
263269
Th_Trace("[hasfeature %#h] => %d<br />\n", argl[1], zArg, rc);
264270
}
265271
Th_SetResultInt(interp, rc);
@@ -447,10 +453,11 @@
447453
int i;
448454
g.interp = Th_CreateInterp(&vtab);
449455
th_register_language(g.interp); /* Basic scripting commands. */
450456
#ifdef FOSSIL_ENABLE_TCL
451457
if( getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
458
+ g.tcl.setup = db_get("tcl-setup", 0); /* Grab optional setup script. */
452459
th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
453460
}
454461
#endif
455462
for(i=0; i<sizeof(aCommand)/sizeof(aCommand[0]); i++){
456463
if ( !aCommand[i].zName || !aCommand[i].xProc ) continue;
457464
--- src/th_main.c
+++ src/th_main.c
@@ -221,13 +221,14 @@
221 ** TH command: hasfeature STRING
222 **
223 ** Return true if the fossil binary has the given compile-time feature
224 ** enabled. The set of features includes:
225 **
226 ** "json" = FOSSIL_ENABLE_JSON
227 ** "tcl" = FOSSIL_ENABLE_TCL
228 ** "ssl" = FOSSIL_ENABLE_SSL
 
229 **
230 */
231 static int hasfeatureCmd(
232 Th_Interp *interp,
233 void *p,
@@ -256,10 +257,15 @@
256 #endif
257 #if defined(FOSSIL_ENABLE_TCL)
258 else if( 0 == fossil_strnicmp( zArg, "tcl", 3 ) ){
259 rc = 1;
260 }
 
 
 
 
 
261 #endif
262 if( g.thTrace ){
263 Th_Trace("[hasfeature %#h] => %d<br />\n", argl[1], zArg, rc);
264 }
265 Th_SetResultInt(interp, rc);
@@ -447,10 +453,11 @@
447 int i;
448 g.interp = Th_CreateInterp(&vtab);
449 th_register_language(g.interp); /* Basic scripting commands. */
450 #ifdef FOSSIL_ENABLE_TCL
451 if( getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
 
452 th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
453 }
454 #endif
455 for(i=0; i<sizeof(aCommand)/sizeof(aCommand[0]); i++){
456 if ( !aCommand[i].zName || !aCommand[i].xProc ) continue;
457
--- src/th_main.c
+++ src/th_main.c
@@ -221,13 +221,14 @@
221 ** TH command: hasfeature STRING
222 **
223 ** Return true if the fossil binary has the given compile-time feature
224 ** enabled. The set of features includes:
225 **
226 ** "json" = FOSSIL_ENABLE_JSON
227 ** "ssl" = FOSSIL_ENABLE_SSL
228 ** "tcl" = FOSSIL_ENABLE_TCL
229 ** "tclStubs" = FOSSIL_ENABLE_TCL_STUBS
230 **
231 */
232 static int hasfeatureCmd(
233 Th_Interp *interp,
234 void *p,
@@ -256,10 +257,15 @@
257 #endif
258 #if defined(FOSSIL_ENABLE_TCL)
259 else if( 0 == fossil_strnicmp( zArg, "tcl", 3 ) ){
260 rc = 1;
261 }
262 #endif
263 #if defined(FOSSIL_ENABLE_TCL_STUBS)
264 else if( 0 == fossil_strnicmp( zArg, "tclStubs", 8 ) ){
265 rc = 1;
266 }
267 #endif
268 if( g.thTrace ){
269 Th_Trace("[hasfeature %#h] => %d<br />\n", argl[1], zArg, rc);
270 }
271 Th_SetResultInt(interp, rc);
@@ -447,10 +453,11 @@
453 int i;
454 g.interp = Th_CreateInterp(&vtab);
455 th_register_language(g.interp); /* Basic scripting commands. */
456 #ifdef FOSSIL_ENABLE_TCL
457 if( getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
458 g.tcl.setup = db_get("tcl-setup", 0); /* Grab optional setup script. */
459 th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
460 }
461 #endif
462 for(i=0; i<sizeof(aCommand)/sizeof(aCommand[0]); i++){
463 if ( !aCommand[i].zName || !aCommand[i].xProc ) continue;
464
+242 -17
--- src/th_tcl.c
+++ src/th_tcl.c
@@ -32,11 +32,11 @@
3232
((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 6))
3333
/*
3434
** Workaround NRE-specific issue in Tcl_EvalObjCmd (SF bug #3399564) by using
3535
** Tcl_EvalObjv instead of invoking the objProc directly.
3636
*/
37
-#define USE_TCL_EVALOBJV 1
37
+# define USE_TCL_EVALOBJV 1
3838
#endif
3939
4040
/*
4141
** These macros are designed to reduce the redundant code required to marshal
4242
** arguments from TH1 to Tcl.
@@ -64,10 +64,94 @@
6464
** Fetch the Tcl interpreter from the specified void pointer, cast to a Tcl
6565
** context.
6666
*/
6767
#define GET_CTX_TCL_INTERP(ctx) \
6868
((struct TclContext *)(ctx))->interp
69
+
70
+/*
71
+** Define the Tcl shared library name, some exported function names, and some
72
+** cross-platform macros for use with the Tcl stubs mechanism, when enabled.
73
+ */
74
+#if defined(USE_TCL_STUBS)
75
+# if defined(_WIN32)
76
+# define WIN32_LEAN_AND_MEAN
77
+# include <windows.h>
78
+# ifndef TCL_LIBRARY_NAME
79
+# define TCL_LIBRARY_NAME "tcl86.dll\0"
80
+# endif
81
+# ifndef TCL_MINOR_OFFSET
82
+# define TCL_MINOR_OFFSET (4)
83
+# endif
84
+# ifndef dlopen
85
+# define dlopen(a,b) (void *)LoadLibrary((a))
86
+# endif
87
+# ifndef dlsym
88
+# define dlsym(a,b) GetProcAddress((HANDLE)(a),(b))
89
+# endif
90
+# ifndef dlclose
91
+# define dlclose(a) FreeLibrary((HANDLE)(a))
92
+# endif
93
+# else
94
+# include <dlfcn.h>
95
+# if defined(__CYGWIN__)
96
+# ifndef TCL_LIBRARY_NAME
97
+# define TCL_LIBRARY_NAME "libtcl8.6.dll\0"
98
+# endif
99
+# ifndef TCL_MINOR_OFFSET
100
+# define TCL_MINOR_OFFSET (8)
101
+# endif
102
+# elif defined(__APPLE__)
103
+# ifndef TCL_LIBRARY_NAME
104
+# define TCL_LIBRARY_NAME "libtcl8.6.dylib\0"
105
+# endif
106
+# ifndef TCL_MINOR_OFFSET
107
+# define TCL_MINOR_OFFSET (8)
108
+# endif
109
+# else
110
+# ifndef TCL_LIBRARY_NAME
111
+# define TCL_LIBRARY_NAME "libtcl8.6.so\0"
112
+# endif
113
+# ifndef TCL_MINOR_OFFSET
114
+# define TCL_MINOR_OFFSET (8)
115
+# endif
116
+# endif /* defined(__CYGWIN__) */
117
+# endif /* defined(_WIN32) */
118
+# ifndef TCL_FINDEXECUTABLE_NAME
119
+# define TCL_FINDEXECUTABLE_NAME "_Tcl_FindExecutable"
120
+# endif
121
+# ifndef TCL_CREATEINTERP_NAME
122
+# define TCL_CREATEINTERP_NAME "_Tcl_CreateInterp"
123
+# endif
124
+#endif /* defined(USE_TCL_STUBS) */
125
+
126
+/*
127
+** The function types for Tcl_FindExecutable and Tcl_CreateInterp are needed
128
+** when the Tcl library is being loaded dynamically by a stubs-enabled
129
+** application (i.e. the inverse of using a stubs-enabled package). These are
130
+** the only Tcl API functions that MUST be called prior to being able to call
131
+** Tcl_InitStubs (i.e. because it requires a Tcl interpreter).
132
+ */
133
+typedef void (tcl_FindExecutableProc) (CONST char * argv0);
134
+typedef Tcl_Interp *(tcl_CreateInterpProc) (void);
135
+
136
+/*
137
+** The function types for the "hook" functions to be called before and after a
138
+** TH1 command makes a call to evaluate a Tcl script. If the "pre" function
139
+** returns anything but TH_OK, then evaluation of the Tcl script is skipped and
140
+** that value is used as the return code. If the "post" function returns
141
+** anything other than its rc argument, that will become the new return code
142
+** for the command.
143
+ */
144
+typedef int (tcl_NotifyProc) (
145
+ void *pContext, /* The context for this notification. */
146
+ Th_Interp *interp, /* The TH1 interpreter being used. */
147
+ void *ctx, /* The original TH1 command context. */
148
+ int argc, /* Number of arguments for the TH1 command. */
149
+ const char **argv, /* Array of arguments for the TH1 command. */
150
+ int *argl, /* Array of lengths for the TH1 command arguments. */
151
+ int rc /* Recommended notification return value. */
152
+);
69153
70154
/*
71155
** Creates and initializes a Tcl interpreter for use with the specified TH1
72156
** interpreter. Stores the created Tcl interpreter in the Tcl context supplied
73157
** by the caller. This must be declared here because quite a few functions in
@@ -100,14 +184,54 @@
100184
/*
101185
** Tcl context information used by TH1. This structure definition has been
102186
** copied from and should be kept in sync with the one in "main.c".
103187
*/
104188
struct TclContext {
105
- int argc;
106
- char **argv;
107
- Tcl_Interp *interp;
189
+ int argc; /* Number of original arguments. */
190
+ char **argv; /* Full copy of the original arguments. */
191
+ void *library; /* The Tcl library module handle. */
192
+ tcl_FindExecutableProc *xFindExecutable; /* Tcl_FindExecutable() pointer. */
193
+ tcl_CreateInterpProc *xCreateInterp; /* Tcl_CreateInterp() pointer. */
194
+ Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */
195
+ char *setup; /* The optional Tcl setup script. */
196
+ tcl_NotifyProc *xPreEval; /* Optional, called before Tcl_Eval*(). */
197
+ void *pPreContext; /* Optional, provided to xPreEval(). */
198
+ tcl_NotifyProc *xPostEval; /* Optional, called after Tcl_Eval*(). */
199
+ void *pPostContext; /* Optional, provided to xPostEval(). */
108200
};
201
+
202
+/*
203
+** This function calls the configured xPreEval or xPostEval functions, if any.
204
+** May have arbitrary side-effects. This function returns the result of the
205
+** called notification function or the value of the rc argument if there is no
206
+** notification function configured.
207
+*/
208
+static int notifyPreOrPostEval(
209
+ int bIsPost,
210
+ Th_Interp *interp,
211
+ void *ctx,
212
+ int argc,
213
+ const char **argv,
214
+ int *argl,
215
+ int rc
216
+){
217
+ struct TclContext *tclContext = (struct TclContext *)ctx;
218
+ tcl_NotifyProc *xNotifyProc;
219
+
220
+ if ( !tclContext ){
221
+ Th_ErrorMessage(interp,
222
+ "Invalid Tcl context", (const char *)"", 0);
223
+ return TH_ERROR;
224
+ }
225
+ xNotifyProc = bIsPost ? tclContext->xPostEval : tclContext->xPreEval;
226
+ if ( xNotifyProc ){
227
+ rc = xNotifyProc(bIsPost ?
228
+ tclContext->pPostContext : tclContext->pPreContext,
229
+ interp, ctx, argc, argv, argl, rc);
230
+ }
231
+ return rc;
232
+}
109233
110234
/*
111235
** Syntax:
112236
**
113237
** tclEval arg ?arg ...?
@@ -119,24 +243,28 @@
119243
const char **argv,
120244
int *argl
121245
){
122246
Tcl_Interp *tclInterp;
123247
Tcl_Obj *objPtr;
124
- int rc;
248
+ int rc = TH_OK;
125249
int nResult;
126250
const char *zResult;
127251
128
- if ( createTclInterp(interp, ctx)!=TH_OK ){
252
+ if( createTclInterp(interp, ctx)!=TH_OK ){
129253
return TH_ERROR;
130254
}
131255
if( argc<2 ){
132256
return Th_WrongNumArgs(interp, "tclEval arg ?arg ...?");
133257
}
134258
tclInterp = GET_CTX_TCL_INTERP(ctx);
135259
if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){
136260
Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0);
137261
return TH_ERROR;
262
+ }
263
+ rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, argl, rc);
264
+ if( rc!=TH_OK ){
265
+ return rc;
138266
}
139267
Tcl_Preserve((ClientData)tclInterp);
140268
if( argc==2 ){
141269
objPtr = Tcl_NewStringObj(argv[1], argl[1]);
142270
Tcl_IncrRefCount(objPtr);
@@ -152,10 +280,11 @@
152280
FREE_ARGV_TO_OBJV();
153281
}
154282
zResult = getTclResult(tclInterp, &nResult);
155283
Th_SetResult(interp, zResult, nResult);
156284
Tcl_Release((ClientData)tclInterp);
285
+ rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, rc);
157286
return rc;
158287
}
159288
160289
/*
161290
** Syntax:
@@ -170,24 +299,28 @@
170299
int *argl
171300
){
172301
Tcl_Interp *tclInterp;
173302
Tcl_Obj *objPtr;
174303
Tcl_Obj *resultObjPtr;
175
- int rc;
304
+ int rc = TH_OK;
176305
int nResult;
177306
const char *zResult;
178307
179
- if ( createTclInterp(interp, ctx)!=TH_OK ){
308
+ if( createTclInterp(interp, ctx)!=TH_OK ){
180309
return TH_ERROR;
181310
}
182311
if( argc<2 ){
183312
return Th_WrongNumArgs(interp, "tclExpr arg ?arg ...?");
184313
}
185314
tclInterp = GET_CTX_TCL_INTERP(ctx);
186315
if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){
187316
Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0);
188317
return TH_ERROR;
318
+ }
319
+ rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, argl, rc);
320
+ if( rc!=TH_OK ){
321
+ return rc;
189322
}
190323
Tcl_Preserve((ClientData)tclInterp);
191324
if( argc==2 ){
192325
objPtr = Tcl_NewStringObj(argv[1], argl[1]);
193326
Tcl_IncrRefCount(objPtr);
@@ -208,10 +341,11 @@
208341
zResult = getTclResult(tclInterp, &nResult);
209342
}
210343
Th_SetResult(interp, zResult, nResult);
211344
if( rc==TCL_OK ) Tcl_DecrRefCount(resultObjPtr);
212345
Tcl_Release((ClientData)tclInterp);
346
+ rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, rc);
213347
return rc;
214348
}
215349
216350
/*
217351
** Syntax:
@@ -224,35 +358,39 @@
224358
int argc,
225359
const char **argv,
226360
int *argl
227361
){
228362
Tcl_Interp *tclInterp;
229
-#ifndef USE_TCL_EVALOBJV
363
+#if !defined(USE_TCL_EVALOBJV)
230364
Tcl_Command command;
231365
Tcl_CmdInfo cmdInfo;
232366
#endif
233
- int rc;
367
+ int rc = TH_OK;
234368
int nResult;
235369
const char *zResult;
236
-#ifndef USE_TCL_EVALOBJV
370
+#if !defined(USE_TCL_EVALOBJV)
237371
Tcl_Obj *objPtr;
238372
#endif
239373
USE_ARGV_TO_OBJV();
240374
241
- if ( createTclInterp(interp, ctx)!=TH_OK ){
375
+ if( createTclInterp(interp, ctx)!=TH_OK ){
242376
return TH_ERROR;
243377
}
244378
if( argc<2 ){
245379
return Th_WrongNumArgs(interp, "tclInvoke command ?arg ...?");
246380
}
247381
tclInterp = GET_CTX_TCL_INTERP(ctx);
248382
if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){
249383
Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0);
250384
return TH_ERROR;
385
+ }
386
+ rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, argl, rc);
387
+ if( rc!=TH_OK ){
388
+ return rc;
251389
}
252390
Tcl_Preserve((ClientData)tclInterp);
253
-#ifndef USE_TCL_EVALOBJV
391
+#if !defined(USE_TCL_EVALOBJV)
254392
objPtr = Tcl_NewStringObj(argv[1], argl[1]);
255393
Tcl_IncrRefCount(objPtr);
256394
command = Tcl_GetCommandFromObj(tclInterp, objPtr);
257395
if( !command || Tcl_GetCommandInfoFromToken(command,&cmdInfo)==0 ){
258396
Th_ErrorMessage(interp, "Tcl command not found:", argv[1], argl[1]);
@@ -267,20 +405,21 @@
267405
return TH_ERROR;
268406
}
269407
Tcl_DecrRefCount(objPtr);
270408
#endif
271409
COPY_ARGV_TO_OBJV();
272
-#ifdef USE_TCL_EVALOBJV
410
+#if defined(USE_TCL_EVALOBJV)
273411
rc = Tcl_EvalObjv(tclInterp, objc, objv, 0);
274412
#else
275413
Tcl_ResetResult(tclInterp);
276414
rc = cmdInfo.objProc(cmdInfo.objClientData, tclInterp, objc, objv);
277415
#endif
278416
FREE_ARGV_TO_OBJV();
279417
zResult = getTclResult(tclInterp, &nResult);
280418
Th_SetResult(interp, zResult, nResult);
281419
Tcl_Release((ClientData)tclInterp);
420
+ rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, rc);
282421
return rc;
283422
}
284423
285424
/*
286425
** Syntax:
@@ -375,10 +514,77 @@
375514
/* Remove the Tcl integration commands. */
376515
for(i=0; i<(sizeof(aCommand)/sizeof(aCommand[0])); i++){
377516
Th_RenameCommand(th1Interp, aCommand[i].zName, -1, NULL, 0);
378517
}
379518
}
519
+
520
+/*
521
+** When Tcl stubs support is enabled, attempts to dynamically load the Tcl
522
+** shared library and fetch the function pointers necessary to create an
523
+** interpreter and initialize the stubs mechanism; otherwise, simply setup
524
+** the function pointers provided by the caller with the statically linked
525
+** functions.
526
+ */
527
+static int loadTcl(
528
+ Th_Interp *interp,
529
+ void **pLibrary,
530
+ tcl_FindExecutableProc **pxFindExecutable,
531
+ tcl_CreateInterpProc **pxCreateInterp
532
+){
533
+#if defined(USE_TCL_STUBS)
534
+ char fileName[] = TCL_LIBRARY_NAME;
535
+#endif
536
+ if( !pLibrary || !pxFindExecutable || !pxCreateInterp ){
537
+ Th_ErrorMessage(interp,
538
+ "Invalid Tcl loader argument(s)", (const char *)"", 0);
539
+ return TH_ERROR;
540
+ }
541
+#if defined(USE_TCL_STUBS)
542
+ do {
543
+ void *library = dlopen(fileName, RTLD_NOW | RTLD_GLOBAL);
544
+ if( library ){
545
+ tcl_FindExecutableProc *xFindExecutable;
546
+ tcl_CreateInterpProc *xCreateInterp;
547
+ const char *procName = TCL_FINDEXECUTABLE_NAME;
548
+ xFindExecutable = (tcl_FindExecutableProc *)dlsym(library, procName + 1);
549
+ if( !xFindExecutable ){
550
+ xFindExecutable = (tcl_FindExecutableProc *)dlsym(library, procName);
551
+ }
552
+ if( !xFindExecutable ){
553
+ Th_ErrorMessage(interp,
554
+ "Could not locate Tcl_FindExecutable", (const char *)"", 0);
555
+ dlclose(library);
556
+ return TH_ERROR;
557
+ }
558
+ procName = TCL_CREATEINTERP_NAME;
559
+ xCreateInterp = (tcl_CreateInterpProc *)dlsym(library, procName + 1);
560
+ if( !xCreateInterp ){
561
+ xCreateInterp = (tcl_CreateInterpProc *)dlsym(library, procName);
562
+ }
563
+ if( !xCreateInterp ){
564
+ Th_ErrorMessage(interp,
565
+ "Could not locate Tcl_CreateInterp", (const char *)"", 0);
566
+ dlclose(library);
567
+ return TH_ERROR;
568
+ }
569
+ *pLibrary = library;
570
+ *pxFindExecutable = xFindExecutable;
571
+ *pxCreateInterp = xCreateInterp;
572
+ return TH_OK;
573
+ }
574
+ } while( --fileName[TCL_MINOR_OFFSET]>'3' ); /* Tcl 8.4+ */
575
+ Th_ErrorMessage(interp,
576
+ "Could not load Tcl shared library \"" TCL_LIBRARY_NAME "\"",
577
+ (const char *)"", 0);
578
+ return TH_ERROR;
579
+#else
580
+ *pLibrary = 0;
581
+ *pxFindExecutable = Tcl_FindExecutable;
582
+ *pxCreateInterp = Tcl_CreateInterp;
583
+ return TH_OK;
584
+#endif
585
+}
380586
381587
/*
382588
** Sets the "argv0", "argc", and "argv" script variables in the Tcl interpreter
383589
** based on the supplied command line arguments.
384590
*/
@@ -446,31 +652,41 @@
446652
struct TclContext *tclContext = (struct TclContext *)pContext;
447653
int argc;
448654
char **argv;
449655
char *argv0 = 0;
450656
Tcl_Interp *tclInterp;
657
+ char *setup;
451658
452659
if ( !tclContext ){
453660
Th_ErrorMessage(interp,
454661
"Invalid Tcl context", (const char *)"", 0);
455662
return TH_ERROR;
456663
}
457664
if ( tclContext->interp ){
458665
return TH_OK;
666
+ }
667
+ if( loadTcl(interp, &tclContext->library, &tclContext->xFindExecutable,
668
+ &tclContext->xCreateInterp)!=TH_OK ){
669
+ return TH_ERROR;
459670
}
460671
argc = tclContext->argc;
461672
argv = tclContext->argv;
462673
if( argc>0 && argv ){
463674
argv0 = argv[0];
464675
}
465
- Tcl_FindExecutable(argv0);
466
- tclInterp = tclContext->interp = Tcl_CreateInterp();
467
- if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){
676
+ tclContext->xFindExecutable(argv0);
677
+ tclInterp = tclContext->xCreateInterp();
678
+ if( !tclInterp ||
679
+#if defined(USE_TCL_STUBS)
680
+ !Tcl_InitStubs(tclInterp, "8.4", 0) ||
681
+#endif
682
+ Tcl_InterpDeleted(tclInterp) ){
468683
Th_ErrorMessage(interp,
469684
"Could not create Tcl interpreter", (const char *)"", 0);
470685
return TH_ERROR;
471686
}
687
+ tclContext->interp = tclInterp;
472688
if( Tcl_Init(tclInterp)!=TCL_OK ){
473689
Th_ErrorMessage(interp,
474690
"Tcl initialization error:", Tcl_GetStringResult(tclInterp), -1);
475691
Tcl_DeleteInterp(tclInterp);
476692
tclContext->interp = tclInterp = 0;
@@ -485,10 +701,19 @@
485701
}
486702
/* Add the TH1 integration commands to Tcl. */
487703
Tcl_CallWhenDeleted(tclInterp, Th1DeleteProc, interp);
488704
Tcl_CreateObjCommand(tclInterp, "th1Eval", Th1EvalObjCmd, interp, NULL);
489705
Tcl_CreateObjCommand(tclInterp, "th1Expr", Th1ExprObjCmd, interp, NULL);
706
+ /* If necessary, evaluate the custom Tcl setup script. */
707
+ setup = tclContext->setup;
708
+ if( setup && Tcl_Eval(tclInterp, setup)!=TCL_OK ){
709
+ Th_ErrorMessage(interp,
710
+ "Tcl setup script error:", Tcl_GetStringResult(tclInterp), -1);
711
+ Tcl_DeleteInterp(tclInterp);
712
+ tclContext->interp = tclInterp = 0;
713
+ return TH_ERROR;
714
+ }
490715
return TH_OK;
491716
}
492717
493718
/*
494719
** Register the Tcl language commands with interpreter interp.
495720
--- src/th_tcl.c
+++ src/th_tcl.c
@@ -32,11 +32,11 @@
32 ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 6))
33 /*
34 ** Workaround NRE-specific issue in Tcl_EvalObjCmd (SF bug #3399564) by using
35 ** Tcl_EvalObjv instead of invoking the objProc directly.
36 */
37 #define USE_TCL_EVALOBJV 1
38 #endif
39
40 /*
41 ** These macros are designed to reduce the redundant code required to marshal
42 ** arguments from TH1 to Tcl.
@@ -64,10 +64,94 @@
64 ** Fetch the Tcl interpreter from the specified void pointer, cast to a Tcl
65 ** context.
66 */
67 #define GET_CTX_TCL_INTERP(ctx) \
68 ((struct TclContext *)(ctx))->interp
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
70 /*
71 ** Creates and initializes a Tcl interpreter for use with the specified TH1
72 ** interpreter. Stores the created Tcl interpreter in the Tcl context supplied
73 ** by the caller. This must be declared here because quite a few functions in
@@ -100,14 +184,54 @@
100 /*
101 ** Tcl context information used by TH1. This structure definition has been
102 ** copied from and should be kept in sync with the one in "main.c".
103 */
104 struct TclContext {
105 int argc;
106 char **argv;
107 Tcl_Interp *interp;
 
 
 
 
 
 
 
 
108 };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
110 /*
111 ** Syntax:
112 **
113 ** tclEval arg ?arg ...?
@@ -119,24 +243,28 @@
119 const char **argv,
120 int *argl
121 ){
122 Tcl_Interp *tclInterp;
123 Tcl_Obj *objPtr;
124 int rc;
125 int nResult;
126 const char *zResult;
127
128 if ( createTclInterp(interp, ctx)!=TH_OK ){
129 return TH_ERROR;
130 }
131 if( argc<2 ){
132 return Th_WrongNumArgs(interp, "tclEval arg ?arg ...?");
133 }
134 tclInterp = GET_CTX_TCL_INTERP(ctx);
135 if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){
136 Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0);
137 return TH_ERROR;
 
 
 
 
138 }
139 Tcl_Preserve((ClientData)tclInterp);
140 if( argc==2 ){
141 objPtr = Tcl_NewStringObj(argv[1], argl[1]);
142 Tcl_IncrRefCount(objPtr);
@@ -152,10 +280,11 @@
152 FREE_ARGV_TO_OBJV();
153 }
154 zResult = getTclResult(tclInterp, &nResult);
155 Th_SetResult(interp, zResult, nResult);
156 Tcl_Release((ClientData)tclInterp);
 
157 return rc;
158 }
159
160 /*
161 ** Syntax:
@@ -170,24 +299,28 @@
170 int *argl
171 ){
172 Tcl_Interp *tclInterp;
173 Tcl_Obj *objPtr;
174 Tcl_Obj *resultObjPtr;
175 int rc;
176 int nResult;
177 const char *zResult;
178
179 if ( createTclInterp(interp, ctx)!=TH_OK ){
180 return TH_ERROR;
181 }
182 if( argc<2 ){
183 return Th_WrongNumArgs(interp, "tclExpr arg ?arg ...?");
184 }
185 tclInterp = GET_CTX_TCL_INTERP(ctx);
186 if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){
187 Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0);
188 return TH_ERROR;
 
 
 
 
189 }
190 Tcl_Preserve((ClientData)tclInterp);
191 if( argc==2 ){
192 objPtr = Tcl_NewStringObj(argv[1], argl[1]);
193 Tcl_IncrRefCount(objPtr);
@@ -208,10 +341,11 @@
208 zResult = getTclResult(tclInterp, &nResult);
209 }
210 Th_SetResult(interp, zResult, nResult);
211 if( rc==TCL_OK ) Tcl_DecrRefCount(resultObjPtr);
212 Tcl_Release((ClientData)tclInterp);
 
213 return rc;
214 }
215
216 /*
217 ** Syntax:
@@ -224,35 +358,39 @@
224 int argc,
225 const char **argv,
226 int *argl
227 ){
228 Tcl_Interp *tclInterp;
229 #ifndef USE_TCL_EVALOBJV
230 Tcl_Command command;
231 Tcl_CmdInfo cmdInfo;
232 #endif
233 int rc;
234 int nResult;
235 const char *zResult;
236 #ifndef USE_TCL_EVALOBJV
237 Tcl_Obj *objPtr;
238 #endif
239 USE_ARGV_TO_OBJV();
240
241 if ( createTclInterp(interp, ctx)!=TH_OK ){
242 return TH_ERROR;
243 }
244 if( argc<2 ){
245 return Th_WrongNumArgs(interp, "tclInvoke command ?arg ...?");
246 }
247 tclInterp = GET_CTX_TCL_INTERP(ctx);
248 if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){
249 Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0);
250 return TH_ERROR;
 
 
 
 
251 }
252 Tcl_Preserve((ClientData)tclInterp);
253 #ifndef USE_TCL_EVALOBJV
254 objPtr = Tcl_NewStringObj(argv[1], argl[1]);
255 Tcl_IncrRefCount(objPtr);
256 command = Tcl_GetCommandFromObj(tclInterp, objPtr);
257 if( !command || Tcl_GetCommandInfoFromToken(command,&cmdInfo)==0 ){
258 Th_ErrorMessage(interp, "Tcl command not found:", argv[1], argl[1]);
@@ -267,20 +405,21 @@
267 return TH_ERROR;
268 }
269 Tcl_DecrRefCount(objPtr);
270 #endif
271 COPY_ARGV_TO_OBJV();
272 #ifdef USE_TCL_EVALOBJV
273 rc = Tcl_EvalObjv(tclInterp, objc, objv, 0);
274 #else
275 Tcl_ResetResult(tclInterp);
276 rc = cmdInfo.objProc(cmdInfo.objClientData, tclInterp, objc, objv);
277 #endif
278 FREE_ARGV_TO_OBJV();
279 zResult = getTclResult(tclInterp, &nResult);
280 Th_SetResult(interp, zResult, nResult);
281 Tcl_Release((ClientData)tclInterp);
 
282 return rc;
283 }
284
285 /*
286 ** Syntax:
@@ -375,10 +514,77 @@
375 /* Remove the Tcl integration commands. */
376 for(i=0; i<(sizeof(aCommand)/sizeof(aCommand[0])); i++){
377 Th_RenameCommand(th1Interp, aCommand[i].zName, -1, NULL, 0);
378 }
379 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
380
381 /*
382 ** Sets the "argv0", "argc", and "argv" script variables in the Tcl interpreter
383 ** based on the supplied command line arguments.
384 */
@@ -446,31 +652,41 @@
446 struct TclContext *tclContext = (struct TclContext *)pContext;
447 int argc;
448 char **argv;
449 char *argv0 = 0;
450 Tcl_Interp *tclInterp;
 
451
452 if ( !tclContext ){
453 Th_ErrorMessage(interp,
454 "Invalid Tcl context", (const char *)"", 0);
455 return TH_ERROR;
456 }
457 if ( tclContext->interp ){
458 return TH_OK;
 
 
 
 
459 }
460 argc = tclContext->argc;
461 argv = tclContext->argv;
462 if( argc>0 && argv ){
463 argv0 = argv[0];
464 }
465 Tcl_FindExecutable(argv0);
466 tclInterp = tclContext->interp = Tcl_CreateInterp();
467 if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){
 
 
 
 
468 Th_ErrorMessage(interp,
469 "Could not create Tcl interpreter", (const char *)"", 0);
470 return TH_ERROR;
471 }
 
472 if( Tcl_Init(tclInterp)!=TCL_OK ){
473 Th_ErrorMessage(interp,
474 "Tcl initialization error:", Tcl_GetStringResult(tclInterp), -1);
475 Tcl_DeleteInterp(tclInterp);
476 tclContext->interp = tclInterp = 0;
@@ -485,10 +701,19 @@
485 }
486 /* Add the TH1 integration commands to Tcl. */
487 Tcl_CallWhenDeleted(tclInterp, Th1DeleteProc, interp);
488 Tcl_CreateObjCommand(tclInterp, "th1Eval", Th1EvalObjCmd, interp, NULL);
489 Tcl_CreateObjCommand(tclInterp, "th1Expr", Th1ExprObjCmd, interp, NULL);
 
 
 
 
 
 
 
 
 
490 return TH_OK;
491 }
492
493 /*
494 ** Register the Tcl language commands with interpreter interp.
495
--- src/th_tcl.c
+++ src/th_tcl.c
@@ -32,11 +32,11 @@
32 ((TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 6))
33 /*
34 ** Workaround NRE-specific issue in Tcl_EvalObjCmd (SF bug #3399564) by using
35 ** Tcl_EvalObjv instead of invoking the objProc directly.
36 */
37 # define USE_TCL_EVALOBJV 1
38 #endif
39
40 /*
41 ** These macros are designed to reduce the redundant code required to marshal
42 ** arguments from TH1 to Tcl.
@@ -64,10 +64,94 @@
64 ** Fetch the Tcl interpreter from the specified void pointer, cast to a Tcl
65 ** context.
66 */
67 #define GET_CTX_TCL_INTERP(ctx) \
68 ((struct TclContext *)(ctx))->interp
69
70 /*
71 ** Define the Tcl shared library name, some exported function names, and some
72 ** cross-platform macros for use with the Tcl stubs mechanism, when enabled.
73 */
74 #if defined(USE_TCL_STUBS)
75 # if defined(_WIN32)
76 # define WIN32_LEAN_AND_MEAN
77 # include <windows.h>
78 # ifndef TCL_LIBRARY_NAME
79 # define TCL_LIBRARY_NAME "tcl86.dll\0"
80 # endif
81 # ifndef TCL_MINOR_OFFSET
82 # define TCL_MINOR_OFFSET (4)
83 # endif
84 # ifndef dlopen
85 # define dlopen(a,b) (void *)LoadLibrary((a))
86 # endif
87 # ifndef dlsym
88 # define dlsym(a,b) GetProcAddress((HANDLE)(a),(b))
89 # endif
90 # ifndef dlclose
91 # define dlclose(a) FreeLibrary((HANDLE)(a))
92 # endif
93 # else
94 # include <dlfcn.h>
95 # if defined(__CYGWIN__)
96 # ifndef TCL_LIBRARY_NAME
97 # define TCL_LIBRARY_NAME "libtcl8.6.dll\0"
98 # endif
99 # ifndef TCL_MINOR_OFFSET
100 # define TCL_MINOR_OFFSET (8)
101 # endif
102 # elif defined(__APPLE__)
103 # ifndef TCL_LIBRARY_NAME
104 # define TCL_LIBRARY_NAME "libtcl8.6.dylib\0"
105 # endif
106 # ifndef TCL_MINOR_OFFSET
107 # define TCL_MINOR_OFFSET (8)
108 # endif
109 # else
110 # ifndef TCL_LIBRARY_NAME
111 # define TCL_LIBRARY_NAME "libtcl8.6.so\0"
112 # endif
113 # ifndef TCL_MINOR_OFFSET
114 # define TCL_MINOR_OFFSET (8)
115 # endif
116 # endif /* defined(__CYGWIN__) */
117 # endif /* defined(_WIN32) */
118 # ifndef TCL_FINDEXECUTABLE_NAME
119 # define TCL_FINDEXECUTABLE_NAME "_Tcl_FindExecutable"
120 # endif
121 # ifndef TCL_CREATEINTERP_NAME
122 # define TCL_CREATEINTERP_NAME "_Tcl_CreateInterp"
123 # endif
124 #endif /* defined(USE_TCL_STUBS) */
125
126 /*
127 ** The function types for Tcl_FindExecutable and Tcl_CreateInterp are needed
128 ** when the Tcl library is being loaded dynamically by a stubs-enabled
129 ** application (i.e. the inverse of using a stubs-enabled package). These are
130 ** the only Tcl API functions that MUST be called prior to being able to call
131 ** Tcl_InitStubs (i.e. because it requires a Tcl interpreter).
132 */
133 typedef void (tcl_FindExecutableProc) (CONST char * argv0);
134 typedef Tcl_Interp *(tcl_CreateInterpProc) (void);
135
136 /*
137 ** The function types for the "hook" functions to be called before and after a
138 ** TH1 command makes a call to evaluate a Tcl script. If the "pre" function
139 ** returns anything but TH_OK, then evaluation of the Tcl script is skipped and
140 ** that value is used as the return code. If the "post" function returns
141 ** anything other than its rc argument, that will become the new return code
142 ** for the command.
143 */
144 typedef int (tcl_NotifyProc) (
145 void *pContext, /* The context for this notification. */
146 Th_Interp *interp, /* The TH1 interpreter being used. */
147 void *ctx, /* The original TH1 command context. */
148 int argc, /* Number of arguments for the TH1 command. */
149 const char **argv, /* Array of arguments for the TH1 command. */
150 int *argl, /* Array of lengths for the TH1 command arguments. */
151 int rc /* Recommended notification return value. */
152 );
153
154 /*
155 ** Creates and initializes a Tcl interpreter for use with the specified TH1
156 ** interpreter. Stores the created Tcl interpreter in the Tcl context supplied
157 ** by the caller. This must be declared here because quite a few functions in
@@ -100,14 +184,54 @@
184 /*
185 ** Tcl context information used by TH1. This structure definition has been
186 ** copied from and should be kept in sync with the one in "main.c".
187 */
188 struct TclContext {
189 int argc; /* Number of original arguments. */
190 char **argv; /* Full copy of the original arguments. */
191 void *library; /* The Tcl library module handle. */
192 tcl_FindExecutableProc *xFindExecutable; /* Tcl_FindExecutable() pointer. */
193 tcl_CreateInterpProc *xCreateInterp; /* Tcl_CreateInterp() pointer. */
194 Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */
195 char *setup; /* The optional Tcl setup script. */
196 tcl_NotifyProc *xPreEval; /* Optional, called before Tcl_Eval*(). */
197 void *pPreContext; /* Optional, provided to xPreEval(). */
198 tcl_NotifyProc *xPostEval; /* Optional, called after Tcl_Eval*(). */
199 void *pPostContext; /* Optional, provided to xPostEval(). */
200 };
201
202 /*
203 ** This function calls the configured xPreEval or xPostEval functions, if any.
204 ** May have arbitrary side-effects. This function returns the result of the
205 ** called notification function or the value of the rc argument if there is no
206 ** notification function configured.
207 */
208 static int notifyPreOrPostEval(
209 int bIsPost,
210 Th_Interp *interp,
211 void *ctx,
212 int argc,
213 const char **argv,
214 int *argl,
215 int rc
216 ){
217 struct TclContext *tclContext = (struct TclContext *)ctx;
218 tcl_NotifyProc *xNotifyProc;
219
220 if ( !tclContext ){
221 Th_ErrorMessage(interp,
222 "Invalid Tcl context", (const char *)"", 0);
223 return TH_ERROR;
224 }
225 xNotifyProc = bIsPost ? tclContext->xPostEval : tclContext->xPreEval;
226 if ( xNotifyProc ){
227 rc = xNotifyProc(bIsPost ?
228 tclContext->pPostContext : tclContext->pPreContext,
229 interp, ctx, argc, argv, argl, rc);
230 }
231 return rc;
232 }
233
234 /*
235 ** Syntax:
236 **
237 ** tclEval arg ?arg ...?
@@ -119,24 +243,28 @@
243 const char **argv,
244 int *argl
245 ){
246 Tcl_Interp *tclInterp;
247 Tcl_Obj *objPtr;
248 int rc = TH_OK;
249 int nResult;
250 const char *zResult;
251
252 if( createTclInterp(interp, ctx)!=TH_OK ){
253 return TH_ERROR;
254 }
255 if( argc<2 ){
256 return Th_WrongNumArgs(interp, "tclEval arg ?arg ...?");
257 }
258 tclInterp = GET_CTX_TCL_INTERP(ctx);
259 if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){
260 Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0);
261 return TH_ERROR;
262 }
263 rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, argl, rc);
264 if( rc!=TH_OK ){
265 return rc;
266 }
267 Tcl_Preserve((ClientData)tclInterp);
268 if( argc==2 ){
269 objPtr = Tcl_NewStringObj(argv[1], argl[1]);
270 Tcl_IncrRefCount(objPtr);
@@ -152,10 +280,11 @@
280 FREE_ARGV_TO_OBJV();
281 }
282 zResult = getTclResult(tclInterp, &nResult);
283 Th_SetResult(interp, zResult, nResult);
284 Tcl_Release((ClientData)tclInterp);
285 rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, rc);
286 return rc;
287 }
288
289 /*
290 ** Syntax:
@@ -170,24 +299,28 @@
299 int *argl
300 ){
301 Tcl_Interp *tclInterp;
302 Tcl_Obj *objPtr;
303 Tcl_Obj *resultObjPtr;
304 int rc = TH_OK;
305 int nResult;
306 const char *zResult;
307
308 if( createTclInterp(interp, ctx)!=TH_OK ){
309 return TH_ERROR;
310 }
311 if( argc<2 ){
312 return Th_WrongNumArgs(interp, "tclExpr arg ?arg ...?");
313 }
314 tclInterp = GET_CTX_TCL_INTERP(ctx);
315 if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){
316 Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0);
317 return TH_ERROR;
318 }
319 rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, argl, rc);
320 if( rc!=TH_OK ){
321 return rc;
322 }
323 Tcl_Preserve((ClientData)tclInterp);
324 if( argc==2 ){
325 objPtr = Tcl_NewStringObj(argv[1], argl[1]);
326 Tcl_IncrRefCount(objPtr);
@@ -208,10 +341,11 @@
341 zResult = getTclResult(tclInterp, &nResult);
342 }
343 Th_SetResult(interp, zResult, nResult);
344 if( rc==TCL_OK ) Tcl_DecrRefCount(resultObjPtr);
345 Tcl_Release((ClientData)tclInterp);
346 rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, rc);
347 return rc;
348 }
349
350 /*
351 ** Syntax:
@@ -224,35 +358,39 @@
358 int argc,
359 const char **argv,
360 int *argl
361 ){
362 Tcl_Interp *tclInterp;
363 #if !defined(USE_TCL_EVALOBJV)
364 Tcl_Command command;
365 Tcl_CmdInfo cmdInfo;
366 #endif
367 int rc = TH_OK;
368 int nResult;
369 const char *zResult;
370 #if !defined(USE_TCL_EVALOBJV)
371 Tcl_Obj *objPtr;
372 #endif
373 USE_ARGV_TO_OBJV();
374
375 if( createTclInterp(interp, ctx)!=TH_OK ){
376 return TH_ERROR;
377 }
378 if( argc<2 ){
379 return Th_WrongNumArgs(interp, "tclInvoke command ?arg ...?");
380 }
381 tclInterp = GET_CTX_TCL_INTERP(ctx);
382 if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){
383 Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0);
384 return TH_ERROR;
385 }
386 rc = notifyPreOrPostEval(0, interp, ctx, argc, argv, argl, rc);
387 if( rc!=TH_OK ){
388 return rc;
389 }
390 Tcl_Preserve((ClientData)tclInterp);
391 #if !defined(USE_TCL_EVALOBJV)
392 objPtr = Tcl_NewStringObj(argv[1], argl[1]);
393 Tcl_IncrRefCount(objPtr);
394 command = Tcl_GetCommandFromObj(tclInterp, objPtr);
395 if( !command || Tcl_GetCommandInfoFromToken(command,&cmdInfo)==0 ){
396 Th_ErrorMessage(interp, "Tcl command not found:", argv[1], argl[1]);
@@ -267,20 +405,21 @@
405 return TH_ERROR;
406 }
407 Tcl_DecrRefCount(objPtr);
408 #endif
409 COPY_ARGV_TO_OBJV();
410 #if defined(USE_TCL_EVALOBJV)
411 rc = Tcl_EvalObjv(tclInterp, objc, objv, 0);
412 #else
413 Tcl_ResetResult(tclInterp);
414 rc = cmdInfo.objProc(cmdInfo.objClientData, tclInterp, objc, objv);
415 #endif
416 FREE_ARGV_TO_OBJV();
417 zResult = getTclResult(tclInterp, &nResult);
418 Th_SetResult(interp, zResult, nResult);
419 Tcl_Release((ClientData)tclInterp);
420 rc = notifyPreOrPostEval(1, interp, ctx, argc, argv, argl, rc);
421 return rc;
422 }
423
424 /*
425 ** Syntax:
@@ -375,10 +514,77 @@
514 /* Remove the Tcl integration commands. */
515 for(i=0; i<(sizeof(aCommand)/sizeof(aCommand[0])); i++){
516 Th_RenameCommand(th1Interp, aCommand[i].zName, -1, NULL, 0);
517 }
518 }
519
520 /*
521 ** When Tcl stubs support is enabled, attempts to dynamically load the Tcl
522 ** shared library and fetch the function pointers necessary to create an
523 ** interpreter and initialize the stubs mechanism; otherwise, simply setup
524 ** the function pointers provided by the caller with the statically linked
525 ** functions.
526 */
527 static int loadTcl(
528 Th_Interp *interp,
529 void **pLibrary,
530 tcl_FindExecutableProc **pxFindExecutable,
531 tcl_CreateInterpProc **pxCreateInterp
532 ){
533 #if defined(USE_TCL_STUBS)
534 char fileName[] = TCL_LIBRARY_NAME;
535 #endif
536 if( !pLibrary || !pxFindExecutable || !pxCreateInterp ){
537 Th_ErrorMessage(interp,
538 "Invalid Tcl loader argument(s)", (const char *)"", 0);
539 return TH_ERROR;
540 }
541 #if defined(USE_TCL_STUBS)
542 do {
543 void *library = dlopen(fileName, RTLD_NOW | RTLD_GLOBAL);
544 if( library ){
545 tcl_FindExecutableProc *xFindExecutable;
546 tcl_CreateInterpProc *xCreateInterp;
547 const char *procName = TCL_FINDEXECUTABLE_NAME;
548 xFindExecutable = (tcl_FindExecutableProc *)dlsym(library, procName + 1);
549 if( !xFindExecutable ){
550 xFindExecutable = (tcl_FindExecutableProc *)dlsym(library, procName);
551 }
552 if( !xFindExecutable ){
553 Th_ErrorMessage(interp,
554 "Could not locate Tcl_FindExecutable", (const char *)"", 0);
555 dlclose(library);
556 return TH_ERROR;
557 }
558 procName = TCL_CREATEINTERP_NAME;
559 xCreateInterp = (tcl_CreateInterpProc *)dlsym(library, procName + 1);
560 if( !xCreateInterp ){
561 xCreateInterp = (tcl_CreateInterpProc *)dlsym(library, procName);
562 }
563 if( !xCreateInterp ){
564 Th_ErrorMessage(interp,
565 "Could not locate Tcl_CreateInterp", (const char *)"", 0);
566 dlclose(library);
567 return TH_ERROR;
568 }
569 *pLibrary = library;
570 *pxFindExecutable = xFindExecutable;
571 *pxCreateInterp = xCreateInterp;
572 return TH_OK;
573 }
574 } while( --fileName[TCL_MINOR_OFFSET]>'3' ); /* Tcl 8.4+ */
575 Th_ErrorMessage(interp,
576 "Could not load Tcl shared library \"" TCL_LIBRARY_NAME "\"",
577 (const char *)"", 0);
578 return TH_ERROR;
579 #else
580 *pLibrary = 0;
581 *pxFindExecutable = Tcl_FindExecutable;
582 *pxCreateInterp = Tcl_CreateInterp;
583 return TH_OK;
584 #endif
585 }
586
587 /*
588 ** Sets the "argv0", "argc", and "argv" script variables in the Tcl interpreter
589 ** based on the supplied command line arguments.
590 */
@@ -446,31 +652,41 @@
652 struct TclContext *tclContext = (struct TclContext *)pContext;
653 int argc;
654 char **argv;
655 char *argv0 = 0;
656 Tcl_Interp *tclInterp;
657 char *setup;
658
659 if ( !tclContext ){
660 Th_ErrorMessage(interp,
661 "Invalid Tcl context", (const char *)"", 0);
662 return TH_ERROR;
663 }
664 if ( tclContext->interp ){
665 return TH_OK;
666 }
667 if( loadTcl(interp, &tclContext->library, &tclContext->xFindExecutable,
668 &tclContext->xCreateInterp)!=TH_OK ){
669 return TH_ERROR;
670 }
671 argc = tclContext->argc;
672 argv = tclContext->argv;
673 if( argc>0 && argv ){
674 argv0 = argv[0];
675 }
676 tclContext->xFindExecutable(argv0);
677 tclInterp = tclContext->xCreateInterp();
678 if( !tclInterp ||
679 #if defined(USE_TCL_STUBS)
680 !Tcl_InitStubs(tclInterp, "8.4", 0) ||
681 #endif
682 Tcl_InterpDeleted(tclInterp) ){
683 Th_ErrorMessage(interp,
684 "Could not create Tcl interpreter", (const char *)"", 0);
685 return TH_ERROR;
686 }
687 tclContext->interp = tclInterp;
688 if( Tcl_Init(tclInterp)!=TCL_OK ){
689 Th_ErrorMessage(interp,
690 "Tcl initialization error:", Tcl_GetStringResult(tclInterp), -1);
691 Tcl_DeleteInterp(tclInterp);
692 tclContext->interp = tclInterp = 0;
@@ -485,10 +701,19 @@
701 }
702 /* Add the TH1 integration commands to Tcl. */
703 Tcl_CallWhenDeleted(tclInterp, Th1DeleteProc, interp);
704 Tcl_CreateObjCommand(tclInterp, "th1Eval", Th1EvalObjCmd, interp, NULL);
705 Tcl_CreateObjCommand(tclInterp, "th1Expr", Th1ExprObjCmd, interp, NULL);
706 /* If necessary, evaluate the custom Tcl setup script. */
707 setup = tclContext->setup;
708 if( setup && Tcl_Eval(tclInterp, setup)!=TCL_OK ){
709 Th_ErrorMessage(interp,
710 "Tcl setup script error:", Tcl_GetStringResult(tclInterp), -1);
711 Tcl_DeleteInterp(tclInterp);
712 tclContext->interp = tclInterp = 0;
713 return TH_ERROR;
714 }
715 return TH_OK;
716 }
717
718 /*
719 ** Register the Tcl language commands with interpreter interp.
720
+1 -1
--- src/timeline.c
+++ src/timeline.c
@@ -917,11 +917,11 @@
917917
const char *zThisTag = 0; /* Suppress links to this tag */
918918
const char *zThisUser = 0; /* Suppress links to this user */
919919
HQuery url; /* URL for various branch links */
920920
int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */
921921
int to_rid = name_to_typed_rid(P("to"),"ci"); /* to= for path timelines */
922
- int noMerge = P("nomerge")!=0; /* Do not follow merge links */
922
+ int noMerge = P("shortest")==0; /* Follow merge links if shorter */
923923
int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */
924924
int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
925925
int pd_rid;
926926
double rBefore, rAfter, rCirca; /* Boundary times */
927927
928928
--- src/timeline.c
+++ src/timeline.c
@@ -917,11 +917,11 @@
917 const char *zThisTag = 0; /* Suppress links to this tag */
918 const char *zThisUser = 0; /* Suppress links to this user */
919 HQuery url; /* URL for various branch links */
920 int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */
921 int to_rid = name_to_typed_rid(P("to"),"ci"); /* to= for path timelines */
922 int noMerge = P("nomerge")!=0; /* Do not follow merge links */
923 int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */
924 int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
925 int pd_rid;
926 double rBefore, rAfter, rCirca; /* Boundary times */
927
928
--- src/timeline.c
+++ src/timeline.c
@@ -917,11 +917,11 @@
917 const char *zThisTag = 0; /* Suppress links to this tag */
918 const char *zThisUser = 0; /* Suppress links to this user */
919 HQuery url; /* URL for various branch links */
920 int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */
921 int to_rid = name_to_typed_rid(P("to"),"ci"); /* to= for path timelines */
922 int noMerge = P("shortest")==0; /* Follow merge links if shorter */
923 int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */
924 int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
925 int pd_rid;
926 double rBefore, rAfter, rCirca; /* Boundary times */
927
928
+9 -3
--- src/update.c
+++ src/update.c
@@ -595,12 +595,13 @@
595595
*/
596596
int historical_version_of_file(
597597
const char *revision, /* The checkin containing the file */
598598
const char *file, /* Full treename of the file */
599599
Blob *content, /* Put the content here */
600
- int *pIsLink, /* Set to true if file is link. */
600
+ int *pIsLink, /* Set to true if file is link. */
601601
int *pIsExe, /* Set to true if file is executable */
602
+ int *pIsBin, /* Set to true if file is binary */
602603
int errCode /* Error code if file not found. Panic if 0. */
603604
){
604605
Manifest *pManifest;
605606
ManifestFile *pFile;
606607
int rid=0;
@@ -617,15 +618,20 @@
617618
pManifest = manifest_get(rid, CFTYPE_MANIFEST);
618619
619620
if( pManifest ){
620621
pFile = manifest_file_find(pManifest, file);
621622
if( pFile ){
623
+ int rc;
622624
rid = uuid_to_rid(pFile->zUuid, 0);
623625
if( pIsExe ) *pIsExe = ( manifest_file_mperm(pFile)==PERM_EXE );
624626
if( pIsLink ) *pIsLink = ( manifest_file_mperm(pFile)==PERM_LNK );
625627
manifest_destroy(pManifest);
626
- return content_get(rid, content);
628
+ rc = content_get(rid, content);
629
+ if( rc && pIsBin ){
630
+ *pIsBin = looks_like_binary(content);
631
+ }
632
+ return rc;
627633
}
628634
manifest_destroy(pManifest);
629635
if( errCode<=0 ){
630636
fossil_fatal("file %s does not exist in checkin: %s", file, revision);
631637
}
@@ -712,11 +718,11 @@
712718
int isLink = 0;
713719
char *zFull;
714720
zFile = db_column_text(&q, 0);
715721
zFull = mprintf("%/%/", g.zLocalRoot, zFile);
716722
errCode = historical_version_of_file(zRevision, zFile, &record,
717
- &isLink, &isExe,2);
723
+ &isLink, &isExe, 0, 2);
718724
if( errCode==2 ){
719725
if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFile)==0 ){
720726
fossil_print("UNMANAGE: %s\n", zFile);
721727
}else{
722728
undo_save(zFile);
723729
--- src/update.c
+++ src/update.c
@@ -595,12 +595,13 @@
595 */
596 int historical_version_of_file(
597 const char *revision, /* The checkin containing the file */
598 const char *file, /* Full treename of the file */
599 Blob *content, /* Put the content here */
600 int *pIsLink, /* Set to true if file is link. */
601 int *pIsExe, /* Set to true if file is executable */
 
602 int errCode /* Error code if file not found. Panic if 0. */
603 ){
604 Manifest *pManifest;
605 ManifestFile *pFile;
606 int rid=0;
@@ -617,15 +618,20 @@
617 pManifest = manifest_get(rid, CFTYPE_MANIFEST);
618
619 if( pManifest ){
620 pFile = manifest_file_find(pManifest, file);
621 if( pFile ){
 
622 rid = uuid_to_rid(pFile->zUuid, 0);
623 if( pIsExe ) *pIsExe = ( manifest_file_mperm(pFile)==PERM_EXE );
624 if( pIsLink ) *pIsLink = ( manifest_file_mperm(pFile)==PERM_LNK );
625 manifest_destroy(pManifest);
626 return content_get(rid, content);
 
 
 
 
627 }
628 manifest_destroy(pManifest);
629 if( errCode<=0 ){
630 fossil_fatal("file %s does not exist in checkin: %s", file, revision);
631 }
@@ -712,11 +718,11 @@
712 int isLink = 0;
713 char *zFull;
714 zFile = db_column_text(&q, 0);
715 zFull = mprintf("%/%/", g.zLocalRoot, zFile);
716 errCode = historical_version_of_file(zRevision, zFile, &record,
717 &isLink, &isExe,2);
718 if( errCode==2 ){
719 if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFile)==0 ){
720 fossil_print("UNMANAGE: %s\n", zFile);
721 }else{
722 undo_save(zFile);
723
--- src/update.c
+++ src/update.c
@@ -595,12 +595,13 @@
595 */
596 int historical_version_of_file(
597 const char *revision, /* The checkin containing the file */
598 const char *file, /* Full treename of the file */
599 Blob *content, /* Put the content here */
600 int *pIsLink, /* Set to true if file is link. */
601 int *pIsExe, /* Set to true if file is executable */
602 int *pIsBin, /* Set to true if file is binary */
603 int errCode /* Error code if file not found. Panic if 0. */
604 ){
605 Manifest *pManifest;
606 ManifestFile *pFile;
607 int rid=0;
@@ -617,15 +618,20 @@
618 pManifest = manifest_get(rid, CFTYPE_MANIFEST);
619
620 if( pManifest ){
621 pFile = manifest_file_find(pManifest, file);
622 if( pFile ){
623 int rc;
624 rid = uuid_to_rid(pFile->zUuid, 0);
625 if( pIsExe ) *pIsExe = ( manifest_file_mperm(pFile)==PERM_EXE );
626 if( pIsLink ) *pIsLink = ( manifest_file_mperm(pFile)==PERM_LNK );
627 manifest_destroy(pManifest);
628 rc = content_get(rid, content);
629 if( rc && pIsBin ){
630 *pIsBin = looks_like_binary(content);
631 }
632 return rc;
633 }
634 manifest_destroy(pManifest);
635 if( errCode<=0 ){
636 fossil_fatal("file %s does not exist in checkin: %s", file, revision);
637 }
@@ -712,11 +718,11 @@
718 int isLink = 0;
719 char *zFull;
720 zFile = db_column_text(&q, 0);
721 zFull = mprintf("%/%/", g.zLocalRoot, zFile);
722 errCode = historical_version_of_file(zRevision, zFile, &record,
723 &isLink, &isExe, 0, 2);
724 if( errCode==2 ){
725 if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFile)==0 ){
726 fossil_print("UNMANAGE: %s\n", zFile);
727 }else{
728 undo_save(zFile);
729
--- test/th1-tcl2.txt
+++ test/th1-tcl2.txt
@@ -9,11 +9,11 @@
99
#
1010
tclInvoke set repository_name [repository 1]
1111
proc doOut {msg} {puts $msg; puts \n}
1212
doOut [tclEval {
1313
package require sqlite3
14
- sqlite3 db $repository_name
14
+ sqlite3 db $repository_name -readonly true
1515
set x [db eval {SELECT COUNT(*) FROM user;}]
1616
db close
1717
return $x
1818
}]
1919
</th1>
2020
--- test/th1-tcl2.txt
+++ test/th1-tcl2.txt
@@ -9,11 +9,11 @@
9 #
10 tclInvoke set repository_name [repository 1]
11 proc doOut {msg} {puts $msg; puts \n}
12 doOut [tclEval {
13 package require sqlite3
14 sqlite3 db $repository_name
15 set x [db eval {SELECT COUNT(*) FROM user;}]
16 db close
17 return $x
18 }]
19 </th1>
20
--- test/th1-tcl2.txt
+++ test/th1-tcl2.txt
@@ -9,11 +9,11 @@
9 #
10 tclInvoke set repository_name [repository 1]
11 proc doOut {msg} {puts $msg; puts \n}
12 doOut [tclEval {
13 package require sqlite3
14 sqlite3 db $repository_name -readonly true
15 set x [db eval {SELECT COUNT(*) FROM user;}]
16 db close
17 return $x
18 }]
19 </th1>
20
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -47,10 +47,14 @@
4747
# FOSSIL_ENABLE_SSL = 1
4848
4949
#### Enable scripting support via Tcl/Tk
5050
#
5151
# FOSSIL_ENABLE_TCL = 1
52
+
53
+#### Load Tcl using the stubs mechanism
54
+#
55
+# FOSSIL_ENABLE_TCL_STUBS = 1
5256
5357
#### Use the Tcl source directory instead of the install directory?
5458
# This is useful when Tcl has been compiled statically with MinGW.
5559
#
5660
FOSSIL_TCL_SOURCE = 1
@@ -96,11 +100,15 @@
96100
TCLINCDIR = $(TCLDIR)/include
97101
TCLLIBDIR = $(TCLDIR)/lib
98102
99103
#### Tcl: Which Tcl library do we want to use (8.4, 8.5, 8.6, etc)?
100104
#
105
+ifdef FOSSIL_ENABLE_TCL_STUBS
106
+LIBTCL = -ltclstub86
107
+else
101108
LIBTCL = -ltcl86
109
+endif
102110
103111
#### C Compile and options for use in building executables that
104112
# will run on the target platform. This is usually the same
105113
# as BCC, unless you are cross-compiling. This C compiler builds
106114
# the finished binary for fossil. The BCC compiler above is used
@@ -134,14 +142,22 @@
134142
ifdef FOSSIL_ENABLE_SSL
135143
TCC += -DFOSSIL_ENABLE_SSL=1
136144
RCC += -DFOSSIL_ENABLE_SSL=1
137145
endif
138146
139
-# With Tcl support (statically linked)
147
+# With Tcl support
140148
ifdef FOSSIL_ENABLE_TCL
141
-TCC += -DFOSSIL_ENABLE_TCL=1 -DSTATIC_BUILD
149
+TCC += -DFOSSIL_ENABLE_TCL=1
142150
RCC += -DFOSSIL_ENABLE_TCL=1
151
+# Either statically linked or via stubs
152
+ifdef FOSSIL_ENABLE_TCL_STUBS
153
+TCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS
154
+RCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS
155
+else
156
+TCC += -DSTATIC_BUILD
157
+RCC += -DSTATIC_BUILD
158
+endif
143159
endif
144160
145161
# With JSON support
146162
ifdef FOSSIL_ENABLE_JSON
147163
TCC += -DFOSSIL_ENABLE_JSON=1
@@ -171,11 +187,15 @@
171187
172188
#### These libraries MUST appear in the same order as they do for Tcl
173189
# or linking with it will not work (exact reason unknown).
174190
#
175191
ifdef FOSSIL_ENABLE_TCL
192
+ifdef FOSSIL_ENABLE_TCL_STUBS
193
+LIB += -lkernel32 -lws2_32
194
+else
176195
LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32
196
+endif
177197
else
178198
LIB += -lkernel32 -lws2_32
179199
endif
180200
181201
#### Tcl shell for use in running the fossil test suite. This is only
182202
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -47,10 +47,14 @@
47 # FOSSIL_ENABLE_SSL = 1
48
49 #### Enable scripting support via Tcl/Tk
50 #
51 # FOSSIL_ENABLE_TCL = 1
 
 
 
 
52
53 #### Use the Tcl source directory instead of the install directory?
54 # This is useful when Tcl has been compiled statically with MinGW.
55 #
56 FOSSIL_TCL_SOURCE = 1
@@ -96,11 +100,15 @@
96 TCLINCDIR = $(TCLDIR)/include
97 TCLLIBDIR = $(TCLDIR)/lib
98
99 #### Tcl: Which Tcl library do we want to use (8.4, 8.5, 8.6, etc)?
100 #
 
 
 
101 LIBTCL = -ltcl86
 
102
103 #### C Compile and options for use in building executables that
104 # will run on the target platform. This is usually the same
105 # as BCC, unless you are cross-compiling. This C compiler builds
106 # the finished binary for fossil. The BCC compiler above is used
@@ -134,14 +142,22 @@
134 ifdef FOSSIL_ENABLE_SSL
135 TCC += -DFOSSIL_ENABLE_SSL=1
136 RCC += -DFOSSIL_ENABLE_SSL=1
137 endif
138
139 # With Tcl support (statically linked)
140 ifdef FOSSIL_ENABLE_TCL
141 TCC += -DFOSSIL_ENABLE_TCL=1 -DSTATIC_BUILD
142 RCC += -DFOSSIL_ENABLE_TCL=1
 
 
 
 
 
 
 
 
143 endif
144
145 # With JSON support
146 ifdef FOSSIL_ENABLE_JSON
147 TCC += -DFOSSIL_ENABLE_JSON=1
@@ -171,11 +187,15 @@
171
172 #### These libraries MUST appear in the same order as they do for Tcl
173 # or linking with it will not work (exact reason unknown).
174 #
175 ifdef FOSSIL_ENABLE_TCL
 
 
 
176 LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32
 
177 else
178 LIB += -lkernel32 -lws2_32
179 endif
180
181 #### Tcl shell for use in running the fossil test suite. This is only
182
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -47,10 +47,14 @@
47 # FOSSIL_ENABLE_SSL = 1
48
49 #### Enable scripting support via Tcl/Tk
50 #
51 # FOSSIL_ENABLE_TCL = 1
52
53 #### Load Tcl using the stubs mechanism
54 #
55 # FOSSIL_ENABLE_TCL_STUBS = 1
56
57 #### Use the Tcl source directory instead of the install directory?
58 # This is useful when Tcl has been compiled statically with MinGW.
59 #
60 FOSSIL_TCL_SOURCE = 1
@@ -96,11 +100,15 @@
100 TCLINCDIR = $(TCLDIR)/include
101 TCLLIBDIR = $(TCLDIR)/lib
102
103 #### Tcl: Which Tcl library do we want to use (8.4, 8.5, 8.6, etc)?
104 #
105 ifdef FOSSIL_ENABLE_TCL_STUBS
106 LIBTCL = -ltclstub86
107 else
108 LIBTCL = -ltcl86
109 endif
110
111 #### C Compile and options for use in building executables that
112 # will run on the target platform. This is usually the same
113 # as BCC, unless you are cross-compiling. This C compiler builds
114 # the finished binary for fossil. The BCC compiler above is used
@@ -134,14 +142,22 @@
142 ifdef FOSSIL_ENABLE_SSL
143 TCC += -DFOSSIL_ENABLE_SSL=1
144 RCC += -DFOSSIL_ENABLE_SSL=1
145 endif
146
147 # With Tcl support
148 ifdef FOSSIL_ENABLE_TCL
149 TCC += -DFOSSIL_ENABLE_TCL=1
150 RCC += -DFOSSIL_ENABLE_TCL=1
151 # Either statically linked or via stubs
152 ifdef FOSSIL_ENABLE_TCL_STUBS
153 TCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS
154 RCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS
155 else
156 TCC += -DSTATIC_BUILD
157 RCC += -DSTATIC_BUILD
158 endif
159 endif
160
161 # With JSON support
162 ifdef FOSSIL_ENABLE_JSON
163 TCC += -DFOSSIL_ENABLE_JSON=1
@@ -171,11 +187,15 @@
187
188 #### These libraries MUST appear in the same order as they do for Tcl
189 # or linking with it will not work (exact reason unknown).
190 #
191 ifdef FOSSIL_ENABLE_TCL
192 ifdef FOSSIL_ENABLE_TCL_STUBS
193 LIB += -lkernel32 -lws2_32
194 else
195 LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32
196 endif
197 else
198 LIB += -lkernel32 -lws2_32
199 endif
200
201 #### Tcl shell for use in running the fossil test suite. This is only
202
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -46,11 +46,15 @@
4646
#
4747
FOSSIL_ENABLE_SSL = 1
4848
4949
#### Enable scripting support via Tcl/Tk
5050
#
51
-# FOSSIL_ENABLE_TCL = 1
51
+FOSSIL_ENABLE_TCL = 1
52
+
53
+#### Load Tcl using the stubs mechanism
54
+#
55
+FOSSIL_ENABLE_TCL_STUBS = 1
5256
5357
#### Use the Tcl source directory instead of the install directory?
5458
# This is useful when Tcl has been compiled statically with MinGW.
5559
#
5660
FOSSIL_TCL_SOURCE = 1
@@ -96,11 +100,15 @@
96100
TCLINCDIR = $(TCLDIR)/include
97101
TCLLIBDIR = $(TCLDIR)/lib
98102
99103
#### Tcl: Which Tcl library do we want to use (8.4, 8.5, 8.6, etc)?
100104
#
105
+ifdef FOSSIL_ENABLE_TCL_STUBS
106
+LIBTCL = -ltclstub86
107
+else
101108
LIBTCL = -ltcl86
109
+endif
102110
103111
#### C Compile and options for use in building executables that
104112
# will run on the target platform. This is usually the same
105113
# as BCC, unless you are cross-compiling. This C compiler builds
106114
# the finished binary for fossil. The BCC compiler above is used
@@ -134,14 +142,22 @@
134142
ifdef FOSSIL_ENABLE_SSL
135143
TCC += -DFOSSIL_ENABLE_SSL=1
136144
RCC += -DFOSSIL_ENABLE_SSL=1
137145
endif
138146
139
-# With Tcl support (statically linked)
147
+# With Tcl support
140148
ifdef FOSSIL_ENABLE_TCL
141
-TCC += -DFOSSIL_ENABLE_TCL=1 -DSTATIC_BUILD
149
+TCC += -DFOSSIL_ENABLE_TCL=1
142150
RCC += -DFOSSIL_ENABLE_TCL=1
151
+# Either statically linked or via stubs
152
+ifdef FOSSIL_ENABLE_TCL_STUBS
153
+TCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS
154
+RCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS
155
+else
156
+TCC += -DSTATIC_BUILD
157
+RCC += -DSTATIC_BUILD
158
+endif
143159
endif
144160
145161
# With JSON support
146162
ifdef FOSSIL_ENABLE_JSON
147163
TCC += -DFOSSIL_ENABLE_JSON=1
@@ -171,11 +187,15 @@
171187
172188
#### These libraries MUST appear in the same order as they do for Tcl
173189
# or linking with it will not work (exact reason unknown).
174190
#
175191
ifdef FOSSIL_ENABLE_TCL
192
+ifdef FOSSIL_ENABLE_TCL_STUBS
193
+LIB += -lkernel32 -lws2_32
194
+else
176195
LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32
196
+endif
177197
else
178198
LIB += -lkernel32 -lws2_32
179199
endif
180200
181201
#### Tcl shell for use in running the fossil test suite. This is only
182202
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -46,11 +46,15 @@
46 #
47 FOSSIL_ENABLE_SSL = 1
48
49 #### Enable scripting support via Tcl/Tk
50 #
51 # FOSSIL_ENABLE_TCL = 1
 
 
 
 
52
53 #### Use the Tcl source directory instead of the install directory?
54 # This is useful when Tcl has been compiled statically with MinGW.
55 #
56 FOSSIL_TCL_SOURCE = 1
@@ -96,11 +100,15 @@
96 TCLINCDIR = $(TCLDIR)/include
97 TCLLIBDIR = $(TCLDIR)/lib
98
99 #### Tcl: Which Tcl library do we want to use (8.4, 8.5, 8.6, etc)?
100 #
 
 
 
101 LIBTCL = -ltcl86
 
102
103 #### C Compile and options for use in building executables that
104 # will run on the target platform. This is usually the same
105 # as BCC, unless you are cross-compiling. This C compiler builds
106 # the finished binary for fossil. The BCC compiler above is used
@@ -134,14 +142,22 @@
134 ifdef FOSSIL_ENABLE_SSL
135 TCC += -DFOSSIL_ENABLE_SSL=1
136 RCC += -DFOSSIL_ENABLE_SSL=1
137 endif
138
139 # With Tcl support (statically linked)
140 ifdef FOSSIL_ENABLE_TCL
141 TCC += -DFOSSIL_ENABLE_TCL=1 -DSTATIC_BUILD
142 RCC += -DFOSSIL_ENABLE_TCL=1
 
 
 
 
 
 
 
 
143 endif
144
145 # With JSON support
146 ifdef FOSSIL_ENABLE_JSON
147 TCC += -DFOSSIL_ENABLE_JSON=1
@@ -171,11 +187,15 @@
171
172 #### These libraries MUST appear in the same order as they do for Tcl
173 # or linking with it will not work (exact reason unknown).
174 #
175 ifdef FOSSIL_ENABLE_TCL
 
 
 
176 LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32
 
177 else
178 LIB += -lkernel32 -lws2_32
179 endif
180
181 #### Tcl shell for use in running the fossil test suite. This is only
182
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -46,11 +46,15 @@
46 #
47 FOSSIL_ENABLE_SSL = 1
48
49 #### Enable scripting support via Tcl/Tk
50 #
51 FOSSIL_ENABLE_TCL = 1
52
53 #### Load Tcl using the stubs mechanism
54 #
55 FOSSIL_ENABLE_TCL_STUBS = 1
56
57 #### Use the Tcl source directory instead of the install directory?
58 # This is useful when Tcl has been compiled statically with MinGW.
59 #
60 FOSSIL_TCL_SOURCE = 1
@@ -96,11 +100,15 @@
100 TCLINCDIR = $(TCLDIR)/include
101 TCLLIBDIR = $(TCLDIR)/lib
102
103 #### Tcl: Which Tcl library do we want to use (8.4, 8.5, 8.6, etc)?
104 #
105 ifdef FOSSIL_ENABLE_TCL_STUBS
106 LIBTCL = -ltclstub86
107 else
108 LIBTCL = -ltcl86
109 endif
110
111 #### C Compile and options for use in building executables that
112 # will run on the target platform. This is usually the same
113 # as BCC, unless you are cross-compiling. This C compiler builds
114 # the finished binary for fossil. The BCC compiler above is used
@@ -134,14 +142,22 @@
142 ifdef FOSSIL_ENABLE_SSL
143 TCC += -DFOSSIL_ENABLE_SSL=1
144 RCC += -DFOSSIL_ENABLE_SSL=1
145 endif
146
147 # With Tcl support
148 ifdef FOSSIL_ENABLE_TCL
149 TCC += -DFOSSIL_ENABLE_TCL=1
150 RCC += -DFOSSIL_ENABLE_TCL=1
151 # Either statically linked or via stubs
152 ifdef FOSSIL_ENABLE_TCL_STUBS
153 TCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS
154 RCC += -DFOSSIL_ENABLE_TCL_STUBS=1 -DUSE_TCL_STUBS
155 else
156 TCC += -DSTATIC_BUILD
157 RCC += -DSTATIC_BUILD
158 endif
159 endif
160
161 # With JSON support
162 ifdef FOSSIL_ENABLE_JSON
163 TCC += -DFOSSIL_ENABLE_JSON=1
@@ -171,11 +187,15 @@
187
188 #### These libraries MUST appear in the same order as they do for Tcl
189 # or linking with it will not work (exact reason unknown).
190 #
191 ifdef FOSSIL_ENABLE_TCL
192 ifdef FOSSIL_ENABLE_TCL_STUBS
193 LIB += -lkernel32 -lws2_32
194 else
195 LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32
196 endif
197 else
198 LIB += -lkernel32 -lws2_32
199 endif
200
201 #### Tcl shell for use in running the fossil test suite. This is only
202
--- win/fossil.rc
+++ win/fossil.rc
@@ -90,10 +90,15 @@
9090
#ifdef FOSSIL_ENABLE_SSL
9191
VALUE "SslEnabled", "Yes, " OPENSSL_VERSION_TEXT "\0"
9292
#endif
9393
#ifdef FOSSIL_ENABLE_TCL
9494
VALUE "TclEnabled", "Yes, Tcl " TCL_PATCH_LEVEL "\0"
95
+#ifdef FOSSIL_ENABLE_TCL_STUBS
96
+ VALUE "TclStubsEnabled", "Yes\0"
97
+#else
98
+ VALUE "TclStubsEnabled", "No\0"
99
+#endif
95100
#endif
96101
#ifdef FOSSIL_ENABLE_JSON
97102
VALUE "JsonEnabled", "Yes, cson\0"
98103
#endif
99104
END
100105
--- win/fossil.rc
+++ win/fossil.rc
@@ -90,10 +90,15 @@
90 #ifdef FOSSIL_ENABLE_SSL
91 VALUE "SslEnabled", "Yes, " OPENSSL_VERSION_TEXT "\0"
92 #endif
93 #ifdef FOSSIL_ENABLE_TCL
94 VALUE "TclEnabled", "Yes, Tcl " TCL_PATCH_LEVEL "\0"
 
 
 
 
 
95 #endif
96 #ifdef FOSSIL_ENABLE_JSON
97 VALUE "JsonEnabled", "Yes, cson\0"
98 #endif
99 END
100
--- win/fossil.rc
+++ win/fossil.rc
@@ -90,10 +90,15 @@
90 #ifdef FOSSIL_ENABLE_SSL
91 VALUE "SslEnabled", "Yes, " OPENSSL_VERSION_TEXT "\0"
92 #endif
93 #ifdef FOSSIL_ENABLE_TCL
94 VALUE "TclEnabled", "Yes, Tcl " TCL_PATCH_LEVEL "\0"
95 #ifdef FOSSIL_ENABLE_TCL_STUBS
96 VALUE "TclStubsEnabled", "Yes\0"
97 #else
98 VALUE "TclStubsEnabled", "No\0"
99 #endif
100 #endif
101 #ifdef FOSSIL_ENABLE_JSON
102 VALUE "JsonEnabled", "Yes, cson\0"
103 #endif
104 END
105

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button