SQLite

Changes On Branch mistake
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch mistake Excluding Merge-Ins

This is equivalent to a diff from 54b8888080 to 80dfcc313e

2025-06-03
14:19
Merge latest changes from the wal2 branch into this branch. (Leaf check-in: 93740658c8 user: dan tags: bedrock)
14:08
Merge latest trunk changes into this branch. (Closed-Leaf check-in: 80dfcc313e user: dan tags: mistake)
10:49
Enhance sqlite3_rsync so that it works even if the replica database is initially malformed. (check-in: ea1754f7d8 user: drh tags: trunk)
2025-05-29
14:59
Update the bedrock branch to version 3.50.0 (check-in: 54b8888080 user: drh tags: bedrock)
14:47
Bring the wal2 branch to to version 3.50.0. (check-in: 9f9f81ca9c user: drh tags: wal2)
2025-05-19
16:58
Merge the latest trunk enhancements into the bedrock branch through the wal2 intermediary. (check-in: e7d6e993df user: drh tags: bedrock)

Changes to Makefile.msc.
2671
2672
2673
2674
2675
2676
2677
2678







2679
2680
2681
2682
2683
2684
2685
# are up-to-date.
#
srctree-check:	$(TOP)\tool\srctree-check.tcl
	$(TCLSH_CMD) $(TOP)\tool\srctree-check.tcl

# Testing for a release
#
releasetest:







	$(TCLSH_CMD) $(TOP)\test\testrunner.tcl release


smoketest:	$(TESTPROGS)
	@set PATH=$(LIBTCLPATH);$(PATH)
	.\testfixture.exe $(TOP)\test\main.test $(TESTOPTS)








|
>
>
>
>
>
>
>







2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
# are up-to-date.
#
srctree-check:	$(TOP)\tool\srctree-check.tcl
	$(TCLSH_CMD) $(TOP)\tool\srctree-check.tcl

# Testing for a release
#
releasetest: verify-source
	$(TCLSH_CMD) $(TOP)\test\testrunner.tcl release

# xdevtest is like releasetest, except that it skips the
# dependency on verify-source so that xdevtest can be run from
# a modified source tree.
#
xdevtest:	
	$(TCLSH_CMD) $(TOP)\test\testrunner.tcl release


smoketest:	$(TESTPROGS)
	@set PATH=$(LIBTCLPATH);$(PATH)
	.\testfixture.exe $(TOP)\test\main.test $(TESTOPTS)

Changes to VERSION.
1
3.50.0
|
1
3.51.0
Changes to autoconf/Makefile.in.
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234

#
# Flags to link the shell app either directly against sqlite3.c
# (ENABLE_STATIC_SHELL==1) or libsqlite3.so (ENABLE_STATIC_SHELL==0).
#
ENABLE_STATIC_SHELL = @ENABLE_STATIC_SHELL@
sqlite3-shell-link-flags.1 = $(TOP)/sqlite3.c $(LDFLAGS.libsqlite3)
sqlite3-shell-link-flags.0 = -L. -lsqlite3 $(LDFLAGS.zlib)
sqlite3-shell-deps.1 = $(TOP)/sqlite3.c
sqlite3-shell-deps.0 = $(libsqlite3.DLL)
#
# STATIC_CLI_SHELL = 1 to statically link sqlite3$(T.exe), else
# 0. Requires static versions of all requisite libraries. Primarily
# intended for use with static-friendly environments like Alpine
# Linux.







|







220
221
222
223
224
225
226
227
228
229
230
231
232
233
234

#
# Flags to link the shell app either directly against sqlite3.c
# (ENABLE_STATIC_SHELL==1) or libsqlite3.so (ENABLE_STATIC_SHELL==0).
#
ENABLE_STATIC_SHELL = @ENABLE_STATIC_SHELL@
sqlite3-shell-link-flags.1 = $(TOP)/sqlite3.c $(LDFLAGS.libsqlite3)
sqlite3-shell-link-flags.0 = -L. -lsqlite3 $(LDFLAGS.zlib) $(LDFLAGS.math)
sqlite3-shell-deps.1 = $(TOP)/sqlite3.c
sqlite3-shell-deps.0 = $(libsqlite3.DLL)
#
# STATIC_CLI_SHELL = 1 to statically link sqlite3$(T.exe), else
# 0. Requires static versions of all requisite libraries. Primarily
# intended for use with static-friendly environments like Alpine
# Linux.
Changes to autoconf/tea/Makefile.in.
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169





170
171
172
173
174
175
176

#
# tx.src is the list of source or object files to include in the
# (single) compiler/linker invocation. This will initially contain any
# sources passed to [teaish-src-add], but may also be appended to by
# teaish.make.
#
tx.src     =@TEAISH_EXT_SRC@

#
# tx.CFLAGS is typically set by teaish.make, whereas TEAISH_CFLAGS
# gets set up via the configure script.
#
tx.CFLAGS  =

#
# tx.LDFLAGS is typically set by teaish.make, whereas TEAISH_LDFLAGS
# gets set up via the configure script.
#
tx.LDFLAGS =

#
# The list of 'dist' files may be appended to from teaish.make.in.
# It can also be set up from teaish.tcl using [teaish-dist-add]
# and/or [teaish-src-add -dist ...].
#
tx.dist.files = @TEAISH_DIST_FILES@






# List of deps which may trigger an auto-reconfigure.
#
teaish__autogen.deps = \
  $(tx.makefile.in) $(teaish.makefile.in) \
  $(tx.tcl) \
  @TEAISH_PKGINDEX_TCL_IN@ @TEAISH_TM_TCL_IN@ \
  @AUTODEPS@







|




















>
>
>
>
>







142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181

#
# tx.src is the list of source or object files to include in the
# (single) compiler/linker invocation. This will initially contain any
# sources passed to [teaish-src-add], but may also be appended to by
# teaish.make.
#
tx.src     = @TEAISH_EXT_SRC@

#
# tx.CFLAGS is typically set by teaish.make, whereas TEAISH_CFLAGS
# gets set up via the configure script.
#
tx.CFLAGS  =

#
# tx.LDFLAGS is typically set by teaish.make, whereas TEAISH_LDFLAGS
# gets set up via the configure script.
#
tx.LDFLAGS =

#
# The list of 'dist' files may be appended to from teaish.make.in.
# It can also be set up from teaish.tcl using [teaish-dist-add]
# and/or [teaish-src-add -dist ...].
#
tx.dist.files = @TEAISH_DIST_FILES@

#
# The base name for a distribution tar/zip file.
#
tx.dist.basename = $(tx.name.dist)-$(tx.version)

# List of deps which may trigger an auto-reconfigure.
#
teaish__autogen.deps = \
  $(tx.makefile.in) $(teaish.makefile.in) \
  $(tx.tcl) \
  @TEAISH_PKGINDEX_TCL_IN@ @TEAISH_TM_TCL_IN@ \
  @AUTODEPS@
195
196
197
198
199
200
201
202
203




204

205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
reconfigure:
	$(teaish.autoreconfig)

$(teaish.makefile): $(teaish__auto.def) $(teaish.makefile.in) \
  @AUTODEPS@

@if TEAISH_TESTER_TCL_IN
@TEAISH_TESTER_TCL_IN@:
@TEAISH_TESTER_TCL@: @TEAISH_TESTER_TCL_IN@




config.log: @TEAISH_TESTER_TCL@

@endif

#
# CC variant for compiling Tcl-using sources.
#
CC.tcl = \
  $(CC) -o $@ $(CFLAGS.configure) $(CFLAGS) $(tx.CFLAGS)

#
# CC variant for linking $(tx.src) into an extension DLL.  Note that
# $(tx.src) must come before $(LDFLAGS...) for linking to third-party
# libs to work.
#
CC.dll = \
  $(CC.tcl) $(tx.src) $(LDFLAGS.shlib) \
    $(LDFLAGS.configure) $(LDFLAGS) $(tx.LDFLAGS) $(TCL_STUB_LIB_SPEC)

@if TEAISH_ENABLE_DLL
#
# The rest of this makefile exists solely to support this brief
# target: the extension shared lib.
#
$(tx.dll): $(tx.src) config.log







|
|
>
>
>
>
|
>















|







200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
reconfigure:
	$(teaish.autoreconfig)

$(teaish.makefile): $(teaish__auto.def) $(teaish.makefile.in) \
  @AUTODEPS@

@if TEAISH_TESTER_TCL_IN
@TEAISH_TESTER_TCL_IN@: $(teaish__autogen.deps)
config.log: @TEAISH_TESTER_TCL_IN@
@TEAISH_TESTER_TCL@:  @TEAISH_TESTER_TCL_IN@
@endif
@if TEAISH_TEST_TCL_IN
@TEAISH_TEST_TCL_IN@: $(teaish__autogen.deps)
config.log: @TEAISH_TEST_TCL_IN@
@TEAISH_TEST_TCL@: @TEAISH_TEST_TCL_IN@
@endif

#
# CC variant for compiling Tcl-using sources.
#
CC.tcl = \
  $(CC) -o $@ $(CFLAGS.configure) $(CFLAGS) $(tx.CFLAGS)

#
# CC variant for linking $(tx.src) into an extension DLL.  Note that
# $(tx.src) must come before $(LDFLAGS...) for linking to third-party
# libs to work.
#
CC.dll = \
  $(CC.tcl) $(tx.src) $(LDFLAGS.shlib) \
    $(tx.LDFLAGS) $(LDFLAGS.configure) $(LDFLAGS) $(TCL_STUB_LIB_SPEC)

@if TEAISH_ENABLE_DLL
#
# The rest of this makefile exists solely to support this brief
# target: the extension shared lib.
#
$(tx.dll): $(tx.src) config.log
244
245
246
247
248
249
250
251

252
253
254
255
256
257




258
259
260




261
262
263
264
265
266
267
#
.PHONY: test-pre test-prepre test-core test test-post test-extension
test-extension: # this name is reserved for use by teaish.make[.in]
@if TEAISH_ENABLE_DLL
test-prepre: $(tx.dll)
@endif
@if TEAISH_TESTER_TCL
test-core.args = @TEAISH_TESTER_TCL@

@if TEAISH_ENABLE_DLL
test-core.args += '$(tx.dll)' '$(tx.loadPrefix)'
@else
test-core.args += '' ''
@endif
test-core.args += @TEAISH_TESTUTIL_TCL@




test-core: test-pre
	$(TCLSH) $(test-core.args)
test-prepre: @TEAISH_TESTER_TCL@




@else # !TEAISH_TESTER_TCL
test-prepre:
@endif # TEAISH_TESTER_TCL
test-pre: test-prepre
test-core: test-pre
test-post: test-core
test: test-post







|
>






>
>
>
>

|
|
>
>
>
>







254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
#
.PHONY: test-pre test-prepre test-core test test-post test-extension
test-extension: # this name is reserved for use by teaish.make[.in]
@if TEAISH_ENABLE_DLL
test-prepre: $(tx.dll)
@endif
@if TEAISH_TESTER_TCL
teaish.tester.tcl = @TEAISH_TESTER_TCL@
test-core.args = $(teaish.tester.tcl)
@if TEAISH_ENABLE_DLL
test-core.args += '$(tx.dll)' '$(tx.loadPrefix)'
@else
test-core.args += '' ''
@endif
test-core.args += @TEAISH_TESTUTIL_TCL@
# Clients may pass additional args via test.args=...
# and ::argv will be rewritten before the test script loads, to
# remove $(test-core.args)
test.args ?=
test-core: test-pre
	$(TCLSH) $(test-core.args) $(test.args)
test-gdb: $(teaish.tester.tcl)
	gdb --args $(TCLSH) $(test-core.args) $(test.args)
test-vg.flags ?= --leak-check=full -v --show-reachable=yes --track-origins=yes
test-vg: $(teaish.tester.tcl)
	valgrind $(test-vg.flags) $(TCLSH) $(test-core.args) $(test.args)
@else # !TEAISH_TESTER_TCL
test-prepre:
@endif # TEAISH_TESTER_TCL
test-pre: test-prepre
test-core: test-pre
test-post: test-core
test: test-post
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
	rm -f config.log config.defines.txt
@if TEAISH_MAKEFILE_IN
@if TEAISH_MAKEFILE
	rm -f @TEAISH_MAKEFILE@
@endif
@endif
@if TEAISH_TESTER_TCL_IN
	rm -f @TEAISH_TESTER_TCL@
@endif
@if TEAISH_PKGINDEX_TCL_IN
	rm -f @TEAISH_PKGINDEX_TCL@
@endif
@if TEAISH_PKGINIT_TCL_IN
	rm -f @TEAISH_PKGINIT_TCL@
@endif







|







303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
	rm -f config.log config.defines.txt
@if TEAISH_MAKEFILE_IN
@if TEAISH_MAKEFILE
	rm -f @TEAISH_MAKEFILE@
@endif
@endif
@if TEAISH_TESTER_TCL_IN
	rm -f $(teaish.tester.tcl)
@endif
@if TEAISH_PKGINDEX_TCL_IN
	rm -f @TEAISH_PKGINDEX_TCL@
@endif
@if TEAISH_PKGINIT_TCL_IN
	rm -f @TEAISH_PKGINIT_TCL@
@endif
351
352
353
354
355
356
357




358
359
360
361

362
363
364
365
366
367
368
	@if [ ! -d "$(install-core.tmdir)" ]; then \
		set -x; $(INSTALL) -d "$(install-core.tmdir)"; \
	fi
	$(INSTALL.noexec) "@TEAISH_TM_TCL@" "$(install-core.tmdir)/$(tx.tm.tgt)"
@endif
install-test: install-core
	@echo "Post-install test of [package require $(tx.name.pkg) $(tx.version)]..."; \




	if echo \
		'set c 0; ' \
		'@TEAISH_POSTINST_PREREQUIRE@' \
		'if {[catch {package require $(tx.name.pkg) $(tx.version)}]} {incr c};' \

		'exit $$c' \
		| $(TCLSH) ; then \
		echo "passed"; \
	else \
		echo "FAILED"; \
		exit 1; \
	fi







>
>
>
>

|

|
>







370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
	@if [ ! -d "$(install-core.tmdir)" ]; then \
		set -x; $(INSTALL) -d "$(install-core.tmdir)"; \
	fi
	$(INSTALL.noexec) "@TEAISH_TM_TCL@" "$(install-core.tmdir)/$(tx.tm.tgt)"
@endif
install-test: install-core
	@echo "Post-install test of [package require $(tx.name.pkg) $(tx.version)]..."; \
	set xtra=""; \
	if [ x != "x$(DESTDIR)" ]; then \
		xtra='set ::auto_path [linsert $$::auto_path 0 [file normalize $(DESTDIR)$(TCLLIBDIR)/..]];'; \
	fi; \
	if echo \
		'set c 0; ' $$xtra \
		'@TEAISH_POSTINST_PREREQUIRE@' \
		'if {[catch {package require $(tx.name.pkg) $(tx.version)} xc]} {incr c};' \
		'if {$$c && "" ne $$xc} {puts $$xc; puts "auto_path=$$::auto_path"};' \
		'exit $$c' \
		| $(TCLSH) ; then \
		echo "passed"; \
	else \
		echo "FAILED"; \
		exit 1; \
	fi
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473

# When installing teaish as part of "make dist", we need to run
# configure with similar flags to what we last configured with but we
# must not pass on any extension-specific flags, as those won't be
# recognized when running in --teaish-install mode, causing
# the sub-configure to fail.
dist.flags = --with-tclsh=$(TCLSH)
dist.reconfig = $(teaish.dir)/configure $(dist.flags)

# Temp dir for dist.zip. Must be different than dist.tgz or else
# parallel builds may hose the dist.
teaish__dist.tmp.zip = teaish__dist_zip
#
# Make a distribution zip file...
#
dist.basename = $(tx.name.dist)-$(tx.version)
dist.zip = $(dist.basename).zip
.PHONY: dist.zip dist.zip-core dist.zip-post
#dist.zip-pre:
# We apparently can't add a pre-hook here, else "make dist" rebuilds
# the archive each time it's run.
$(dist.zip): $(tx.dist.files)
	@rm -fr $(teaish__dist.tmp.zip)
	@mkdir -p $(teaish__dist.tmp.zip)/$(dist.basename)
	@tar cf $(teaish__dist.tmp.zip)/tmp.tar $(tx.dist.files)
	@tar xf $(teaish__dist.tmp.zip)/tmp.tar -C $(teaish__dist.tmp.zip)/$(dist.basename)
@if TEAISH_DIST_FULL
	@$(dist.reconfig) \
		--teaish-install=$(teaish__dist.tmp.zip)/$(dist.basename) \
		--t-e-d=$(teaish__dist.tmp.zip)/$(dist.basename) >/dev/null
@endif
	@rm -f $(dist.basename)/tmp.tar $(dist.zip)
	@cd $(teaish__dist.tmp.zip) && zip -q -r ../$(dist.zip) $(dist.basename)
	@rm -fr $(teaish__dist.tmp.zip)
	@ls -la $(dist.zip)
dist.zip-core: $(dist.zip)
dist.zip-post: dist.zip-core
dist.zip: dist.zip-post
dist: dist.zip
undist-zip:
	rm -f $(dist.zip)
undist: undist-zip
@endif #BIN_ZIP

#
# Make a distribution tarball...
#
teaish__dist.tmp.tgz = teaish__dist_tgz
dist.tgz = $(dist.basename).tar.gz
.PHONY: dist.tgz dist.tgz-core dist.tgz-post
# dist.tgz-pre:
# see notes in dist.zip
$(dist.tgz): $(tx.dist.files)
	@rm -fr $(teaish__dist.tmp.tgz)
	@mkdir -p $(teaish__dist.tmp.tgz)/$(dist.basename)
	@tar cf $(teaish__dist.tmp.tgz)/tmp.tar $(tx.dist.files)
	@tar xf $(teaish__dist.tmp.tgz)/tmp.tar -C $(teaish__dist.tmp.tgz)/$(dist.basename)
@if TEAISH_DIST_FULL
	@rm -f $(teaish__dist.tmp.tgz)/$(dist.basename)/pkgIndex.tcl.in; # kludge
	@$(dist.reconfig) \
		--teaish-install=$(teaish__dist.tmp.tgz)/$(dist.basename) \
		--t-e-d=$(teaish__dist.tmp.zip)/$(dist.basename) >/dev/null
@endif
	@rm -f $(dist.basename)/tmp.tar $(dist.tgz)
	@cd $(teaish__dist.tmp.tgz) && tar czf ../$(dist.tgz) $(dist.basename)
	@rm -fr $(teaish__dist.tmp.tgz)
	@ls -la $(dist.tgz)
dist.tgz-core: $(dist.tgz)
dist.tgz-post: dist.tgz-core
dist.tgz: dist.tgz-post
dist: dist.tgz
undist-tgz:







|







<
|






|

|


|
|

|
|















|





|

|

|

|
|

|
|







426
427
428
429
430
431
432
433
434
435
436
437
438
439
440

441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496

# When installing teaish as part of "make dist", we need to run
# configure with similar flags to what we last configured with but we
# must not pass on any extension-specific flags, as those won't be
# recognized when running in --teaish-install mode, causing
# the sub-configure to fail.
dist.flags = --with-tclsh=$(TCLSH)
dist.reconfig = $(teaish.dir)/configure $(tx.dist.reconfig-flags) $(dist.flags)

# Temp dir for dist.zip. Must be different than dist.tgz or else
# parallel builds may hose the dist.
teaish__dist.tmp.zip = teaish__dist_zip
#
# Make a distribution zip file...
#

dist.zip = $(tx.dist.basename).zip
.PHONY: dist.zip dist.zip-core dist.zip-post
#dist.zip-pre:
# We apparently can't add a pre-hook here, else "make dist" rebuilds
# the archive each time it's run.
$(dist.zip): $(tx.dist.files)
	@rm -fr $(teaish__dist.tmp.zip)
	@mkdir -p $(teaish__dist.tmp.zip)/$(tx.dist.basename)
	@tar cf $(teaish__dist.tmp.zip)/tmp.tar $(tx.dist.files)
	@tar xf $(teaish__dist.tmp.zip)/tmp.tar -C $(teaish__dist.tmp.zip)/$(tx.dist.basename)
@if TEAISH_DIST_FULL
	@$(dist.reconfig) \
		--teaish-install=$(teaish__dist.tmp.zip)/$(tx.dist.basename) \
		--t-e-d=$(teaish__dist.tmp.zip)/$(tx.dist.basename) >/dev/null
@endif
	@rm -f $(tx.dist.basename)/tmp.tar $(dist.zip)
	@cd $(teaish__dist.tmp.zip) && zip -q -r ../$(dist.zip) $(tx.dist.basename)
	@rm -fr $(teaish__dist.tmp.zip)
	@ls -la $(dist.zip)
dist.zip-core: $(dist.zip)
dist.zip-post: dist.zip-core
dist.zip: dist.zip-post
dist: dist.zip
undist-zip:
	rm -f $(dist.zip)
undist: undist-zip
@endif #BIN_ZIP

#
# Make a distribution tarball...
#
teaish__dist.tmp.tgz = teaish__dist_tgz
dist.tgz = $(tx.dist.basename).tar.gz
.PHONY: dist.tgz dist.tgz-core dist.tgz-post
# dist.tgz-pre:
# see notes in dist.zip
$(dist.tgz): $(tx.dist.files)
	@rm -fr $(teaish__dist.tmp.tgz)
	@mkdir -p $(teaish__dist.tmp.tgz)/$(tx.dist.basename)
	@tar cf $(teaish__dist.tmp.tgz)/tmp.tar $(tx.dist.files)
	@tar xf $(teaish__dist.tmp.tgz)/tmp.tar -C $(teaish__dist.tmp.tgz)/$(tx.dist.basename)
@if TEAISH_DIST_FULL
	@rm -f $(teaish__dist.tmp.tgz)/$(tx.dist.basename)/pkgIndex.tcl.in; # kludge
	@$(dist.reconfig) \
		--teaish-install=$(teaish__dist.tmp.tgz)/$(tx.dist.basename) \
		--t-e-d=$(teaish__dist.tmp.zip)/$(tx.dist.basename) >/dev/null
@endif
	@rm -f $(tx.dist.basename)/tmp.tar $(dist.tgz)
	@cd $(teaish__dist.tmp.tgz) && tar czf ../$(dist.tgz) $(tx.dist.basename)
	@rm -fr $(teaish__dist.tmp.tgz)
	@ls -la $(dist.tgz)
dist.tgz-core: $(dist.tgz)
dist.tgz-post: dist.tgz-core
dist.tgz: dist.tgz-post
dist: dist.tgz
undist-tgz:
Changes to autoconf/tea/README.txt.
36
37
38
39
40
41
42
43




44
45
46
47
48
49
50
51
52
53
54

55



56
57
58
59
60
61

62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

In both of the above, replace $(TCLSH) with the full pathname of
of the tclsh that you want the SQLite extension to work with.  See
step-by-step instructions at the links below for more information:

    https://sqlite.org/src/doc/trunk/doc/compile-for-unix.md
    https://sqlite.org/src/doc/trunk/doc/compile-for-windows.md





The whole point of the amalgamation-autoconf tarball (in which this
README.txt file is embedded) is to provide a means of compiling
SQLite that does not require first installing TCL and/or "tclsh".
The canonical Makefile in the SQLite source tree provides more
capabilities (such as the the ability to run test cases to ensure
that the build worked) and is better maintained.  The only
downside of the canonical Makefile is that it requires a TCL
installation.  But if you are wanting to build the TCL extension for
SQLite, then presumably you already have a TCL installation.  So why
not just use the more-capable and better-maintained canoncal Makefile?


This TEA builder is derived from code found at




    http://core.tcl-lang.org/tclconfig
    http://core.tcl-lang.org/sampleextension

The SQLite developers do not understand how it works.  It seems to
work for us.  It might also work for you.  But we cannot promise that.


If you want to use this TEA builder and it works for you, that's fine.
But if you have trouble, the first thing you should do is go back
to using the canonical Makefile in the SQLite source tree.

------------------------------------------------------------------


UNIX BUILD
==========

Building under most UNIX systems is easy, just run the configure script
and then run make. For more information about the build process, see
the tcl/unix/README file in the Tcl src dist. The following minimal
example will install the extension in the /opt/tcl directory.

	$ cd sqlite-*-tea
	$ ./configure --prefix=/opt/tcl
	$ make
	$ make install

WINDOWS BUILD
=============

The recommended method to build extensions under windows is to use the
Msys + Mingw build process. This provides a Unix-style build while
generating native Windows binaries. Using the Msys + Mingw build tools
means that you can use the same configure script as per the Unix build
to create a Makefile. See the tcl/win/README file for the URL of
the Msys + Mingw download.

If you have VC++ then you may wish to use the files in the win
subdirectory and build the extension using just VC++. These files have
been designed to be as generic as possible but will require some
additional maintenance by the project developer to synchronise with
the TEA configure.in and Makefile.in files. Instructions for using the
VC++ makefile are written in the first part of the Makefile.vc
file.








>
>
>
>

|
|
|
|
|
|
|
|
|

>
|
>
>
>




<
|
>











|
|
<
<


|
|





<
<
<
<
<
<
|
<
<
<
<
<
|
|
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82


83
84
85
86
87
88
89
90
91






92





93
94

In both of the above, replace $(TCLSH) with the full pathname of
of the tclsh that you want the SQLite extension to work with.  See
step-by-step instructions at the links below for more information:

    https://sqlite.org/src/doc/trunk/doc/compile-for-unix.md
    https://sqlite.org/src/doc/trunk/doc/compile-for-windows.md

And info about the extension's Tcl interface can be found at:

    https://sqlite.org/tclsqlite.html

The whole point of the amalgamation-autoconf tarball (in which this
README.txt file is embedded) is to provide a means of compiling SQLite
that does not require first installing TCL and/or "tclsh".  The
canonical Makefile in the SQLite source tree provides more
capabilities (such as the the ability to run test cases to ensure that
the build worked) and is better maintained.  The only downside of the
canonical Makefile is that it requires a TCL installation.  But if you
are wanting to build the TCL extension for SQLite, then presumably you
already have a TCL installation.  So why not just use the more-capable
and better-maintained canoncal Makefile?

As of version 3.50.0, this build process uses "teaish":

    https://fossil.wanderinghorse.net/r/teaish

which is conceptually derived from the pre-3.50 toolchain, TEA:

    http://core.tcl-lang.org/tclconfig
    http://core.tcl-lang.org/sampleextension


It to works for us.  It might also work for you.  But we cannot
promise that.

If you want to use this TEA builder and it works for you, that's fine.
But if you have trouble, the first thing you should do is go back
to using the canonical Makefile in the SQLite source tree.

------------------------------------------------------------------


UNIX BUILD
==========

Building under most UNIX systems is easy, just run the configure
script and then run make. For example:



	$ cd sqlite-*-tea
	$ ./configure --with-tcl=/path/to/tcl/install/root
	$ make test
	$ make install

WINDOWS BUILD
=============







On Windows this build is known to work on Cygwin and some Msys2





environments. We do not currently support Microsoft makefiles for
native Windows builds.
Changes to autoconf/tea/_teaish.tester.tcl.in.
17
18
19
20
21
22
23

24
25
26
27
28
29
30
31
@TEAISH_VSATISFIES_CODE@
@endif
if {[llength [lindex $::argv 0]] > 0} {
  load [file normalize [lindex $::argv 0]] [lindex $::argv 1];
  # ----^^^^^^^ needed on Haiku when argv 0 is just a filename, else
  # load cannot find the file.
}

source -encoding utf-8 [lindex $::argv 2]; # teaish/tester.tcl
@if TEAISH_PKGINIT_TCL
apply {{file} {
  set dir [file dirname $::argv0]
  source -encoding utf-8 $file
}} [join {@TEAISH_PKGINIT_TCL@}]
@endif
@if TEAISH_TM_TCL







>
|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@TEAISH_VSATISFIES_CODE@
@endif
if {[llength [lindex $::argv 0]] > 0} {
  load [file normalize [lindex $::argv 0]] [lindex $::argv 1];
  # ----^^^^^^^ needed on Haiku when argv 0 is just a filename, else
  # load cannot find the file.
}
set ::argv [lassign $argv - -]
source -encoding utf-8 [lindex $::argv 0]; # teaish/tester.tcl
@if TEAISH_PKGINIT_TCL
apply {{file} {
  set dir [file dirname $::argv0]
  source -encoding utf-8 $file
}} [join {@TEAISH_PKGINIT_TCL@}]
@endif
@if TEAISH_TM_TCL
Deleted autoconf/tea/doc/sqlite3.n.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.TH sqlite3 n 4.1 "Tcl-Extensions"
.HS sqlite3 tcl
.BS
.SH NAME
sqlite3 \- an interface to the SQLite3 database engine
.SH SYNOPSIS
\fBsqlite3\fI command_name ?filename?\fR
.br
.SH DESCRIPTION
SQLite3 is a self-contains, zero-configuration, transactional SQL database
engine.  This extension provides an easy to use interface for accessing
SQLite database files from Tcl.
.PP
For full documentation see \fIhttps://sqlite.org/\fR and
in particular \fIhttps://sqlite.org/tclsqlite.html\fR.
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






























Changes to autoconf/tea/teaish.tcl.
63
64
65
66
67
68
69

70
71
72
73
74
75
76
    -name sqlite
    -name.pkg sqlite3
    -version $version
    -name.dist $distname
    -vsatisfies 8.6-
    -libDir sqlite$version
    -pragmas $pragmas

  }
}} [teaish-get -dir]

#
# Must return either an empty string or a list in the form accepted by
# autosetup's [options] function.
#







>







63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
    -name sqlite
    -name.pkg sqlite3
    -version $version
    -name.dist $distname
    -vsatisfies 8.6-
    -libDir sqlite$version
    -pragmas $pragmas
    -src generic/tclsqlite3.c
  }
}} [teaish-get -dir]

#
# Must return either an empty string or a list in the form accepted by
# autosetup's [options] function.
#
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#
# Gets called by tea-configure-core. Must perform any configuration
# work needed for this extension.
#
proc teaish-configure {} {
  use teaish/feature

  teaish-src-add -dist -dir generic/tclsqlite3.c

  if {[proj-opt-was-provided override-sqlite-version]} {
    teaish-pkginfo-set -version [opt-val override-sqlite-version]
    proj-warn "overriding sqlite version number:" [teaish-pkginfo-get -version]
  } elseif {[proj-opt-was-provided with-system-sqlite]
            && [opt-val with-system-sqlite] ne "0"} {
    proj-fatal "when using --with-system-sqlite also use" \
      "--override-sqlite-version to specify a library version number."







<
<







116
117
118
119
120
121
122


123
124
125
126
127
128
129
#
# Gets called by tea-configure-core. Must perform any configuration
# work needed for this extension.
#
proc teaish-configure {} {
  use teaish/feature



  if {[proj-opt-was-provided override-sqlite-version]} {
    teaish-pkginfo-set -version [opt-val override-sqlite-version]
    proj-warn "overriding sqlite version number:" [teaish-pkginfo-get -version]
  } elseif {[proj-opt-was-provided with-system-sqlite]
            && [opt-val with-system-sqlite] ne "0"} {
    proj-fatal "when using --with-system-sqlite also use" \
      "--override-sqlite-version to specify a library version number."
Changes to autosetup/proj.tcl.
56
57
58
59
60
61
62
63
64


65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

92
93

94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# updating global state via feature tests.
#

#
# $proj__Config is an internal-use-only array for storing whatever generic
# internal stuff we need stored.
#
array set ::proj__Config {
  self-tests 1


}


#
# List of dot-in files to filter in the final stages of
# configuration. Some configuration steps may append to this.  Each
# one in this list which exists will trigger the generation of a
# file with that same name, minus the ".in", in the build directory
# (which differ from the source dir in out-of-tree builds).
#
# See: proj-dot-ins-append and proj-dot-ins-process
#
set ::proj__Config(dot-in-files) [list]
set ::proj__Config(isatty) [isatty? stdout]

#
# @proj-warn msg
#
# Emits a warning message to stderr. All args are appended with a
# space between each.
#
proc proj-warn {args} {
  show-notices
  puts stderr [join [list "WARNING: \[[proj-scope 1]\]: " {*}$args] " "]
}



# Internal impl of [proj-fatal] and [proj-error]. It must be called
# using tailcall.

proc proj__faterr {failMode argv} {
  show-notices
  set lvl 1
  while {"-up" eq [lindex $argv 0]} {
    set argv [lassign $argv -]
    incr lvl
  }
  if {$failMode} {
    puts stderr [join [list "FATAL: \[[proj-scope $lvl]]: " {*}$argv]]
    exit 1
  } else {
    error [join [list "\[[proj-scope $lvl]]:" {*}$argv]]
  }
}


#
# @proj-fatal ?-up...? msg...
#
# Emits an error message to stderr and exits with non-0. All args are
# appended with a space between each.
#
# The calling scope's name is used in the error message. To instead
# use the name of a call higher up in the stack, use -up once for each
# additional level.
#
proc proj-fatal {args} {
  tailcall proj__faterr 1 $args
}

#
# @proj-error ?-up...? msg...
#
# Works like proj-fatal but uses [error] intead of [exit].
#
proc proj-error {args} {
  tailcall proj__faterr 0 $args
}

set ::proj__Config(verbose-assert) [get-env proj-assert-verbose 0]
#
# @proj-assert script ?message?
#
# Kind of like a C assert: if uplevel of [list expr $script] is false,
# a fatal error is triggered. The error message, by default, includes
# the body of the failed assertion, but if $msg is set then that is
# used instead.
#
proc proj-assert {script {msg ""}} {
  if {1 eq $::proj__Config(verbose-assert)} {
    msg-result [proj-bold "asserting: $script"]
  }
  if {![uplevel 1 [list expr $script]]} {
    if {"" eq $msg} {
      set msg $script
    }
    proj-fatal "Assertion failed in \[[proj-scope 1]\]: $msg"
  }
}

#
# @proj-bold str
#
# If this function believes that the current console might support







|
|
>
>
|
<











<









|



>


>
|


|
|



|


|


<












|








|


<
















|







56
57
58
59
60
61
62
63
64
65
66
67

68
69
70
71
72
73
74
75
76
77
78

79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109

110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133

134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# updating global state via feature tests.
#

#
# $proj__Config is an internal-use-only array for storing whatever generic
# internal stuff we need stored.
#
array set ::proj__Config [subst {
  self-tests [get-env proj.self-tests 0]
  verbose-assert [get-env proj.assert-verbose 0]
  isatty [isatty? stdout]
}]


#
# List of dot-in files to filter in the final stages of
# configuration. Some configuration steps may append to this.  Each
# one in this list which exists will trigger the generation of a
# file with that same name, minus the ".in", in the build directory
# (which differ from the source dir in out-of-tree builds).
#
# See: proj-dot-ins-append and proj-dot-ins-process
#
set ::proj__Config(dot-in-files) [list]


#
# @proj-warn msg
#
# Emits a warning message to stderr. All args are appended with a
# space between each.
#
proc proj-warn {args} {
  show-notices
  puts stderr [join [list "WARNING:" \[ [proj-scope 1] \]: {*}$args] " "]
}


#
# Internal impl of [proj-fatal] and [proj-error]. It must be called
# using tailcall.
#
proc proj__faterr {failMode args} {
  show-notices
  set lvl 1
  while {"-up" eq [lindex $args 0]} {
    set args [lassign $args -]
    incr lvl
  }
  if {$failMode} {
    puts stderr [join [list "FATAL:" \[ [proj-scope $lvl] \]: {*}$args]]
    exit 1
  } else {
    error [join [list in \[ [proj-scope $lvl] \]: {*}$args]]
  }
}


#
# @proj-fatal ?-up...? msg...
#
# Emits an error message to stderr and exits with non-0. All args are
# appended with a space between each.
#
# The calling scope's name is used in the error message. To instead
# use the name of a call higher up in the stack, use -up once for each
# additional level.
#
proc proj-fatal {args} {
  tailcall proj__faterr 1 {*}$args
}

#
# @proj-error ?-up...? msg...
#
# Works like proj-fatal but uses [error] intead of [exit].
#
proc proj-error {args} {
  tailcall proj__faterr 0 {*}$args
}


#
# @proj-assert script ?message?
#
# Kind of like a C assert: if uplevel of [list expr $script] is false,
# a fatal error is triggered. The error message, by default, includes
# the body of the failed assertion, but if $msg is set then that is
# used instead.
#
proc proj-assert {script {msg ""}} {
  if {1 eq $::proj__Config(verbose-assert)} {
    msg-result [proj-bold "asserting: $script"]
  }
  if {![uplevel 1 [list expr $script]]} {
    if {"" eq $msg} {
      set msg $script
    }
    tailcall proj__faterr 1 "Assertion failed:" $msg
  }
}

#
# @proj-bold str
#
# If this function believes that the current console might support
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
    2 {
      lappend fileIn {*}$args
    }
    default {
      proj-fatal "Too many arguments: $fileIn $args"
    }
  }
  #puts "******* [proj-scope]: adding $fileIn"
  lappend ::proj__Config(dot-in-files) $fileIn
}

#
# @proj-dot-ins-list
#
# Returns the current list of [proj-dot-ins-append]'d files, noting







|







1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
    2 {
      lappend fileIn {*}$args
    }
    default {
      proj-fatal "Too many arguments: $fileIn $args"
    }
  }
  #puts "******* [proj-scope]: adding [llength $fileIn]-length item: $fileIn"
  lappend ::proj__Config(dot-in-files) $fileIn
}

#
# @proj-dot-ins-list
#
# Returns the current list of [proj-dot-ins-append]'d files, noting
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710

1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
#  associated script, if any, it runs the file through
#  proj-validate-no-unresolved-ats, erroring out if that does.
#
# -clear: after processing, empty the dot-ins list. This effectively
#  makes proj-dot-ins-append available for re-use.
#
proc proj-dot-ins-process {args} {
  proj-parse-simple-flags args flags {
    -touch   "" {return "-touch"}
    -clear    0 {expr 1}
    -validate 0 {expr 1}
  }

  if {[llength $args] > 0} {
    error "Invalid argument to [proj-scope]: $args"
  }
  foreach f $::proj__Config(dot-in-files) {
    proj-assert {3==[llength $f]} \
      "Expecting proj-dot-ins-list to be stored in 3-entry lists"
    lassign $f fIn fOut fScript
    #puts "DOING $fIn  ==> $fOut"
    proj-make-from-dot-in {*}$flags(-touch) $fIn $fOut
    if {$flags(-validate)} {
      proj-validate-no-unresolved-ats $fOut
    }
    if {"" ne $fScript} {







|




>





|







1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
#  associated script, if any, it runs the file through
#  proj-validate-no-unresolved-ats, erroring out if that does.
#
# -clear: after processing, empty the dot-ins list. This effectively
#  makes proj-dot-ins-append available for re-use.
#
proc proj-dot-ins-process {args} {
  proj-parse-flags args flags {
    -touch   "" {return "-touch"}
    -clear    0 {expr 1}
    -validate 0 {expr 1}
  }
  #puts "args=$args"; parray flags
  if {[llength $args] > 0} {
    error "Invalid argument to [proj-scope]: $args"
  }
  foreach f $::proj__Config(dot-in-files) {
    proj-assert {3==[llength $f]} \
      "Expecting proj-dot-ins-list to be stored in 3-entry lists. Got: $f"
    lassign $f fIn fOut fScript
    #puts "DOING $fIn  ==> $fOut"
    proj-make-from-dot-in {*}$flags(-touch) $fIn $fOut
    if {$flags(-validate)} {
      proj-validate-no-unresolved-ats $fOut
    }
    if {"" ne $fScript} {
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
#
proc proj-validate-no-unresolved-ats {args} {
  foreach f $args {
    set lnno 1
    set isMake [string match {*[Mm]ake*} $f]
    foreach line [proj-file-content-list $f] {
      if {!$isMake || ![string match "#*" [string trimleft $line]]} {
        if {[regexp {(@[A-Za-z0-9_]+@)} $line match]} {
          error "Unresolved reference to $match at line $lnno of $f"
        }
      }
      incr lnno
    }
  }
}







|







1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
#
proc proj-validate-no-unresolved-ats {args} {
  foreach f $args {
    set lnno 1
    set isMake [string match {*[Mm]ake*} $f]
    foreach line [proj-file-content-list $f] {
      if {!$isMake || ![string match "#*" [string trimleft $line]]} {
        if {[regexp {(@[A-Za-z0-9_\.]+@)} $line match]} {
          error "Unresolved reference to $match at line $lnno of $f"
        }
      }
      incr lnno
    }
  }
}
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
#
# By default it returns the result as string of all -D... flags,
# but if passed the -list flag it will return a list of the
# individual CFLAGS.
#
proc proj-define-to-cflag {args} {
  set rv {}
  proj-parse-simple-flags args flags {
    -list       0 {expr 1}
    -quote      0 {expr 1}
    -zero-undef 0 {expr 1}
  }
  foreach d $args {
    set v [get-define $d ""]
    set li {}







|







1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
#
# By default it returns the result as string of all -D... flags,
# but if passed the -list flag it will return a list of the
# individual CFLAGS.
#
proc proj-define-to-cflag {args} {
  set rv {}
  proj-parse-flags args flags {
    -list       0 {expr 1}
    -quote      0 {expr 1}
    -zero-undef 0 {expr 1}
  }
  foreach d $args {
    set v [get-define $d ""]
    set li {}
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
# @proj-cache-set ?-key KEY? ?-level 0? value
#
# Sets a feature-check cache entry with the given key.
#
# See proj-cache-key for -key's and -level's semantics, noting that
# this function adds one to -level for purposes of that call.
proc proj-cache-set {args} {
  proj-parse-simple-flags args flags {
    -key => 0
    -level => 0
  }
  lassign $args val
  set key [proj-cache-key $flags(-key) [expr {1 + $flags(-level)}]]
  #puts "** fcheck set $key = $val"
  set ::proj__Cache($key) $val







|







1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
# @proj-cache-set ?-key KEY? ?-level 0? value
#
# Sets a feature-check cache entry with the given key.
#
# See proj-cache-key for -key's and -level's semantics, noting that
# this function adds one to -level for purposes of that call.
proc proj-cache-set {args} {
  proj-parse-flags args flags {
    -key => 0
    -level => 0
  }
  lassign $args val
  set key [proj-cache-key $flags(-key) [expr {1 + $flags(-level)}]]
  #puts "** fcheck set $key = $val"
  set ::proj__Cache($key) $val
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
# If the feature-check cache has a matching entry then this function
# assigns its value to tgtVar and returns 1, else it assigns tgtVar to
# "" and returns 0.
#
# See proj-cache-key for $key's and $addLevel's semantics, noting that
# this function adds one to $addLevel for purposes of that call.
proc proj-cache-check {args} {
  proj-parse-simple-flags args flags {
    -key => 0
    -level => 0
  }
  lassign $args tgtVar
  upvar $tgtVar tgt
  set rc 0
  set key [proj-cache-key $flags(-key) [expr {1 + $flags(-level)}]]







|







2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
# If the feature-check cache has a matching entry then this function
# assigns its value to tgtVar and returns 1, else it assigns tgtVar to
# "" and returns 0.
#
# See proj-cache-key for $key's and $addLevel's semantics, noting that
# this function adds one to $addLevel for purposes of that call.
proc proj-cache-check {args} {
  proj-parse-flags args flags {
    -key => 0
    -level => 0
  }
  lassign $args tgtVar
  upvar $tgtVar tgt
  set rc 0
  set key [proj-cache-key $flags(-key) [expr {1 + $flags(-level)}]]
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077



2078
2079


2080
2081
2082

2083
2084





2085
2086

















2087
2088
2089
2090
2091

2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102










2103

2104






2105
2106
2107

2108
2109
2110
2111
2112
2113
2114
2115
2116
2117

2118
2119
2120

2121
2122
2123
2124
2125
2126
2127
2128

2129
2130
2131
2132
2133
2134




2135
2136
2137
2138
2139
2140
2141
2142
2143
2144





2145
2146




























2147
2148
2149









2150
2151
2152





2153




2154
2155

2156
2157
2158
2159
2160
2161







2162


2163

2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174

2175
2176
2177
2178

2179
2180

2181
2182
2183





2184
2185







2186


2187
2188

2189
2190
2191

2192
2193
2194

2195


2196




2197

2198








2199






2200
2201











2202
2203
2204

2205
2206
2207
2208
2209


2210







2211
2212
2213




2214
2215
2216
2217
2218
2219
2220
      return $arg
    }
  }
  return ""
}

#
# @proj-parse-simple-flags ...
#
# A helper to parse flags from proc argument lists.
#
# Expects a list of arguments to parse, an array name to store any



# -flag values to, and a prototype object which declares the flags.
#


# The prototype must be a list in one of the following forms:
#
#   -flag defaultValue {script}

#
#   -flag => defaultValue





#   -----^--^ (with spaces there!)
#

















# Repeated for each flag.
#
# The first form represents a basic flag with no associated
# following argument. The second form extracts its value
# from the following argument in $argvName.

#
# The first argument to this function is the name of a var holding the
# args to parse. It will be overwritten, possibly with a smaller list.
#
# The second argument the name of an array variable to create in the
# caller's scope. (Pneumonic: => points to the next argument.)
#
# For the first form of flag, $script is run in the caller's scope if
# $argv contains -flag, and the result of that script is the new value
# for $tgtArrayName(-flag). This function intercepts [return $val]
# from $script. Any empty script will result in the flag having ""










# assigned to it.

#






# The args list is only inspected until the first argument which is
# not described by $prototype. i.e. the first "non-flag" (not counting
# values consumed for flags defined like --flag=>default).

#
# If a "--" flag is encountered, no more arguments are inspected as
# flags. If "--" is the first non-flag argument, the "--" flag is
# removed from the results but all remaining arguments are passed
# through. If "--" appears after the first non-flag, it is retained.
#
# This function assumes that each flag is unique, and using a flag
# more than once behaves in a last-one-wins fashion.
#
# Any argvName entries not described in $prototype are not treated as

# flags.
#
# Returns the number of flags it processed in $argvName.

#
# Example:
#
# set args [list -foo -bar {blah} 8 9 10 -theEnd]
# proj-parse-simple-flags args flags {
#   -foo    0  {expr 1}
#   -bar    => 0
#   -no-baz 2  {return 0}

# }
#
# After that $flags would contain {-foo 1 -bar {blah} -no-baz 2}
# and $args would be {8 9 10 -theEnd}.
#
# Potential TODOs: consider using lappend instead of set so that any




# given flag can be used more than once. Or add a syntax to indicate
# that multiples are allowed. Also consider searching the whole
# argv list, rather than stopping at the first non-flag
#
proc proj-parse-simple-flags {argvName tgtArrayName prototype} {
  upvar $argvName argv
  upvar $tgtArrayName tgt
  array set dflt {}
  array set scripts {}
  array set consuming {}





  set n [llength $prototype]
  # Figure out what our flags are...




























  for {set i 0} {$i < $n} {incr i} {
    set k [lindex $prototype $i]
    #puts "**** #$i of $n k=$k"









    proj-assert {[string match -* $k]} \
      "Invalid flag value: $k"
    set v ""





    set s ""




    switch -exact -- [lindex $prototype [expr {$i + 1}]] {
      => {

        incr i 2
        if {$i >= $n} {
          proj-error "Missing argument for $k => flag"
        }
        set consuming($k) 1
        set v [lindex $prototype $i]







      }


      default {

        set v [lindex $prototype [incr i]]
        set s [lindex $prototype [incr i]]
        set scripts($k) $s
      }
    }
    #puts "**** #$i of $n k=$k v=$v s=$s"
    set dflt($k) $v
  }
  # Now look for those flags in the source list
  array set tgt [array get dflt]
  unset dflt

  set rc 0
  set rv {}
  set skipMode 0
  set n [llength $argv]

  for {set i 0} {$i < $n} {incr i} {
    set arg [lindex $argv $i]

    if {$skipMode} {
      lappend rv $arg
    } elseif {"--" eq $arg} {





      incr skipMode
    } elseif {[info exists tgt($arg)]} {







      if {[info exists consuming($arg)]} {


        if {$i + 1 >= $n} {
          proj-assert 0 {Cannot happen - bounds already checked}

        }
        set tgt($arg) [lindex $argv [incr i]]
      } elseif {"" eq $scripts($arg)} {

        set tgt($arg) ""
      } else {
        #puts "**** running scripts($arg) $scripts($arg)"

        set code [catch {uplevel 1 $scripts($arg)} xrc xopt]


        #puts "**** tgt($arg)=$scripts($arg) code=$code rc=$rc"




        if {$code in {0 2}} {

          set tgt($arg) $xrc








        } else {






          return {*}$xopt $xrc
        }











      }
      incr rc
    } else {

      incr skipMode
      lappend rv $arg
    }
  }
  set argv $rv


  return $rc







}

if {$::proj__Config(self-tests)} {




  apply {{} {
    #proj-warn "Test code for proj-cache"
    proj-assert {![proj-cache-check -key here check]}
    proj-assert {"here" eq [proj-cache-key here]}
    proj-assert {"" eq $check}
    proj-cache-set -key here thevalue
    proj-assert {[proj-cache-check -key here check]}







|



|
>
>
>
|

>
>
|

|
>

|
>
>
>
>
>
|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|

<
|
|
>

|
|

|
<

|
|
|
|
>
>
>
>
>
>
>
>
>
>
|
>

>
>
>
>
>
>
|
|
|
>


|
|
|

<
<
<
|
>
|

|
>



|
|
|
|
|
>
|

|


|
>
>
>
>
|
<
|

|

|
|
|
|
>
>
>
>
>

|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



>
>
>
>
>
>
>
>
>

|
|
>
>
>
>
>
|
>
>
>
>


>


|

|
|
>
>
>
>
>
>
>
|
>
>

>
|
|
|


|
|

|
|
<
>




>


>



>
>
>
>
>
|
|
>
>
>
>
>
>
>
|
>
>
|
<
>

|
|
>
|

|
>
|
>
>
|
>
>
>
>
|
>
|
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
|

>
>
>
>
>
>
>
>
>
>
>



>
|




>
>

>
>
>
>
>
>
>



>
>
>
>







2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117

2118
2119
2120
2121
2122
2123
2124
2125

2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159



2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185

2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284

2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314

2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
      return $arg
    }
  }
  return ""
}

#
# @proj-parse-flags argvListName targetArrayName {prototype}
#
# A helper to parse flags from proc argument lists.
#
# The first argument is the name of a var holding the args to
# parse. It will be overwritten, possibly with a smaller list.
#
# The second argument is the name of an array variable to create in
# the caller's scope.
#
# The third argument, $prototype, is a description of how to handle
# the flags. Each entry in that list must be in one of the
# following forms:
#
#   -flag  defaultValue ?-literal|-call|-apply?
#                       script|number|incr|proc-name|{apply $aLambda}
#
#   -flag* ...as above...
#
#   -flag  => defaultValue ?-call proc-name-and-args|-apply lambdaExpr?
#
#   -flag* => ...as above...
#
#   :PRAGMA
#
# The first two forms represents a basic flag with no associated
# following argument. The third and fourth forms, called arg-consuming
# flags, extract the value from the following argument in $argvName
# (pneumonic: => points to the next argument.). The :PRAGMA form
# offers a way to configure certain aspects of this call.
#
# If $argv contains any given flag from $prototype, its default value
# is overridden depending on several factors:
#
#  - If the -literal flag is used, or the flag's script is a number,
#    value is used verbatim.
#
#  - Else if the -call flag is used, the argument must be a proc name
#    and any leading arguments, e.g. {apply $myLambda}.  The proc is passed
#    the (flag, value) as arguments (non-consuming flags will get
#    passed the flag's current/starting value and consuming flags will
#    get the next argument).  Its result becomes the result of the
#    flag.
#

#  - Else if -apply X is used, it's effectively shorthand for -call
#    {apply X}. Its argument may either be a $lambaRef or a {{f v}
#    {body}} construct.
#
#  - Else if $script is one of the following values, it is treated as
#    the result of...
#
#    - incr: increments the current value of the flag.

#
#  - Else $script is eval'd to get its result value. That result
#    becomes the new flag value for $tgtArrayName(-flag). This
#    function intercepts [return $val] from eval'ing $script.  Any
#    empty script will result in the flag having "" assigned to it.
#
# Unless the -flag has a trailing asterisk, e.g. -flag*, this function
# assumes that each flag is unique, and using a flag more than once
# causes an error to be triggered. the -flag* forms works similarly
# except that may appear in $argv any number of times:
#
#  - For non-arg-consuming flags, each invocation of -flag causes the
#    result of $script to overwrite the previous value. e.g. so
#    {-flag* {x} {incr foo}} has a default value of x, but passing in
#    -flag twice would change it to the result of incrementing foo
#    twice. This form can be used to implement, e.g., increasing
#    verbosity levels by passing -verbose multiple times.
#
#  - For arg-consuming flags, the given flag starts with value X, but
#    if the flag is provided in $argv, the default is cleared, then
#    each instance of -flag causes its value to be appended to the
#    result, so {-flag* => {a b c}} defaults to {a b c}, but passing
#    in -flag y -flag z would change it to {y z}, not {a b c y z}..
#
# By default, the args list is only inspected until the first argument
# which is not described by $prototype. i.e. the first "non-flag" (not
# counting values consumed for flags defined like -flag => default).
# The :all-flags pragma (see below) can modify this behavior.
#
# If a "--" flag is encountered, no more arguments are inspected as
# flags unless the :all-flags pragma (see below) is in effect. The
# first instance of "--" is removed from the target result list but
# all remaining instances of "--" are are passed through.
#



# Any argvName entries not described in $prototype are considered to
# be "non-flags" for purposes of this function, even if they
# ostensibly look like flags.
#
# Returns the number of flags it processed in $argvName, not counting
# "--".
#
# Example:
#
## set args [list -foo -bar {blah} -z 8 9 10 -theEnd]
## proj-parse-flags args flags {
##   -foo    0  {expr 1}
##   -bar    => 0
##   -no-baz 1  {return 0}
##   -z 0 2
## }
#
# After that $flags would contain {-foo 1 -bar {blah} -no-baz 1 -z 2}
# and $args would be {8 9 10 -theEnd}.
#
# Pragmas:
#
# Passing :PRAGMAS to this function may modify how it works. The
# following pragmas are supported (note the leading ":"):
#
#   :all-flags indicates that the whole input list should be scanned,

#   not stopping at the first non-flag or "--".
#
proc proj-parse-flags {argvName tgtArrayName prototype} {
  upvar $argvName argv
  upvar $tgtArrayName outFlags
  array set flags {}; # staging area
  array set scripts {};      # map of -flag=>script
  array set consuming {};    # map of -flag=>1 for arg-consuming flags
  array set multi {};        # map of -flag=>1 for multi-time flags
  array set seen {};         # map of -flag=>number of times seen
  array set call {};         # map of -flag=>1 for -call entries
  set incrSkip 1; # 1 if we stop at the first non-flag, else 0
  # Parse $prototype for flag definitions...
  set n [llength $prototype]
  set checkProtoFlag {
    #puts "**** checkProtoFlag #$i of $n k=$k fv=$fv"
    switch -exact -- $fv {
      -literal {
        proj-assert {![info exists consuming($k)]}
        set scripts($k) [list expr [lindex $prototype [incr i]]]
      }
      -apply {
        set fv [lindex $prototype [incr i]]
        if {2 == [llength $fv]} {
          # Treat this as a lambda literal
          set fv [list $fv]
        }
        lappend call($k) "apply $fv"
      }
      -call {
        # arg is either a proc name or {apply $aLambda}
        set fv [lindex $prototype [incr i]]
        lappend call($k) $fv
      }
      default {
        proj-assert {![info exists consuming($k)]}
        set scripts($k) $fv
      }
    }
    if {$i >= $n} {
      proj-error -up "[proj-scope]: Missing argument for $k flag"
    }
  }
  for {set i 0} {$i < $n} {incr i} {
    set k [lindex $prototype $i]
    #puts "**** #$i of $n k=$k"

    # Check for :PRAGMA...
    switch -exact -- $k {
      :all-flags {
        set incrSkip 0
        continue
      }
    }

    proj-assert {[string match -* $k]} \
      "Invalid argument: $k"

    if {[string match {*\*} $k]} {
      # Re-map -foo* to -foo and flag -foo as a repeatable flag
      set k [string map {* ""} $k]
      incr multi($k)
    }

    if {[info exists flags($k)]} {
      proj-error -up "[proj-scope]: Duplicated prototype for flag $k"
    }

    switch -exact -- [lindex $prototype [expr {$i + 1}]] {
      => {
        # -flag => DFLT ?-subflag arg?
        incr i 2
        if {$i >= $n} {
          proj-error -up "[proj-scope]: Missing argument for $k => flag"
        }
        incr consuming($k)
        set vi [lindex $prototype $i]
        if {$vi in {-apply -call}} {
          proj-error -up "[proj-scope]: Missing default value for $k flag"
        } else {
          set fv [lindex $prototype [expr {$i + 1}]]
          if {$fv in {-apply -call}} {
            incr i
            eval $checkProtoFlag
          }
        }
      }
      default {
        # -flag VALUE ?flag? SCRIPT
        set vi [lindex $prototype [incr i]]
        set fv [lindex $prototype [incr i]]
        eval $checkProtoFlag
      }
    }
    #puts "**** #$i of $n k=$k vi=$vi"
    set flags($k) $vi
  }
  #puts "-- flags"; parray flags
  #puts "-- scripts"; parray scripts

  #puts "-- calls"; parray call
  set rc 0
  set rv {}
  set skipMode 0
  set n [llength $argv]
  # Now look for those flags in $argv...
  for {set i 0} {$i < $n} {incr i} {
    set arg [lindex $argv $i]
    #puts "-- [proj-scope] arg=$arg"
    if {$skipMode} {
      lappend rv $arg
    } elseif {"--" eq $arg} {
      # "--" is the conventional way to end processing of args
      if {[incr seen(--)] > 1} {
        # Elide only the first one
        lappend rv $arg
      }
      incr skipMode $incrSkip
    } elseif {[info exists flags($arg)]} {
      # A known flag...
      set isMulti [info exists multi($arg)]
      incr seen($arg)
      if {1 < $seen($arg) && !$isMulti} {
        proj-error -up [proj-scope] "$arg flag was used multiple times"
      }
      set vMode 0; # 0=as-is, 1=eval, 2=call
      set isConsuming [info exists consuming($arg)]
      if {$isConsuming} {
        incr i
        if {$i >= $n} {

          proj-error -up [proj-scope] "is missing argument for $arg flag"
        }
        set vv [lindex $argv $i]
      } elseif {[info exists scripts($arg)]} {
        set vMode 1
        set vv $scripts($arg)
      } else {
        set vv $flags($arg)
      }

      if {[info exists call($arg)]} {
        set vMode 2
        set vv [concat {*}$call($arg) $arg $vv]
      } elseif {$isConsuming} {
        proj-assert {!$vMode}
        # fall through
      } elseif {"" eq $vv || [string is double -strict $vv]} {
        set vMode 0
      } elseif {$vv in {incr}} {
        set vMode 0
        switch -exact $vv {
          incr {
            set xx $flags($k); incr xx; set vv $xx; unset xx
          }
          default {
            proj-error "Unhandled \$vv value $vv"
          }
        }
      } else {
        set vv [list eval $vv]
        set vMode 1
      }
      if {$vMode} {
        set code [catch [list uplevel 1 $vv] vv xopt]
        if {$code ni {0 2}} {
          return {*}$xopt $vv
        }
      }
      if {$isConsuming && $isMulti} {
        if {1 == $seen($arg)} {
          # On the first hit, overwrite the default with a new list.
          set flags($arg) [list $vv]
        } else {
          # On subsequent hits, append to the list.
          lappend flags($arg) $vv
        }
      } else {
        set flags($arg) $vv
      }
      incr rc
    } else {
      # Non-flag
      incr skipMode $incrSkip
      lappend rv $arg
    }
  }
  set argv $rv
  array set outFlags [array get flags]
  #puts "-- rv=$rv argv=$argv flags="; parray flags
  return $rc
}; # proj-parse-flags

#
# Older (deprecated) name of proj-parse-flags.
#
proc proj-parse-simple-flags {args} {
  tailcall proj-parse-flags {*}$args
}

if {$::proj__Config(self-tests)} {
  set __ova $::proj__Config(verbose-assert);
  set ::proj__Config(verbose-assert) 1
  puts "Running [info script] self-tests..."
  # proj-cache...
  apply {{} {
    #proj-warn "Test code for proj-cache"
    proj-assert {![proj-cache-check -key here check]}
    proj-assert {"here" eq [proj-cache-key here]}
    proj-assert {"" eq $check}
    proj-cache-set -key here thevalue
    proj-assert {[proj-cache-check -key here check]}
2229
2230
2231
2232
2233
2234
2235
2236

































































































    #parray ::proj__Cache;
    proj-assert {"" ne [proj-cache-remove]}
    proj-assert {![proj-cache-check check]}
    proj-assert {"" eq [proj-cache-remove]}
    proj-assert {"" eq $check}
  }}
}







































































































|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507

    #parray ::proj__Cache;
    proj-assert {"" ne [proj-cache-remove]}
    proj-assert {![proj-cache-check check]}
    proj-assert {"" eq [proj-cache-remove]}
    proj-assert {"" eq $check}
  }}

  # proj-parse-flags ...
  apply {{} {
    set foo 3
    set argv {-a "hi - world" -b -b -b -- -a {bye bye} -- -d -D c -a "" --}
    proj-parse-flags argv flags {
      :all-flags
      -a* => "gets overwritten"
      -b* 7 {incr foo}
      -d 1 0
      -D 0 1
    }

    #puts "-- argv = $argv"; parray flags;
    proj-assert {"-- c --" eq $argv}
    proj-assert {$flags(-a) eq "{hi - world} {bye bye} {}"}
    proj-assert {$foo == 6}
    proj-assert {$flags(-b) eq $foo}
    proj-assert {$flags(-d) == 0}
    proj-assert {$flags(-D) == 1}
    set foo 0
    foreach x $flags(-a) {
      proj-assert {$x in {{hi - world} {bye bye} {}}}
      incr foo
    }
    proj-assert {3 == $foo}

    set argv {-a {hi world} -b -maybe -- -a {bye bye} -- -b c --}
    set foo 0
    proj-parse-flags argv flags {
      -a => "aaa"
      -b 0 {incr foo}
      -maybe no -literal yes
    }
    #parray flags; puts "--- argv = $argv"
    proj-assert {"-a {bye bye} -- -b c --" eq $argv}
    proj-assert {$flags(-a) eq "hi world"}
    proj-assert {1 == $flags(-b)}
    proj-assert {"yes" eq $flags(-maybe)}

    set argv {-f -g -a aaa -M -M -M -L -H -A AAA a b c}
    set foo 0
    set myLambda {{flag val} {
      proj-assert {$flag in {-f -g -M}}
      #puts "myLambda flag=$flag val=$val"
      incr val
    }}
    proc myNonLambda {flag val} {
      proj-assert {$flag in {-A -a}}
      #puts "myNonLambda flag=$flag val=$val"
      concat $val $val
    }
    proj-parse-flags argv flags {
      -f 0 -call {apply $myLambda}
      -g 2 -apply $myLambda
      -h 3 -apply $myLambda
      -H 30 33
      -a => aAAAa -apply {{f v} {
        set v
      }}
      -A => AaaaA -call myNonLambda
      -B => 17 -call myNonLambda
      -M* 0 -apply $myLambda
      -L "" -literal $myLambda
    }
    rename myNonLambda ""
    #puts "--- argv = $argv"; parray flags
    proj-assert {$flags(-f) == 1}
    proj-assert {$flags(-g) == 3}
    proj-assert {$flags(-h) == 3}
    proj-assert {$flags(-H) == 33}
    proj-assert {$flags(-a) == {aaa}}
    proj-assert {$flags(-A) eq "AAA AAA"}
    proj-assert {$flags(-B) == 17}
    proj-assert {$flags(-M) == 3}
    proj-assert {$flags(-L) eq $myLambda}

    set argv {-touch -validate}
    proj-parse-flags argv flags {
      -touch "" {return "-touch"}
      -validate 0 1
    }
    #puts "----- argv = $argv"; parray flags
    proj-assert {$flags(-touch) eq "-touch"}
    proj-assert {$flags(-validate) == 1}
    proj-assert {$argv eq {}}

    set argv {-i -i -i}
    proj-parse-flags argv flags {
      -i* 0 incr
    }
    proj-assert {3 == $flags(-i)}
  }}
  set ::proj__Config(verbose-assert) $__ova
  unset __ova
  puts "Done running [info script] self-tests."
}; # proj- API self-tests
Changes to ext/fts3/fts3Int.h.
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223

#define deliberate_fall_through

/*
** Macros needed to provide flexible arrays in a portable way
*/
#ifndef offsetof
# define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
#endif
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
# define FLEXARRAY
#else
# define FLEXARRAY 1
#endif








|







209
210
211
212
213
214
215
216
217
218
219
220
221
222
223

#define deliberate_fall_through

/*
** Macros needed to provide flexible arrays in a portable way
*/
#ifndef offsetof
# define offsetof(ST,M) ((size_t)((char*)&((ST*)0)->M - (char*)0))
#endif
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
# define FLEXARRAY
#else
# define FLEXARRAY 1
#endif

Changes to ext/fts5/fts5Int.h.
16
17
18
19
20
21
22

23
24
25
26
27
28
29

#include "fts5.h"
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1

#include <string.h>
#include <assert.h>


#ifndef SQLITE_AMALGAMATION

typedef unsigned char  u8;
typedef unsigned int   u32;
typedef unsigned short u16;
typedef short i16;







>







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

#include "fts5.h"
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1

#include <string.h>
#include <assert.h>
#include <stddef.h>

#ifndef SQLITE_AMALGAMATION

typedef unsigned char  u8;
typedef unsigned int   u32;
typedef unsigned short u16;
typedef short i16;
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# define EIGHT_BYTE_ALIGNMENT(X)   ((((uptr)(X) - (uptr)0)&7)==0)
#endif

/*
** Macros needed to provide flexible arrays in a portable way
*/
#ifndef offsetof
# define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
#endif
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
# define FLEXARRAY
#else
# define FLEXARRAY 1
#endif








|







76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# define EIGHT_BYTE_ALIGNMENT(X)   ((((uptr)(X) - (uptr)0)&7)==0)
#endif

/*
** Macros needed to provide flexible arrays in a portable way
*/
#ifndef offsetof
# define offsetof(ST,M) ((size_t)((char*)&((ST*)0)->M - (char*)0))
#endif
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
# define FLEXARRAY
#else
# define FLEXARRAY 1
#endif

Changes to ext/jni/src/org/sqlite/jni/capi/AggregateFunction.java.
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
     To be called from the implementation's xStep() method, as well
     as the xValue() and xInverse() methods of the {@link WindowFunction}
     subclass, to fetch the current per-call UDF state. On the
     first call to this method for any given sqlite3_context
     argument, the context is set to the given initial value. On all other
     calls, the 2nd argument is ignored.

     @see SQLFunction.PerContextState#getAggregateState
  */
  protected final ValueHolder<T> getAggregateState(sqlite3_context cx, T initialValue){
    return map.getAggregateState(cx, initialValue);
  }

  /**
     To be called from the implementation's xFinal() method to fetch
     the final state of the UDF and remove its mapping.

     see SQLFunction.PerContextState#takeAggregateState
  */
  protected final T takeAggregateState(sqlite3_context cx){
    return map.takeAggregateState(cx);
  }
}







|









|





116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
     To be called from the implementation's xStep() method, as well
     as the xValue() and xInverse() methods of the {@link WindowFunction}
     subclass, to fetch the current per-call UDF state. On the
     first call to this method for any given sqlite3_context
     argument, the context is set to the given initial value. On all other
     calls, the 2nd argument is ignored.

     @see AggregateFunction.PerContextState#getAggregateState
  */
  protected final ValueHolder<T> getAggregateState(sqlite3_context cx, T initialValue){
    return map.getAggregateState(cx, initialValue);
  }

  /**
     To be called from the implementation's xFinal() method to fetch
     the final state of the UDF and remove its mapping.

     see AggregateFunction.PerContextState#takeAggregateState
  */
  protected final T takeAggregateState(sqlite3_context cx){
    return map.takeAggregateState(cx);
  }
}
Changes to ext/rtree/rtree.c.
60
61
62
63
64
65
66


67
68
69
70
71
72
73
  #include "sqlite3ext.h"
  SQLITE_EXTENSION_INIT1
#else
  #include "sqlite3.h"
#endif
int sqlite3GetToken(const unsigned char*,int*); /* In the SQLite core */



/*
** If building separately, we will need some setup that is normally
** found in sqliteInt.h
*/
#if !defined(SQLITE_AMALGAMATION)
#include "sqlite3rtree.h"
typedef sqlite3_int64 i64;







>
>







60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
  #include "sqlite3ext.h"
  SQLITE_EXTENSION_INIT1
#else
  #include "sqlite3.h"
#endif
int sqlite3GetToken(const unsigned char*,int*); /* In the SQLite core */

#include <stddef.h>

/*
** If building separately, we will need some setup that is normally
** found in sqliteInt.h
*/
#if !defined(SQLITE_AMALGAMATION)
#include "sqlite3rtree.h"
typedef sqlite3_int64 i64;
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# define ALWAYS(X)      ((X)?1:(assert(0),0))
# define NEVER(X)       ((X)?(assert(0),1):0)
#else
# define ALWAYS(X)      (X)
# define NEVER(X)       (X)
#endif
#ifndef offsetof
#define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
#endif
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
# define FLEXARRAY
#else
# define FLEXARRAY 1
#endif
#endif /* !defined(SQLITE_AMALGAMATION) */







|







93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# define ALWAYS(X)      ((X)?1:(assert(0),0))
# define NEVER(X)       ((X)?(assert(0),1):0)
#else
# define ALWAYS(X)      (X)
# define NEVER(X)       (X)
#endif
#ifndef offsetof
# define offsetof(ST,M) ((size_t)((char*)&((ST*)0)->M - (char*)0))
#endif
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
# define FLEXARRAY
#else
# define FLEXARRAY 1
#endif
#endif /* !defined(SQLITE_AMALGAMATION) */
Changes to ext/wasm/GNUmakefile.
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
#
# Note that the SQLITE_... build flags used here have NO EFFECT on the
# JS/WASM build. They are solely for use with $(bin.c-pp) itself.
#
# -D... flags which should be included in all invocations should be
# appended to $(SQLITE.CALL.C-PP.FILTER.global).
bin.c-pp := ./c-pp
$(bin.c-pp): c-pp.c $(sqlite3.c) $(MAKEFILE)
	$(CC) -O0 -o $@ c-pp.c $(sqlite3.c) '-DCMPP_DEFAULT_DELIM="//#"' -I$(dir.top) \
		-DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_UTF16 \
		-DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_WAL -DSQLITE_THREADSAFE=0 \
		-DSQLITE_TEMP_STORE=3
DISTCLEAN_FILES += $(bin.c-pp)
SQLITE.CALL.C-PP.FILTER.global ?=
ifeq (1,$(SQLITE_C_IS_SEE))







|







406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
#
# Note that the SQLITE_... build flags used here have NO EFFECT on the
# JS/WASM build. They are solely for use with $(bin.c-pp) itself.
#
# -D... flags which should be included in all invocations should be
# appended to $(SQLITE.CALL.C-PP.FILTER.global).
bin.c-pp := ./c-pp
$(bin.c-pp): c-pp.c $(sqlite3.c) # $(MAKEFILE)
	$(CC) -O0 -o $@ c-pp.c $(sqlite3.c) '-DCMPP_DEFAULT_DELIM="//#"' -I$(dir.top) \
		-DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_UTF16 \
		-DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_WAL -DSQLITE_THREADSAFE=0 \
		-DSQLITE_TEMP_STORE=3
DISTCLEAN_FILES += $(bin.c-pp)
SQLITE.CALL.C-PP.FILTER.global ?=
ifeq (1,$(SQLITE_C_IS_SEE))
592
593
594
595
596
597
598

599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619

620
621
622
623
624
625
626
########################################################################
# emcc flags for .c/.o/.wasm/.js.
emcc.flags :=
ifeq (1,$(emcc.verbose))
emcc.flags += -v
# -v is _very_ loud but also informative about what it's doing
endif


########################################################################
# emcc flags for .c/.o.
emcc.cflags :=
emcc.cflags += -std=c99 -fPIC
# -------------^^^^^^^^ we need c99 for $(sqlite3-wasm.c), primarily
# for variadic macros and snprintf() to implement
# sqlite3_wasm_enum_json().
emcc.cflags += -I. -I$(dir.top)
########################################################################
# emcc flags specific to building .js/.wasm files...
emcc.jsflags := -fPIC
emcc.jsflags += --no-entry
emcc.jsflags += -sWASM_BIGINT=$(emcc.WASM_BIGINT)
emcc.jsflags += -sMODULARIZE
emcc.jsflags += -sDYNAMIC_EXECUTION=0
emcc.jsflags += -sNO_POLYFILL
emcc.jsflags += -sEXPORTED_FUNCTIONS=@$(EXPORTED_FUNCTIONS.api)
emcc.exportedRuntimeMethods := \
    -sEXPORTED_RUNTIME_METHODS=wasmMemory
    # wasmMemory ==> required by our code for use with -sIMPORTED_MEMORY

emcc.jsflags += $(emcc.exportedRuntimeMethods)
emcc.jsflags += -sUSE_CLOSURE_COMPILER=0
emcc.jsflags += -sIMPORTED_MEMORY
ifeq (,$(filter -O0,$(emcc_opt)))
emcc.assert ?= 0
else
emcc.assert ?= 2







>



















|
|
>







592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
########################################################################
# emcc flags for .c/.o/.wasm/.js.
emcc.flags :=
ifeq (1,$(emcc.verbose))
emcc.flags += -v
# -v is _very_ loud but also informative about what it's doing
endif


########################################################################
# emcc flags for .c/.o.
emcc.cflags :=
emcc.cflags += -std=c99 -fPIC
# -------------^^^^^^^^ we need c99 for $(sqlite3-wasm.c), primarily
# for variadic macros and snprintf() to implement
# sqlite3_wasm_enum_json().
emcc.cflags += -I. -I$(dir.top)
########################################################################
# emcc flags specific to building .js/.wasm files...
emcc.jsflags := -fPIC
emcc.jsflags += --no-entry
emcc.jsflags += -sWASM_BIGINT=$(emcc.WASM_BIGINT)
emcc.jsflags += -sMODULARIZE
emcc.jsflags += -sDYNAMIC_EXECUTION=0
emcc.jsflags += -sNO_POLYFILL
emcc.jsflags += -sEXPORTED_FUNCTIONS=@$(EXPORTED_FUNCTIONS.api)
emcc.exportedRuntimeMethods := \
    -sEXPORTED_RUNTIME_METHODS=wasmMemory,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAP64,HEAPU64
# wasmMemory ==> required by our code for use with -sIMPORTED_MEMORY
# Emscripten 4.0.7 (2025-04-15) stops exporting HEAP* by default
emcc.jsflags += $(emcc.exportedRuntimeMethods)
emcc.jsflags += -sUSE_CLOSURE_COMPILER=0
emcc.jsflags += -sIMPORTED_MEMORY
ifeq (,$(filter -O0,$(emcc_opt)))
emcc.assert ?= 0
else
emcc.assert ?= 2
Changes to ext/wasm/api/sqlite3-wasm.c.
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
    typedef struct {
      int argvIndex;
      unsigned char omit;
    } sqlite3_index_constraint_usage;
    { /* Validate that the above struct sizeof()s match
      ** expectations. We could improve upon this by
      ** checking the offsetof() for each member. */
      const sqlite3_index_info siiCheck;
#define IndexSzCheck(T,M)           \
      (sizeof(T) == sizeof(*siiCheck.M))
      if(!IndexSzCheck(sqlite3_index_constraint,aConstraint)
         || !IndexSzCheck(sqlite3_index_orderby,aOrderBy)
         || !IndexSzCheck(sqlite3_index_constraint_usage,aConstraintUsage)){
        assert(!"sizeof mismatch in sqlite3_index_... struct(s)");
        return 0;







|







1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
    typedef struct {
      int argvIndex;
      unsigned char omit;
    } sqlite3_index_constraint_usage;
    { /* Validate that the above struct sizeof()s match
      ** expectations. We could improve upon this by
      ** checking the offsetof() for each member. */
      const sqlite3_index_info siiCheck = {0};
#define IndexSzCheck(T,M)           \
      (sizeof(T) == sizeof(*siiCheck.M))
      if(!IndexSzCheck(sqlite3_index_constraint,aConstraint)
         || !IndexSzCheck(sqlite3_index_orderby,aOrderBy)
         || !IndexSzCheck(sqlite3_index_constraint_usage,aConstraintUsage)){
        assert(!"sizeof mismatch in sqlite3_index_... struct(s)");
        return 0;
Changes to main.mk.
1832
1833
1834
1835
1836
1837
1838






1839
1840
1841
1842
1843
1844
1845
	$(TCLSH_CMD) $(TOP)/test/testrunner.tcl mdevtest $(TSTRNNR_OPTS)

mdevtest: srctree-check has_tclsh85
	$(TCLSH_CMD) $(TOP)/test/testrunner.tcl mdevtest $(TSTRNNR_OPTS)

sdevtest: has_tclsh85
	$(TCLSH_CMD) $(TOP)/test/testrunner.tcl sdevtest $(TSTRNNR_OPTS)







#
# Validate that various generated files in the source tree
# are up-to-date.
#
srctree-check:	$(TOP)/tool/srctree-check.tcl
	$(TCLSH_CMD) $(TOP)/tool/srctree-check.tcl







>
>
>
>
>
>







1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
	$(TCLSH_CMD) $(TOP)/test/testrunner.tcl mdevtest $(TSTRNNR_OPTS)

mdevtest: srctree-check has_tclsh85
	$(TCLSH_CMD) $(TOP)/test/testrunner.tcl mdevtest $(TSTRNNR_OPTS)

sdevtest: has_tclsh85
	$(TCLSH_CMD) $(TOP)/test/testrunner.tcl sdevtest $(TSTRNNR_OPTS)

# Like releasetest, except it omits srctree-check and verify-source so
# that it can be used on a modified source tree.
#
xdevtest: has_tclsh85
	$(TCLSH_CMD) $(TOP)/test/testrunner.tcl release $(TSTRNNR_OPTS)

#
# Validate that various generated files in the source tree
# are up-to-date.
#
srctree-check:	$(TOP)/tool/srctree-check.tcl
	$(TCLSH_CMD) $(TOP)/tool/srctree-check.tcl
Changes to src/btree.c.
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
  UnpackedRecord *pIdxKey;   /* Unpacked index key */

  if( pKey ){
    KeyInfo *pKeyInfo = pCur->pKeyInfo;
    assert( nKey==(i64)(int)nKey );
    pIdxKey = sqlite3VdbeAllocUnpackedRecord(pKeyInfo);
    if( pIdxKey==0 ) return SQLITE_NOMEM_BKPT;
    sqlite3VdbeRecordUnpack(pKeyInfo, (int)nKey, pKey, pIdxKey);
    if( pIdxKey->nField==0 || pIdxKey->nField>pKeyInfo->nAllField ){
      rc = SQLITE_CORRUPT_BKPT;
    }else{
      rc = sqlite3BtreeIndexMoveto(pCur, pIdxKey, pRes);
    }
    sqlite3DbFree(pCur->pKeyInfo->db, pIdxKey);
  }else{







|







1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
  UnpackedRecord *pIdxKey;   /* Unpacked index key */

  if( pKey ){
    KeyInfo *pKeyInfo = pCur->pKeyInfo;
    assert( nKey==(i64)(int)nKey );
    pIdxKey = sqlite3VdbeAllocUnpackedRecord(pKeyInfo);
    if( pIdxKey==0 ) return SQLITE_NOMEM_BKPT;
    sqlite3VdbeRecordUnpack((int)nKey, pKey, pIdxKey);
    if( pIdxKey->nField==0 || pIdxKey->nField>pKeyInfo->nAllField ){
      rc = SQLITE_CORRUPT_BKPT;
    }else{
      rc = sqlite3BtreeIndexMoveto(pCur, pIdxKey, pRes);
    }
    sqlite3DbFree(pCur->pKeyInfo->db, pIdxKey);
  }else{
3103
3104
3105
3106
3107
3108
3109

3110
3111
3112
3113
3114
3115
3116
      sqlite3_mutex_free(pBt->mutex);
    }
    removed = 1;
  }
  sqlite3_mutex_leave(pMainMtx);
  return removed;
#else

  return 1;
#endif
}

/*
** Make sure pBt->pTmpSpace points to an allocation of
** MX_CELL_SIZE(pBt) bytes with a 4-byte prefix for a left-child







>







3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
      sqlite3_mutex_free(pBt->mutex);
    }
    removed = 1;
  }
  sqlite3_mutex_leave(pMainMtx);
  return removed;
#else
  UNUSED_PARAMETER( pBt );
  return 1;
#endif
}

/*
** Make sure pBt->pTmpSpace points to an allocation of
** MX_CELL_SIZE(pBt) bytes with a 4-byte prefix for a left-child
3946
3947
3948
3949
3950
3951
3952







3953
3954
3955
3956
3957
3958
3959
      }
    }
 
    if( rc!=SQLITE_OK ){
      (void)sqlite3PagerWalWriteLock(pPager, 0);
      unlockBtreeIfUnused(pBt);
    }







  }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
          btreeInvokeBusyHandler(pBt) );
  sqlite3PagerWalDb(pPager, 0);
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY;
#endif








>
>
>
>
>
>
>







3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
      }
    }
 
    if( rc!=SQLITE_OK ){
      (void)sqlite3PagerWalWriteLock(pPager, 0);
      unlockBtreeIfUnused(pBt);
    }
#if defined(SQLITE_ENABLE_SETLK_TIMEOUT)
    if( rc==SQLITE_BUSY_TIMEOUT ){
      /* If a blocking lock timed out, break out of the loop here so that
      ** the busy-handler is not invoked.  */
      break;
    }
#endif
  }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
          btreeInvokeBusyHandler(pBt) );
  sqlite3PagerWalDb(pPager, 0);
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY;
#endif

11834
11835
11836
11837
11838
11839
11840

11841
11842
11843
11844
11845
11846
11847
/*
** Return SQLITE_LOCKED_SHAREDCACHE if another user of the same shared
** btree as the argument handle holds an exclusive lock on the
** sqlite_schema table. Otherwise SQLITE_OK.
*/
int sqlite3BtreeSchemaLocked(Btree *p){
  int rc;

  assert( sqlite3_mutex_held(p->db->mutex) );
  sqlite3BtreeEnter(p);
  rc = querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK);
  assert( rc==SQLITE_OK || rc==SQLITE_LOCKED_SHAREDCACHE );
  sqlite3BtreeLeave(p);
  return rc;
}







>







11842
11843
11844
11845
11846
11847
11848
11849
11850
11851
11852
11853
11854
11855
11856
/*
** Return SQLITE_LOCKED_SHAREDCACHE if another user of the same shared
** btree as the argument handle holds an exclusive lock on the
** sqlite_schema table. Otherwise SQLITE_OK.
*/
int sqlite3BtreeSchemaLocked(Btree *p){
  int rc;
  UNUSED_PARAMETER(p);  /* only used in DEBUG builds */
  assert( sqlite3_mutex_held(p->db->mutex) );
  sqlite3BtreeEnter(p);
  rc = querySharedCacheTableLock(p, SCHEMA_ROOT, READ_LOCK);
  assert( rc==SQLITE_OK || rc==SQLITE_LOCKED_SHAREDCACHE );
  sqlite3BtreeLeave(p);
  return rc;
}
Changes to src/dbpage.c.
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240

241
242
243
244
245
246
247
248
249
250
**     1     schema=main, pgno=?1
**     2     schema=?1, full table scan
**     3     schema=?1, pgno=?2
**
** idxStr is not used
*/
static int dbpageFilter(
  sqlite3_vtab_cursor *pCursor, 
  int idxNum, const char *idxStr,
  int argc, sqlite3_value **argv
){
  DbpageCursor *pCsr = (DbpageCursor *)pCursor;
  DbpageTable *pTab = (DbpageTable *)pCursor->pVtab;
  int rc;
  sqlite3 *db = pTab->db;
  Btree *pBt;

  (void)idxStr;

  
  /* Default setting is no rows of result */
  pCsr->pgno = 1; 
  pCsr->mxPgno = 0;

  if( idxNum & 2 ){
    const char *zSchema;
    assert( argc>=1 );
    zSchema = (const char*)sqlite3_value_text(argv[0]);
    pCsr->iDb = sqlite3FindDbName(db, zSchema);







|









|
>
|

|







223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
**     1     schema=main, pgno=?1
**     2     schema=?1, full table scan
**     3     schema=?1, pgno=?2
**
** idxStr is not used
*/
static int dbpageFilter(
  sqlite3_vtab_cursor *pCursor,
  int idxNum, const char *idxStr,
  int argc, sqlite3_value **argv
){
  DbpageCursor *pCsr = (DbpageCursor *)pCursor;
  DbpageTable *pTab = (DbpageTable *)pCursor->pVtab;
  int rc;
  sqlite3 *db = pTab->db;
  Btree *pBt;

  UNUSED_PARAMETER(idxStr);
  UNUSED_PARAMETER(argc);

  /* Default setting is no rows of result */
  pCsr->pgno = 1;
  pCsr->mxPgno = 0;

  if( idxNum & 2 ){
    const char *zSchema;
    assert( argc>=1 );
    zSchema = (const char*)sqlite3_value_text(argv[0]);
    pCsr->iDb = sqlite3FindDbName(db, zSchema);
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
  }
  if( pCsr->pPage1 ) sqlite3PagerUnrefPageOne(pCsr->pPage1);
  rc = sqlite3PagerGet(pCsr->pPager, 1, &pCsr->pPage1, 0);
  return rc;
}

static int dbpageColumn(
  sqlite3_vtab_cursor *pCursor, 
  sqlite3_context *ctx, 
  int i
){
  DbpageCursor *pCsr = (DbpageCursor *)pCursor;
  int rc = SQLITE_OK;
  switch( i ){
    case 0: {           /* pgno */
      sqlite3_result_int(ctx, pCsr->pgno);







|
|







272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
  }
  if( pCsr->pPage1 ) sqlite3PagerUnrefPageOne(pCsr->pPage1);
  rc = sqlite3PagerGet(pCsr->pPager, 1, &pCsr->pPage1, 0);
  return rc;
}

static int dbpageColumn(
  sqlite3_vtab_cursor *pCursor,
  sqlite3_context *ctx,
  int i
){
  DbpageCursor *pCsr = (DbpageCursor *)pCursor;
  int rc = SQLITE_OK;
  switch( i ){
    case 0: {           /* pgno */
      sqlite3_result_int(ctx, pCsr->pgno);
Changes to src/expr.c.
69
70
71
72
73
74
75
76


77
78
79
80
81
82
83
      assert( pExpr->iColumn < pExpr->iTable );
      assert( pExpr->iColumn >= 0 );
      assert( pExpr->iTable==pExpr->pLeft->x.pSelect->pEList->nExpr );
      return sqlite3ExprAffinity(
          pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr
      );
    }
    if( op==TK_VECTOR ){


      assert( ExprUseXList(pExpr) );
      return sqlite3ExprAffinity(pExpr->x.pList->a[0].pExpr);
    }
    if( ExprHasProperty(pExpr, EP_Skip|EP_IfNullRow) ){
      assert( pExpr->op==TK_COLLATE
           || pExpr->op==TK_IF_NULL_ROW
           || (pExpr->op==TK_REGISTER && pExpr->op2==TK_IF_NULL_ROW) );







|
>
>







69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
      assert( pExpr->iColumn < pExpr->iTable );
      assert( pExpr->iColumn >= 0 );
      assert( pExpr->iTable==pExpr->pLeft->x.pSelect->pEList->nExpr );
      return sqlite3ExprAffinity(
          pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr
      );
    }
    if( op==TK_VECTOR
     || (op==TK_FUNCTION && pExpr->affExpr==SQLITE_AFF_DEFER)
    ){
      assert( ExprUseXList(pExpr) );
      return sqlite3ExprAffinity(pExpr->x.pList->a[0].pExpr);
    }
    if( ExprHasProperty(pExpr, EP_Skip|EP_IfNullRow) ){
      assert( pExpr->op==TK_COLLATE
           || pExpr->op==TK_IF_NULL_ROW
           || (pExpr->op==TK_REGISTER && pExpr->op2==TK_IF_NULL_ROW) );
262
263
264
265
266
267
268
269


270
271
272
273
274
275
276
      }
      break;
    }
    if( op==TK_CAST || op==TK_UPLUS ){
      p = p->pLeft;
      continue;
    }
    if( op==TK_VECTOR ){


      assert( ExprUseXList(p) );
      p = p->x.pList->a[0].pExpr;
      continue;
    }
    if( op==TK_COLLATE ){
      assert( !ExprHasProperty(p, EP_IntValue) );
      pColl = sqlite3GetCollSeq(pParse, ENC(db), 0, p->u.zToken);







|
>
>







264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
      }
      break;
    }
    if( op==TK_CAST || op==TK_UPLUS ){
      p = p->pLeft;
      continue;
    }
    if( op==TK_VECTOR
     || (op==TK_FUNCTION && p->affExpr==SQLITE_AFF_DEFER)
    ){
      assert( ExprUseXList(p) );
      p = p->x.pList->a[0].pExpr;
      continue;
    }
    if( op==TK_COLLATE ){
      assert( !ExprHasProperty(p, EP_IntValue) );
      pColl = sqlite3GetCollSeq(pParse, ENC(db), 0, p->u.zToken);
Changes to src/json.c.
1281
1282
1283
1284
1285
1286
1287


1288
1289
1290
1291
1292
1293
1294
1295
1296
  szType = a[0]>>4;
  if( szType<=11 ){
    nExtra = 0;
  }else if( szType==12 ){
    nExtra = 1;
  }else if( szType==13 ){
    nExtra = 2;


  }else{
    nExtra = 4;
  }
  if( szPayload<=11 ){
    nNeeded = 0;
  }else if( szPayload<=0xff ){
    nNeeded = 1;
  }else if( szPayload<=0xffff ){
    nNeeded = 2;







>
>

|







1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
  szType = a[0]>>4;
  if( szType<=11 ){
    nExtra = 0;
  }else if( szType==12 ){
    nExtra = 1;
  }else if( szType==13 ){
    nExtra = 2;
  }else if( szType==14 ){
    nExtra = 4;
  }else{
    nExtra = 8;
  }
  if( szPayload<=11 ){
    nNeeded = 0;
  }else if( szPayload<=0xff ){
    nNeeded = 1;
  }else if( szPayload<=0xffff ){
    nNeeded = 2;
Changes to src/os_unix.c.
5185
5186
5187
5188
5189
5190
5191

5192
5193
5194
5195
5196
5197
5198
5199
5200
5201
5202
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
    return unixMutexFreeShmlock(pDbFd, ofst, n, flags);
  }


  /* Check that, if this to be a blocking lock, no locks that occur later
  ** in the following list than the lock being obtained are already held:
  **

  **   1. Checkpointer lock (ofst==1).
  **   2. Write lock (ofst==0).
  **   3. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK).
  **
  ** In other words, if this is a blocking lock, none of the locks that
  ** occur later in the above list than the lock being obtained may be
  ** held.
  **
  ** It is not permitted to block on the RECOVER lock.
  */
#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && defined(SQLITE_DEBUG)
  {
    u16 lockMask = (p->exclMask|p->sharedMask);
    assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
          (ofst!=2)                                   /* not RECOVER */
       && (ofst!=1 || lockMask==0 || lockMask==2)
       && (ofst!=0 || lockMask<3)
       && (ofst<3  || lockMask<(1<<ofst))
    ));
  }
#endif








>
|
|
|




<
<





|







5185
5186
5187
5188
5189
5190
5191
5192
5193
5194
5195
5196
5197
5198
5199


5200
5201
5202
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
    return unixMutexFreeShmlock(pDbFd, ofst, n, flags);
  }


  /* Check that, if this to be a blocking lock, no locks that occur later
  ** in the following list than the lock being obtained are already held:
  **
  **   1. Recovery lock (ofst==2).
  **   2. Checkpointer lock (ofst==1).
  **   3. Write lock (ofst==0).
  **   4. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK).
  **
  ** In other words, if this is a blocking lock, none of the locks that
  ** occur later in the above list than the lock being obtained may be
  ** held.


  */
#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && defined(SQLITE_DEBUG)
  {
    u16 lockMask = (p->exclMask|p->sharedMask);
    assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
          (ofst!=2 || lockMask==0)
       && (ofst!=1 || lockMask==0 || lockMask==2)
       && (ofst!=0 || lockMask<3)
       && (ofst<3  || lockMask<(1<<ofst))
    ));
  }
#endif

Changes to src/os_win.c.
2718
2719
2720
2721
2722
2723
2724

2725



2726
2727
2728
2729
2730
2731
2732
    */
    if( !ret && GetLastError()==ERROR_IO_PENDING ){
      DWORD nDelay = (nMs==0 ? INFINITE : nMs);
      DWORD res = osWaitForSingleObject(ovlp.hEvent, nDelay);
      if( res==WAIT_OBJECT_0 ){
        ret = TRUE;
      }else if( res==WAIT_TIMEOUT ){

        rc = SQLITE_BUSY_TIMEOUT;



      }else{
        /* Some other error has occurred */
        rc = SQLITE_IOERR_LOCK;
      }

      /* If it is still pending, cancel the LockFileEx() call. */
      osCancelIo(hFile);







>

>
>
>







2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
    */
    if( !ret && GetLastError()==ERROR_IO_PENDING ){
      DWORD nDelay = (nMs==0 ? INFINITE : nMs);
      DWORD res = osWaitForSingleObject(ovlp.hEvent, nDelay);
      if( res==WAIT_OBJECT_0 ){
        ret = TRUE;
      }else if( res==WAIT_TIMEOUT ){
#if SQLITE_ENABLE_SETLK_TIMEOUT==1
        rc = SQLITE_BUSY_TIMEOUT;
#else
        rc = SQLITE_BUSY;
#endif
      }else{
        /* Some other error has occurred */
        rc = SQLITE_IOERR_LOCK;
      }

      /* If it is still pending, cancel the LockFileEx() call. */
      osCancelIo(hFile);
4529
4530
4531
4532
4533
4534
4535

4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
       || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
       || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
  assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );

  /* Check that, if this to be a blocking lock, no locks that occur later
  ** in the following list than the lock being obtained are already held:
  **

  **   1. Checkpointer lock (ofst==1).
  **   2. Write lock (ofst==0).
  **   3. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK).
  **
  ** In other words, if this is a blocking lock, none of the locks that
  ** occur later in the above list than the lock being obtained may be
  ** held.
  **
  ** It is not permitted to block on the RECOVER lock.
  */
#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && defined(SQLITE_DEBUG)
  {
    u16 lockMask = (p->exclMask|p->sharedMask);
    assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
          (ofst!=2)                                   /* not RECOVER */
       && (ofst!=1 || lockMask==0 || lockMask==2)
       && (ofst!=0 || lockMask<3)
       && (ofst<3  || lockMask<(1<<ofst))
    ));
  }
#endif








>
|
|
|




<
<





|







4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547


4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
       || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
       || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
  assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );

  /* Check that, if this to be a blocking lock, no locks that occur later
  ** in the following list than the lock being obtained are already held:
  **
  **   1. Recovery lock (ofst==2).
  **   2. Checkpointer lock (ofst==1).
  **   3. Write lock (ofst==0).
  **   4. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK).
  **
  ** In other words, if this is a blocking lock, none of the locks that
  ** occur later in the above list than the lock being obtained may be
  ** held.


  */
#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && defined(SQLITE_DEBUG)
  {
    u16 lockMask = (p->exclMask|p->sharedMask);
    assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
          (ofst!=2 || lockMask==0)
       && (ofst!=1 || lockMask==0 || lockMask==2)
       && (ofst!=0 || lockMask<3)
       && (ofst<3  || lockMask<(1<<ofst))
    ));
  }
#endif

Changes to src/pager.c.
699
700
701
702
703
704
705



706
707
708
709
710
711
712
  int (*xGet)(Pager*,Pgno,DbPage**,int); /* Routine to fetch a patch */
  char *pTmpSpace;            /* Pager.pageSize bytes of space for tmp use */
  PCache *pPCache;            /* Pointer to page cache object */
#ifndef SQLITE_OMIT_WAL
  Wal *pWal;                  /* Write-ahead log used by "journal_mode=wal" */
  char *zWal;                 /* File name for write-ahead log */
#endif



};

/*
** Indexes for use with Pager.aStat[]. The Pager.aStat[] array contains
** the values accessed by passing SQLITE_DBSTATUS_CACHE_HIT, CACHE_MISS
** or CACHE_WRITE to sqlite3_db_status().
*/







>
>
>







699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
  int (*xGet)(Pager*,Pgno,DbPage**,int); /* Routine to fetch a patch */
  char *pTmpSpace;            /* Pager.pageSize bytes of space for tmp use */
  PCache *pPCache;            /* Pointer to page cache object */
#ifndef SQLITE_OMIT_WAL
  Wal *pWal;                  /* Write-ahead log used by "journal_mode=wal" */
  char *zWal;                 /* File name for write-ahead log */
#endif
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  sqlite3 *dbWal;
#endif
};

/*
** Indexes for use with Pager.aStat[]. The Pager.aStat[] array contains
** the values accessed by passing SQLITE_DBSTATUS_CACHE_HIT, CACHE_MISS
** or CACHE_WRITE to sqlite3_db_status().
*/
7766
7767
7768
7769
7770
7771
7772





7773
7774
7775
7776
7777
7778
7779
  ** (e.g. due to malloc() failure), return an error code.
  */
  if( rc==SQLITE_OK ){
    rc = sqlite3WalOpen(pPager->pVfs,
        pPager->fd, pPager->zWal, pPager->exclusiveMode,
        pPager->journalSizeLimit, bWal2, &pPager->pWal
    );





  }
  pagerFixMaplimit(pPager);

  return rc;
}









>
>
>
>
>







7769
7770
7771
7772
7773
7774
7775
7776
7777
7778
7779
7780
7781
7782
7783
7784
7785
7786
7787
  ** (e.g. due to malloc() failure), return an error code.
  */
  if( rc==SQLITE_OK ){
    rc = sqlite3WalOpen(pPager->pVfs,
        pPager->fd, pPager->zWal, pPager->exclusiveMode,
        pPager->journalSizeLimit, bWal2, &pPager->pWal
    );
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
    if( rc==SQLITE_OK ){
      sqlite3WalDb(pPager->pWal, pPager->dbWal);
    }
#endif
  }
  pagerFixMaplimit(pPager);

  return rc;
}


7888
7889
7890
7891
7892
7893
7894

7895
7896
7897
7898
7899
7900
7901
}

/*
** Set the database handle used by the wal layer to determine if
** blocking locks are required.
*/
void sqlite3PagerWalDb(Pager *pPager, sqlite3 *db){

  if( pagerUseWal(pPager) ){
    sqlite3WalDb(pPager->pWal, db);
  }
}
#endif

#ifdef SQLITE_ENABLE_SNAPSHOT







>







7896
7897
7898
7899
7900
7901
7902
7903
7904
7905
7906
7907
7908
7909
7910
}

/*
** Set the database handle used by the wal layer to determine if
** blocking locks are required.
*/
void sqlite3PagerWalDb(Pager *pPager, sqlite3 *db){
  pPager->dbWal = db;
  if( pagerUseWal(pPager) ){
    sqlite3WalDb(pPager->pWal, db);
  }
}
#endif

#ifdef SQLITE_ENABLE_SNAPSHOT
Changes to src/select.c.
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614


615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630


631




632
633
634
635
636
637
638
        ){
          sqlite3ErrorMsg(pParse, "cannot join using column %s - column "
            "not present in both tables", zName);
          return 1;
        }
        pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iLeftCol);
        sqlite3SrcItemColumnUsed(&pSrc->a[iLeft], iLeftCol);
        if( (pSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){
          /* This branch runs if the query contains one or more RIGHT or FULL
          ** JOINs.  If only a single table on the left side of this join
          ** contains the zName column, then this branch is a no-op.
          ** But if there are two or more tables on the left side
          ** of the join, construct a coalesce() function that gathers all
          ** such tables.  Raise an error if more than one of those references
          ** to zName is not also within a prior USING clause.
          **
          ** We really ought to raise an error if there are two or more
          ** non-USING references to zName on the left of an INNER or LEFT
          ** JOIN.  But older versions of SQLite do not do that, so we avoid
          ** adding a new error so as to not break legacy applications.
          */
          ExprList *pFuncArgs = 0;   /* Arguments to the coalesce() */
          static const Token tkCoalesce = { "coalesce", 8 };


          while( tableAndColumnIndex(pSrc, iLeft+1, i, zName, &iLeft, &iLeftCol,
                                     pRight->fg.isSynthUsing)!=0 ){
            if( pSrc->a[iLeft].fg.isUsing==0
             || sqlite3IdListIndex(pSrc->a[iLeft].u3.pUsing, zName)<0
            ){
              sqlite3ErrorMsg(pParse, "ambiguous reference to %s in USING()",
                              zName);
              break;
            }
            pFuncArgs = sqlite3ExprListAppend(pParse, pFuncArgs, pE1);
            pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iLeftCol);
            sqlite3SrcItemColumnUsed(&pSrc->a[iLeft], iLeftCol);
          }
          if( pFuncArgs ){
            pFuncArgs = sqlite3ExprListAppend(pParse, pFuncArgs, pE1);
            pE1 = sqlite3ExprFunction(pParse, pFuncArgs, &tkCoalesce, 0);


          }




        }
        pE2 = sqlite3CreateColumnExpr(db, pSrc, i+1, iRightCol);
        sqlite3SrcItemColumnUsed(pRight, iRightCol);
        pEq = sqlite3PExpr(pParse, TK_EQ, pE1, pE2);
        assert( pE2!=0 || pEq==0 );
        if( pEq ){
          ExprSetProperty(pEq, joinType);







|















>
>
















>
>
|
>
>
>
>







592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
        ){
          sqlite3ErrorMsg(pParse, "cannot join using column %s - column "
            "not present in both tables", zName);
          return 1;
        }
        pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iLeftCol);
        sqlite3SrcItemColumnUsed(&pSrc->a[iLeft], iLeftCol);
        if( (pSrc->a[0].fg.jointype & JT_LTORJ)!=0 && pParse->nErr==0 ){
          /* This branch runs if the query contains one or more RIGHT or FULL
          ** JOINs.  If only a single table on the left side of this join
          ** contains the zName column, then this branch is a no-op.
          ** But if there are two or more tables on the left side
          ** of the join, construct a coalesce() function that gathers all
          ** such tables.  Raise an error if more than one of those references
          ** to zName is not also within a prior USING clause.
          **
          ** We really ought to raise an error if there are two or more
          ** non-USING references to zName on the left of an INNER or LEFT
          ** JOIN.  But older versions of SQLite do not do that, so we avoid
          ** adding a new error so as to not break legacy applications.
          */
          ExprList *pFuncArgs = 0;   /* Arguments to the coalesce() */
          static const Token tkCoalesce = { "coalesce", 8 };
          assert( pE1!=0 );
          ExprSetProperty(pE1, EP_CanBeNull);
          while( tableAndColumnIndex(pSrc, iLeft+1, i, zName, &iLeft, &iLeftCol,
                                     pRight->fg.isSynthUsing)!=0 ){
            if( pSrc->a[iLeft].fg.isUsing==0
             || sqlite3IdListIndex(pSrc->a[iLeft].u3.pUsing, zName)<0
            ){
              sqlite3ErrorMsg(pParse, "ambiguous reference to %s in USING()",
                              zName);
              break;
            }
            pFuncArgs = sqlite3ExprListAppend(pParse, pFuncArgs, pE1);
            pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iLeftCol);
            sqlite3SrcItemColumnUsed(&pSrc->a[iLeft], iLeftCol);
          }
          if( pFuncArgs ){
            pFuncArgs = sqlite3ExprListAppend(pParse, pFuncArgs, pE1);
            pE1 = sqlite3ExprFunction(pParse, pFuncArgs, &tkCoalesce, 0);
            if( pE1 ){
              pE1->affExpr = SQLITE_AFF_DEFER;
            }
          }
        }else if( (pSrc->a[i+1].fg.jointype & JT_LEFT)!=0 && pParse->nErr==0 ){
          assert( pE1!=0 );
          ExprSetProperty(pE1, EP_CanBeNull);
        }
        pE2 = sqlite3CreateColumnExpr(db, pSrc, i+1, iRightCol);
        sqlite3SrcItemColumnUsed(pRight, iRightCol);
        pEq = sqlite3PExpr(pParse, TK_EQ, pE1, pE2);
        assert( pE2!=0 || pEq==0 );
        if( pEq ){
          ExprSetProperty(pEq, joinType);
2101
2102
2103
2104
2105
2106
2107




2108
2109
2110
2111
2112
2113
2114
    sqlite3VdbeSetColName(v, i, COLNAME_TABLE, zOrigTab, SQLITE_TRANSIENT);
    sqlite3VdbeSetColName(v, i, COLNAME_COLUMN, zOrigCol, SQLITE_TRANSIENT);
#else
    zType = columnType(&sNC, p, 0, 0, 0);
#endif
    sqlite3VdbeSetColName(v, i, COLNAME_DECLTYPE, zType, SQLITE_TRANSIENT);
  }




#endif /* !defined(SQLITE_OMIT_DECLTYPE) */
}


/*
** Compute the column names for a SELECT statement.
**







>
>
>
>







2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
    sqlite3VdbeSetColName(v, i, COLNAME_TABLE, zOrigTab, SQLITE_TRANSIENT);
    sqlite3VdbeSetColName(v, i, COLNAME_COLUMN, zOrigCol, SQLITE_TRANSIENT);
#else
    zType = columnType(&sNC, p, 0, 0, 0);
#endif
    sqlite3VdbeSetColName(v, i, COLNAME_DECLTYPE, zType, SQLITE_TRANSIENT);
  }
#else
  UNUSED_PARAMETER(pParse);
  UNUSED_PARAMETER(pTabList);
  UNUSED_PARAMETER(pEList);
#endif /* !defined(SQLITE_OMIT_DECLTYPE) */
}


/*
** Compute the column names for a SELECT statement.
**
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
**        (2a) the outer query must not be a join and
**        (2b) the outer query must not use subqueries
**             other than the one FROM-clause subquery that is a candidate
**             for flattening.  (This is due to ticket [2f7170d73bf9abf80]
**             from 2015-02-09.)
**
**   (3)  If the subquery is the right operand of a LEFT JOIN then
**        (3a) the subquery may not be a join and
**        (3b) the FROM clause of the subquery may not contain a virtual
**             table and
**        (**) Was: "The outer query may not have a GROUP BY." This case
**             is now managed correctly
**        (3d) the outer query may not be DISTINCT.
**        See also (26) for restrictions on RIGHT JOIN.
**
**   (4)  The subquery can not be DISTINCT.
**







|
|
|







4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
**        (2a) the outer query must not be a join and
**        (2b) the outer query must not use subqueries
**             other than the one FROM-clause subquery that is a candidate
**             for flattening.  (This is due to ticket [2f7170d73bf9abf80]
**             from 2015-02-09.)
**
**   (3)  If the subquery is the right operand of a LEFT JOIN then
**        (3a) the subquery may not be a join
**        (**) Was (3b): "the FROM clause of the subquery may not contain
**             a virtual table"
**        (**) Was: "The outer query may not have a GROUP BY." This case
**             is now managed correctly
**        (3d) the outer query may not be DISTINCT.
**        See also (26) for restrictions on RIGHT JOIN.
**
**   (4)  The subquery can not be DISTINCT.
**
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
  **
  ** which is not at all the same thing.
  **
  ** See also tickets #306, #350, and #3300.
  */
  if( (pSubitem->fg.jointype & (JT_OUTER|JT_LTORJ))!=0 ){
    if( pSubSrc->nSrc>1                        /* (3a) */
     || IsVirtual(pSubSrc->a[0].pSTab)         /* (3b) */
     || (p->selFlags & SF_Distinct)!=0         /* (3d) */
     || (pSubitem->fg.jointype & JT_RIGHT)!=0  /* (26) */
    ){
      return 0;
    }
    isOuterJoin = 1;
  }







|







4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
  **
  ** which is not at all the same thing.
  **
  ** See also tickets #306, #350, and #3300.
  */
  if( (pSubitem->fg.jointype & (JT_OUTER|JT_LTORJ))!=0 ){
    if( pSubSrc->nSrc>1                        /* (3a) */
     /**** || IsVirtual(pSubSrc->a[0].pSTab)      (3b)-omitted */
     || (p->selFlags & SF_Distinct)!=0         /* (3d) */
     || (pSubitem->fg.jointype & JT_RIGHT)!=0  /* (26) */
    ){
      return 0;
    }
    isOuterJoin = 1;
  }
Changes to src/shell.c.in.
883
884
885
886
887
888
889
890

891
892
893
894
895








896
897
898
899
900
901
902
  const char *z2 = z;
  while( *z2 ){ z2++; }
  return 0x3fffffff & (int)(z2 - z);
}

/*
** Return the length of a string in characters.  Multibyte UTF8 characters
** count as a single character.

*/
static int strlenChar(const char *z){
  int n = 0;
  while( *z ){
    if( (0xc0&*(z++))!=0x80 ) n++;








  }
  return n;
}

/*
** Return open FILE * if zFile exists, can be opened for read
** and is an ordinary file or a character stream source.







|
>




|
>
>
>
>
>
>
>
>







883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
  const char *z2 = z;
  while( *z2 ){ z2++; }
  return 0x3fffffff & (int)(z2 - z);
}

/*
** Return the length of a string in characters.  Multibyte UTF8 characters
** count as a single character for single-width characters, or as two
** characters for double-width characters.
*/
static int strlenChar(const char *z){
  int n = 0;
  while( *z ){
    if( (0x80&z[0])==0 ){
      n++;
      z++;
    }else{
      int u = 0;
      int len = decodeUtf8((const u8*)z, &u);
      z += len;
      n += cli_wcwidth(u);
    }
  }
  return n;
}

/*
** Return open FILE * if zFile exists, can be opened for read
** and is an ordinary file or a character stream source.
Changes to src/sqliteInt.h.
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
#endif

/*
** GCC does not define the offsetof() macro so we'll have to do it
** ourselves.
*/
#ifndef offsetof
#define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
#endif

/*
** Work around C99 "flex-array" syntax for pre-C99 compilers, so as
** to avoid complaints from -fsanitize=strict-bounds.
*/
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)







|







761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
#endif

/*
** GCC does not define the offsetof() macro so we'll have to do it
** ourselves.
*/
#ifndef offsetof
# define offsetof(ST,M) ((size_t)((char*)&((ST*)0)->M - (char*)0))
#endif

/*
** Work around C99 "flex-array" syntax for pre-C99 compilers, so as
** to avoid complaints from -fsanitize=strict-bounds.
*/
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
2353
2354
2355
2356
2357
2358
2359

2360
2361
2362
2363
2364
2365
2366
#define SQLITE_AFF_NONE     0x40  /* '@' */
#define SQLITE_AFF_BLOB     0x41  /* 'A' */
#define SQLITE_AFF_TEXT     0x42  /* 'B' */
#define SQLITE_AFF_NUMERIC  0x43  /* 'C' */
#define SQLITE_AFF_INTEGER  0x44  /* 'D' */
#define SQLITE_AFF_REAL     0x45  /* 'E' */
#define SQLITE_AFF_FLEXNUM  0x46  /* 'F' */


#define sqlite3IsNumericAffinity(X)  ((X)>=SQLITE_AFF_NUMERIC)

/*
** The SQLITE_AFF_MASK values masks off the significant bits of an
** affinity value.
*/







>







2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
#define SQLITE_AFF_NONE     0x40  /* '@' */
#define SQLITE_AFF_BLOB     0x41  /* 'A' */
#define SQLITE_AFF_TEXT     0x42  /* 'B' */
#define SQLITE_AFF_NUMERIC  0x43  /* 'C' */
#define SQLITE_AFF_INTEGER  0x44  /* 'D' */
#define SQLITE_AFF_REAL     0x45  /* 'E' */
#define SQLITE_AFF_FLEXNUM  0x46  /* 'F' */
#define SQLITE_AFF_DEFER    0x58  /* 'X'  - defer computation until later */

#define sqlite3IsNumericAffinity(X)  ((X)>=SQLITE_AFF_NUMERIC)

/*
** The SQLITE_AFF_MASK values masks off the significant bits of an
** affinity value.
*/
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677






2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689


2690







2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742


/*
** An instance of the following structure is passed as the first
** argument to sqlite3VdbeKeyCompare and is used to control the
** comparison of the two index keys.
**
** Note that aSortOrder[] and aColl[] have nField+1 slots.  There
** are nField slots for the columns of an index then one extra slot
** for the rowid at the end.






*/
struct KeyInfo {
  u32 nRef;           /* Number of references to this KeyInfo object */
  u8 enc;             /* Text encoding - one of the SQLITE_UTF* values */
  u16 nKeyField;      /* Number of key columns in the index */
  u16 nAllField;      /* Total columns, including key plus others */
  sqlite3 *db;        /* The database connection */
  u8 *aSortFlags;     /* Sort order for each column. */
  CollSeq *aColl[FLEXARRAY]; /* Collating sequence for each term of the key */
};

/* The size (in bytes) of a KeyInfo object with up to N fields */


#define SZ_KEYINFO(N)  (offsetof(KeyInfo,aColl) + (N)*sizeof(CollSeq*))








/*
** Allowed bit values for entries in the KeyInfo.aSortFlags[] array.
*/
#define KEYINFO_ORDER_DESC    0x01    /* DESC sort order */
#define KEYINFO_ORDER_BIGNULL 0x02    /* NULL is larger than any other value */

/*
** This object holds a record which has been parsed out into individual
** fields, for the purposes of doing a comparison.
**
** A record is an object that contains one or more fields of data.
** Records are used to store the content of a table row and to store
** the key of an index.  A blob encoding of a record is created by
** the OP_MakeRecord opcode of the VDBE and is disassembled by the
** OP_Column opcode.
**
** An instance of this object serves as a "key" for doing a search on
** an index b+tree. The goal of the search is to find the entry that
** is closed to the key described by this object.  This object might hold
** just a prefix of the key.  The number of fields is given by
** pKeyInfo->nField.
**
** The r1 and r2 fields are the values to return if this key is less than
** or greater than a key in the btree, respectively.  These are normally
** -1 and +1 respectively, but might be inverted to +1 and -1 if the b-tree
** is in DESC order.
**
** The key comparison functions actually return default_rc when they find
** an equals comparison.  default_rc can be -1, 0, or +1.  If there are
** multiple entries in the b-tree with the same key (when only looking
** at the first pKeyInfo->nFields,) then default_rc can be set to -1 to
** cause the search to find the last match, or +1 to cause the search to
** find the first match.
**
** The key comparison functions will set eqSeen to true if they ever
** get and equal results when comparing this structure to a b-tree record.
** When default_rc!=0, the search might end up on the record immediately
** before the first match or immediately after the last match.  The
** eqSeen field will indicate whether or not an exact match exists in the
** b-tree.
*/
struct UnpackedRecord {
  KeyInfo *pKeyInfo;  /* Collation and sort-order information */
  Mem *aMem;          /* Values */
  union {
    char *z;            /* Cache of aMem[0].z for vdbeRecordCompareString() */
    i64 i;              /* Cache of aMem[0].u.i for vdbeRecordCompareInt() */
  } u;
  int n;              /* Cache of aMem[0].n used by vdbeRecordCompareString() */
  u16 nField;         /* Number of entries in apMem[] */
  i8 default_rc;      /* Comparison result if keys are equal */







|
|
|
>
>
>
>
>
>











|
>
>

>
>
>
>
>
>
>



















|
|
<









|











|
|







2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727

2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757


/*
** An instance of the following structure is passed as the first
** argument to sqlite3VdbeKeyCompare and is used to control the
** comparison of the two index keys.
**
** The aSortOrder[] and aColl[] arrays have nAllField slots each. There
** are nKeyField slots for the columns of an index then extra slots
** for the rowid or key at the end.  The aSortOrder array is located after
** the aColl[] array.
**
** If SQLITE_ENABLE_PREUPDATE_HOOK is defined, then aSortFlags might be NULL
** to indicate that this object is for use by a preupdate hook.  When aSortFlags
** is NULL, then nAllField is uninitialized and no space is allocated for
** aColl[], so those fields may not be used.
*/
struct KeyInfo {
  u32 nRef;           /* Number of references to this KeyInfo object */
  u8 enc;             /* Text encoding - one of the SQLITE_UTF* values */
  u16 nKeyField;      /* Number of key columns in the index */
  u16 nAllField;      /* Total columns, including key plus others */
  sqlite3 *db;        /* The database connection */
  u8 *aSortFlags;     /* Sort order for each column. */
  CollSeq *aColl[FLEXARRAY]; /* Collating sequence for each term of the key */
};

/* The size (in bytes) of a KeyInfo object with up to N fields.  This includes
** the main body of the KeyInfo object and the aColl[] array of N elements,
** but does not count the memory used to hold aSortFlags[]. */
#define SZ_KEYINFO(N)  (offsetof(KeyInfo,aColl) + (N)*sizeof(CollSeq*))

/* The size of a bare KeyInfo with no aColl[] entries */
#if FLEXARRAY+1 > 1
# define SZ_KEYINFO_0   offsetof(KeyInfo,aColl)
#else
# define SZ_KEYINFO_0   sizeof(KeyInfo)
#endif

/*
** Allowed bit values for entries in the KeyInfo.aSortFlags[] array.
*/
#define KEYINFO_ORDER_DESC    0x01    /* DESC sort order */
#define KEYINFO_ORDER_BIGNULL 0x02    /* NULL is larger than any other value */

/*
** This object holds a record which has been parsed out into individual
** fields, for the purposes of doing a comparison.
**
** A record is an object that contains one or more fields of data.
** Records are used to store the content of a table row and to store
** the key of an index.  A blob encoding of a record is created by
** the OP_MakeRecord opcode of the VDBE and is disassembled by the
** OP_Column opcode.
**
** An instance of this object serves as a "key" for doing a search on
** an index b+tree. The goal of the search is to find the entry that
** is closest to the key described by this object.  This object might hold
** just a prefix of the key.  The number of fields is given by nField.

**
** The r1 and r2 fields are the values to return if this key is less than
** or greater than a key in the btree, respectively.  These are normally
** -1 and +1 respectively, but might be inverted to +1 and -1 if the b-tree
** is in DESC order.
**
** The key comparison functions actually return default_rc when they find
** an equals comparison.  default_rc can be -1, 0, or +1.  If there are
** multiple entries in the b-tree with the same key (when only looking
** at the first nField elements) then default_rc can be set to -1 to
** cause the search to find the last match, or +1 to cause the search to
** find the first match.
**
** The key comparison functions will set eqSeen to true if they ever
** get and equal results when comparing this structure to a b-tree record.
** When default_rc!=0, the search might end up on the record immediately
** before the first match or immediately after the last match.  The
** eqSeen field will indicate whether or not an exact match exists in the
** b-tree.
*/
struct UnpackedRecord {
  KeyInfo *pKeyInfo;  /* Comparison info for the index that is unpacked */
  Mem *aMem;          /* Values for columns of the index */
  union {
    char *z;            /* Cache of aMem[0].z for vdbeRecordCompareString() */
    i64 i;              /* Cache of aMem[0].u.i for vdbeRecordCompareInt() */
  } u;
  int n;              /* Cache of aMem[0].n used by vdbeRecordCompareString() */
  u16 nField;         /* Number of entries in apMem[] */
  i8 default_rc;      /* Comparison result if keys are equal */
Changes to src/tclsqlite.c.
45
46
47
48
49
50
51




52
53
54
55
56
57
58
# endif
#endif
/* Compatability between Tcl8.6 and Tcl9.0 */
#if TCL_MAJOR_VERSION==9
# define CONST const
#elif !defined(Tcl_Size)
  typedef int Tcl_Size;




#endif
/**** End copy of tclsqlite.h ****/

#include <errno.h>

/*
** Some additional include files are needed if this file is not







>
>
>
>







45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# endif
#endif
/* Compatability between Tcl8.6 and Tcl9.0 */
#if TCL_MAJOR_VERSION==9
# define CONST const
#elif !defined(Tcl_Size)
  typedef int Tcl_Size;
# ifndef Tcl_BounceRefCount
#  define Tcl_BounceRefCount(X) Tcl_IncrRefCount(X); Tcl_DecrRefCount(X)
   /* https://www.tcl-lang.org/man/tcl9.0/TclLib/Object.html */
# endif
#endif
/**** End copy of tclsqlite.h ****/

#include <errno.h>

/*
** Some additional include files are needed if this file is not
1080
1081
1082
1083
1084
1085
1086


1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
      ** happening, make sure pCmd has a valid string representation */
      Tcl_GetString(pCmd);
    }
    rc = Tcl_EvalObjEx(p->interp, pCmd, TCL_EVAL_DIRECT);
    Tcl_DecrRefCount(pCmd);
  }



  if( rc && rc!=TCL_RETURN ){
    sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1);
  }else{
    Tcl_Obj *pVar = Tcl_GetObjResult(p->interp);
    Tcl_Size n;
    u8 *data;
    const char *zType = (pVar->typePtr ? pVar->typePtr->name : "");
    char c = zType[0];
    int eType = p->eType;

    if( eType==SQLITE_NULL ){
      if( c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0 ){
        /* Only return a BLOB type if the Tcl variable is a bytearray and
        ** has no string representation. */
        eType = SQLITE_BLOB;
      }else if( (c=='b' && pVar->bytes==0 && strcmp(zType,"boolean")==0 )
             || (c=='b' && pVar->bytes==0 && strcmp(zType,"booleanString")==0 )
             || (c=='w' && strcmp(zType,"wideInt")==0)
             || (c=='i' && strcmp(zType,"int")==0) 
      ){
        eType = SQLITE_INTEGER;
      }else if( c=='d' && strcmp(zType,"double")==0 ){
        eType = SQLITE_FLOAT;
      }else{
        eType = SQLITE_TEXT;
      }







>
>
|

















|







1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
      ** happening, make sure pCmd has a valid string representation */
      Tcl_GetString(pCmd);
    }
    rc = Tcl_EvalObjEx(p->interp, pCmd, TCL_EVAL_DIRECT);
    Tcl_DecrRefCount(pCmd);
  }

  if( TCL_BREAK==rc ){
    sqlite3_result_null(context);
  }else if( rc && rc!=TCL_RETURN ){
    sqlite3_result_error(context, Tcl_GetStringResult(p->interp), -1);
  }else{
    Tcl_Obj *pVar = Tcl_GetObjResult(p->interp);
    Tcl_Size n;
    u8 *data;
    const char *zType = (pVar->typePtr ? pVar->typePtr->name : "");
    char c = zType[0];
    int eType = p->eType;

    if( eType==SQLITE_NULL ){
      if( c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0 ){
        /* Only return a BLOB type if the Tcl variable is a bytearray and
        ** has no string representation. */
        eType = SQLITE_BLOB;
      }else if( (c=='b' && pVar->bytes==0 && strcmp(zType,"boolean")==0 )
             || (c=='b' && pVar->bytes==0 && strcmp(zType,"booleanString")==0 )
             || (c=='w' && strcmp(zType,"wideInt")==0)
             || (c=='i' && strcmp(zType,"int")==0)
      ){
        eType = SQLITE_INTEGER;
      }else if( c=='d' && strcmp(zType,"double")==0 ){
        eType = SQLITE_FLOAT;
      }else{
        eType = SQLITE_TEXT;
      }
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623

1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
struct DbEvalContext {
  SqliteDb *pDb;                  /* Database handle */
  Tcl_Obj *pSql;                  /* Object holding string zSql */
  const char *zSql;               /* Remaining SQL to execute */
  SqlPreparedStmt *pPreStmt;      /* Current statement */
  int nCol;                       /* Number of columns returned by pStmt */
  int evalFlags;                  /* Flags used */
  Tcl_Obj *pArray;                /* Name of array variable */
  Tcl_Obj **apColName;            /* Array of column names */
};

#define SQLITE_EVAL_WITHOUTNULLS  0x00001  /* Unset array(*) for NULL */


/*
** Release any cache of column names currently held as part of
** the DbEvalContext structure passed as the first argument.
*/
static void dbReleaseColumnNames(DbEvalContext *p){
  if( p->apColName ){
    int i;
    for(i=0; i<p->nCol; i++){
      Tcl_DecrRefCount(p->apColName[i]);
    }
    Tcl_Free((char *)p->apColName);
    p->apColName = 0;
  }
  p->nCol = 0;
}

/*
** Initialize a DbEvalContext structure.
**
** If pArray is not NULL, then it contains the name of a Tcl array
** variable. The "*" member of this array is set to a list containing
** the names of the columns returned by the statement as part of each
** call to dbEvalStep(), in order from left to right. e.g. if the names
** of the returned columns are a, b and c, it does the equivalent of the
** tcl command:
**
**     set ${pArray}(*) {a b c}
*/
static void dbEvalInit(
  DbEvalContext *p,               /* Pointer to structure to initialize */
  SqliteDb *pDb,                  /* Database handle */
  Tcl_Obj *pSql,                  /* Object containing SQL script */
  Tcl_Obj *pArray,                /* Name of Tcl array to set (*) element of */
  int evalFlags                   /* Flags controlling evaluation */
){
  memset(p, 0, sizeof(DbEvalContext));
  p->pDb = pDb;
  p->zSql = Tcl_GetString(pSql);
  p->pSql = pSql;
  Tcl_IncrRefCount(pSql);
  if( pArray ){
    p->pArray = pArray;
    Tcl_IncrRefCount(pArray);
  }
  p->evalFlags = evalFlags;
  addDatabaseRef(p->pDb);
}

/*
** Obtain information about the row that the DbEvalContext passed as the







|




>




















|






|





|







|
|
|







1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
struct DbEvalContext {
  SqliteDb *pDb;                  /* Database handle */
  Tcl_Obj *pSql;                  /* Object holding string zSql */
  const char *zSql;               /* Remaining SQL to execute */
  SqlPreparedStmt *pPreStmt;      /* Current statement */
  int nCol;                       /* Number of columns returned by pStmt */
  int evalFlags;                  /* Flags used */
  Tcl_Obj *pVarName;              /* Name of target array/dict variable */
  Tcl_Obj **apColName;            /* Array of column names */
};

#define SQLITE_EVAL_WITHOUTNULLS  0x00001  /* Unset array(*) for NULL */
#define SQLITE_EVAL_ASDICT        0x00002  /* Use dict instead of array */

/*
** Release any cache of column names currently held as part of
** the DbEvalContext structure passed as the first argument.
*/
static void dbReleaseColumnNames(DbEvalContext *p){
  if( p->apColName ){
    int i;
    for(i=0; i<p->nCol; i++){
      Tcl_DecrRefCount(p->apColName[i]);
    }
    Tcl_Free((char *)p->apColName);
    p->apColName = 0;
  }
  p->nCol = 0;
}

/*
** Initialize a DbEvalContext structure.
**
** If pVarName is not NULL, then it contains the name of a Tcl array
** variable. The "*" member of this array is set to a list containing
** the names of the columns returned by the statement as part of each
** call to dbEvalStep(), in order from left to right. e.g. if the names
** of the returned columns are a, b and c, it does the equivalent of the
** tcl command:
**
**     set ${pVarName}(*) {a b c}
*/
static void dbEvalInit(
  DbEvalContext *p,               /* Pointer to structure to initialize */
  SqliteDb *pDb,                  /* Database handle */
  Tcl_Obj *pSql,                  /* Object containing SQL script */
  Tcl_Obj *pVarName,              /* Name of Tcl array to set (*) element of */
  int evalFlags                   /* Flags controlling evaluation */
){
  memset(p, 0, sizeof(DbEvalContext));
  p->pDb = pDb;
  p->zSql = Tcl_GetString(pSql);
  p->pSql = pSql;
  Tcl_IncrRefCount(pSql);
  if( pVarName ){
    p->pVarName = pVarName;
    Tcl_IncrRefCount(pVarName);
  }
  p->evalFlags = evalFlags;
  addDatabaseRef(p->pDb);
}

/*
** Obtain information about the row that the DbEvalContext passed as the
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706


1707
1708
1709






1710



1711



1712

1713
1714
1715
1716
1717
1718
1719
  if( 0==p->apColName ){
    sqlite3_stmt *pStmt = p->pPreStmt->pStmt;
    int i;                        /* Iterator variable */
    int nCol;                     /* Number of columns returned by pStmt */
    Tcl_Obj **apColName = 0;      /* Array of column names */

    p->nCol = nCol = sqlite3_column_count(pStmt);
    if( nCol>0 && (papColName || p->pArray) ){
      apColName = (Tcl_Obj**)Tcl_Alloc( sizeof(Tcl_Obj*)*nCol );
      for(i=0; i<nCol; i++){
        apColName[i] = Tcl_NewStringObj(sqlite3_column_name(pStmt,i), -1);
        Tcl_IncrRefCount(apColName[i]);
      }
      p->apColName = apColName;
    }

    /* If results are being stored in an array variable, then create
    ** the array(*) entry for that array
    */
    if( p->pArray ){
      Tcl_Interp *interp = p->pDb->interp;
      Tcl_Obj *pColList = Tcl_NewObj();
      Tcl_Obj *pStar = Tcl_NewStringObj("*", -1);



      for(i=0; i<nCol; i++){
        Tcl_ListObjAppendElement(interp, pColList, apColName[i]);
      }






      Tcl_IncrRefCount(pStar);



      Tcl_ObjSetVar2(interp, p->pArray, pStar, pColList, 0);



      Tcl_DecrRefCount(pStar);

    }
  }

  if( papColName ){
    *papColName = p->apColName;
  }
  if( pnCol ){







|








|
|

|




>
>



>
>
>
>
>
>
|
>
>
>
|
>
>
>

>







1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
  if( 0==p->apColName ){
    sqlite3_stmt *pStmt = p->pPreStmt->pStmt;
    int i;                        /* Iterator variable */
    int nCol;                     /* Number of columns returned by pStmt */
    Tcl_Obj **apColName = 0;      /* Array of column names */

    p->nCol = nCol = sqlite3_column_count(pStmt);
    if( nCol>0 && (papColName || p->pVarName) ){
      apColName = (Tcl_Obj**)Tcl_Alloc( sizeof(Tcl_Obj*)*nCol );
      for(i=0; i<nCol; i++){
        apColName[i] = Tcl_NewStringObj(sqlite3_column_name(pStmt,i), -1);
        Tcl_IncrRefCount(apColName[i]);
      }
      p->apColName = apColName;
    }

    /* If results are being stored in a variable then create the
    ** array(*) or dict(*) entry for that variable.
    */
    if( p->pVarName ){
      Tcl_Interp *interp = p->pDb->interp;
      Tcl_Obj *pColList = Tcl_NewObj();
      Tcl_Obj *pStar = Tcl_NewStringObj("*", -1);

      Tcl_IncrRefCount(pColList);
      Tcl_IncrRefCount(pStar);
      for(i=0; i<nCol; i++){
        Tcl_ListObjAppendElement(interp, pColList, apColName[i]);
      }
      if( 0==(SQLITE_EVAL_ASDICT & p->evalFlags) ){
        Tcl_ObjSetVar2(interp, p->pVarName, pStar, pColList, 0);
      }else{
        Tcl_Obj * pDict = Tcl_ObjGetVar2(interp, p->pVarName, NULL, 0);
        if( !pDict ){
          pDict = Tcl_NewDictObj();
        }else if( Tcl_IsShared(pDict) ){
          pDict = Tcl_DuplicateObj(pDict);
        }
        if( Tcl_DictObjPut(interp, pDict, pStar, pColList)==TCL_OK ){
          Tcl_ObjSetVar2(interp, p->pVarName, NULL, pDict, 0);
        }
        Tcl_BounceRefCount(pDict);
      }
      Tcl_DecrRefCount(pStar);
      Tcl_DecrRefCount(pColList);
    }
  }

  if( papColName ){
    *papColName = p->apColName;
  }
  if( pnCol ){
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
      SqlPreparedStmt *pPreStmt = p->pPreStmt;
      sqlite3_stmt *pStmt = pPreStmt->pStmt;

      rcs = sqlite3_step(pStmt);
      if( rcs==SQLITE_ROW ){
        return TCL_OK;
      }
      if( p->pArray ){
        dbEvalRowInfo(p, 0, 0);
      }
      rcs = sqlite3_reset(pStmt);

      pDb->nStep = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_FULLSCAN_STEP,1);
      pDb->nSort = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_SORT,1);
      pDb->nIndex = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_AUTOINDEX,1);







|







1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
      SqlPreparedStmt *pPreStmt = p->pPreStmt;
      sqlite3_stmt *pStmt = pPreStmt->pStmt;

      rcs = sqlite3_step(pStmt);
      if( rcs==SQLITE_ROW ){
        return TCL_OK;
      }
      if( p->pVarName ){
        dbEvalRowInfo(p, 0, 0);
      }
      rcs = sqlite3_reset(pStmt);

      pDb->nStep = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_FULLSCAN_STEP,1);
      pDb->nSort = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_SORT,1);
      pDb->nIndex = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_AUTOINDEX,1);
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
*/
static void dbEvalFinalize(DbEvalContext *p){
  if( p->pPreStmt ){
    sqlite3_reset(p->pPreStmt->pStmt);
    dbReleaseStmt(p->pDb, p->pPreStmt, 0);
    p->pPreStmt = 0;
  }
  if( p->pArray ){
    Tcl_DecrRefCount(p->pArray);
    p->pArray = 0;
  }
  Tcl_DecrRefCount(p->pSql);
  dbReleaseColumnNames(p);
  delDatabaseRef(p->pDb);
}

/*







|
|
|







1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
*/
static void dbEvalFinalize(DbEvalContext *p){
  if( p->pPreStmt ){
    sqlite3_reset(p->pPreStmt->pStmt);
    dbReleaseStmt(p->pDb, p->pPreStmt, 0);
    p->pPreStmt = 0;
  }
  if( p->pVarName ){
    Tcl_DecrRefCount(p->pVarName);
    p->pVarName = 0;
  }
  Tcl_DecrRefCount(p->pSql);
  dbReleaseColumnNames(p);
  delDatabaseRef(p->pDb);
}

/*
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909



1910
1911
1912














1913














1914
1915
1916
1917
1918
1919
1920
# define Tcl_NREvalObj(a,b,c) 0
# define Tcl_NRCreateCommand(a,b,c,d,e,f) (void)0
#endif

/*
** This function is part of the implementation of the command:
**
**   $db eval SQL ?ARRAYNAME? SCRIPT
*/
static int SQLITE_TCLAPI DbEvalNextCmd(
  ClientData data[],                   /* data[0] is the (DbEvalContext*) */
  Tcl_Interp *interp,                  /* Tcl interpreter */
  int result                           /* Result so far */
){
  int rc = result;                     /* Return code */

  /* The first element of the data[] array is a pointer to a DbEvalContext
  ** structure allocated using Tcl_Alloc(). The second element of data[]
  ** is a pointer to a Tcl_Obj containing the script to run for each row
  ** returned by the queries encapsulated in data[0]. */
  DbEvalContext *p = (DbEvalContext *)data[0];
  Tcl_Obj *pScript = (Tcl_Obj *)data[1];
  Tcl_Obj *pArray = p->pArray;

  while( (rc==TCL_OK || rc==TCL_CONTINUE) && TCL_OK==(rc = dbEvalStep(p)) ){
    int i;
    int nCol;
    Tcl_Obj **apColName;
    dbEvalRowInfo(p, &nCol, &apColName);
    for(i=0; i<nCol; i++){
      if( pArray==0 ){
        Tcl_ObjSetVar2(interp, apColName[i], 0, dbEvalColumnValue(p,i), 0);
      }else if( (p->evalFlags & SQLITE_EVAL_WITHOUTNULLS)!=0
             && sqlite3_column_type(p->pPreStmt->pStmt, i)==SQLITE_NULL 
      ){



        Tcl_UnsetVar2(interp, Tcl_GetString(pArray), 
                      Tcl_GetString(apColName[i]), 0);
      }else{














        Tcl_ObjSetVar2(interp, pArray, apColName[i], dbEvalColumnValue(p,i), 0);














      }
    }

    /* The required interpreter variables are now populated with the data
    ** from the current row. If using NRE, schedule callbacks to evaluate
    ** script pScript, then to invoke this function again to fetch the next
    ** row (or clean up if there is no next row or the script throws an







|













|
|







|


|

>
>
>
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
# define Tcl_NREvalObj(a,b,c) 0
# define Tcl_NRCreateCommand(a,b,c,d,e,f) (void)0
#endif

/*
** This function is part of the implementation of the command:
**
**   $db eval SQL ?TGT-NAME? SCRIPT
*/
static int SQLITE_TCLAPI DbEvalNextCmd(
  ClientData data[],                   /* data[0] is the (DbEvalContext*) */
  Tcl_Interp *interp,                  /* Tcl interpreter */
  int result                           /* Result so far */
){
  int rc = result;                     /* Return code */

  /* The first element of the data[] array is a pointer to a DbEvalContext
  ** structure allocated using Tcl_Alloc(). The second element of data[]
  ** is a pointer to a Tcl_Obj containing the script to run for each row
  ** returned by the queries encapsulated in data[0]. */
  DbEvalContext *p = (DbEvalContext *)data[0];
  Tcl_Obj * const pScript = (Tcl_Obj *)data[1];
  Tcl_Obj * const pVarName = p->pVarName;

  while( (rc==TCL_OK || rc==TCL_CONTINUE) && TCL_OK==(rc = dbEvalStep(p)) ){
    int i;
    int nCol;
    Tcl_Obj **apColName;
    dbEvalRowInfo(p, &nCol, &apColName);
    for(i=0; i<nCol; i++){
      if( pVarName==0 ){
        Tcl_ObjSetVar2(interp, apColName[i], 0, dbEvalColumnValue(p,i), 0);
      }else if( (p->evalFlags & SQLITE_EVAL_WITHOUTNULLS)!=0
             && sqlite3_column_type(p->pPreStmt->pStmt, i)==SQLITE_NULL
      ){
        /* Remove NULL-containing column from the target container... */
        if( 0==(SQLITE_EVAL_ASDICT & p->evalFlags) ){
          /* Target is an array */
          Tcl_UnsetVar2(interp, Tcl_GetString(pVarName),
                        Tcl_GetString(apColName[i]), 0);
        }else{
          /* Target is a dict */
          Tcl_Obj *pDict = Tcl_ObjGetVar2(interp, pVarName, NULL, 0);
          if( pDict ){
            if( Tcl_IsShared(pDict) ){
              pDict = Tcl_DuplicateObj(pDict);
            }
            if( Tcl_DictObjRemove(interp, pDict, apColName[i])==TCL_OK ){
              Tcl_ObjSetVar2(interp, pVarName, NULL, pDict, 0);
            }
            Tcl_BounceRefCount(pDict);
          }
        }
      }else if( 0==(SQLITE_EVAL_ASDICT & p->evalFlags) ){
        /* Target is an array: set target(colName) = colValue */
        Tcl_ObjSetVar2(interp, pVarName, apColName[i],
                       dbEvalColumnValue(p,i), 0);
      }else{
        /* Target is a dict: set target(colName) = colValue */
        Tcl_Obj *pDict = Tcl_ObjGetVar2(interp, pVarName, NULL, 0);
        if( !pDict ){
          pDict = Tcl_NewDictObj();
        }else if( Tcl_IsShared(pDict) ){
          pDict = Tcl_DuplicateObj(pDict);
        }
        if( Tcl_DictObjPut(interp, pDict, apColName[i],
                           dbEvalColumnValue(p,i))==TCL_OK ){
          Tcl_ObjSetVar2(interp, pVarName, NULL, pDict, 0);
        }
        Tcl_BounceRefCount(pDict);
      }
    }

    /* The required interpreter variables are now populated with the data
    ** from the current row. If using NRE, schedule callbacks to evaluate
    ** script pScript, then to invoke this function again to fetch the next
    ** row (or clean up if there is no next row or the script throws an
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
    "interrupt",              "last_insert_rowid",     "nullvalue",
    "onecolumn",              "preupdate",             "profile",
    "progress",               "rekey",                 "restore",
    "rollback_hook",          "serialize",             "status",
    "timeout",                "total_changes",         "trace",
    "trace_v2",               "transaction",           "unlock_notify",
    "update_hook",            "version",               "wal_hook",
    0                        
  };
  enum DB_enum {
    DB_AUTHORIZER,            DB_BACKUP,               DB_BIND_FALLBACK,
    DB_BUSY,                  DB_CACHE,                DB_CHANGES,
    DB_CLOSE,                 DB_COLLATE,              DB_COLLATION_NEEDED,
    DB_COMMIT_HOOK,           DB_COMPLETE,             DB_CONFIG,
    DB_COPY,                  DB_DESERIALIZE,          DB_ENABLE_LOAD_EXTENSION,







|







2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
    "interrupt",              "last_insert_rowid",     "nullvalue",
    "onecolumn",              "preupdate",             "profile",
    "progress",               "rekey",                 "restore",
    "rollback_hook",          "serialize",             "status",
    "timeout",                "total_changes",         "trace",
    "trace_v2",               "transaction",           "unlock_notify",
    "update_hook",            "version",               "wal_hook",
    0
  };
  enum DB_enum {
    DB_AUTHORIZER,            DB_BACKUP,               DB_BIND_FALLBACK,
    DB_BUSY,                  DB_CACHE,                DB_CHANGES,
    DB_CLOSE,                 DB_COLLATE,              DB_COLLATION_NEEDED,
    DB_COMMIT_HOOK,           DB_COMPLETE,             DB_CONFIG,
    DB_COPY,                  DB_DESERIALIZE,          DB_ENABLE_LOAD_EXTENSION,
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859

2860
2861
2862

2863
2864
2865
2866
2867
2868
2869
2870

2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
    if( rc==TCL_BREAK ){
      rc = TCL_OK;
    }
    break;
  }

  /*
  **    $db eval ?options? $sql ?array? ?{  ...code... }?
  **
  ** The SQL statement in $sql is evaluated.  For each row, the values are
  ** placed in elements of the array named "array" and ...code... is executed.

  ** If "array" and "code" are omitted, then no callback is every invoked.
  ** If "array" is an empty string, then the values are placed in variables
  ** that have the same name as the fields extracted by the query.

  */
  case DB_EVAL: {
    int evalFlags = 0;
    const char *zOpt;
    while( objc>3 && (zOpt = Tcl_GetString(objv[2]))!=0 && zOpt[0]=='-' ){
      if( strcmp(zOpt, "-withoutnulls")==0 ){
        evalFlags |= SQLITE_EVAL_WITHOUTNULLS;
      }

      else{
        Tcl_AppendResult(interp, "unknown option: \"", zOpt, "\"", (void*)0);
        return TCL_ERROR;
      }
      objc--;
      objv++;
    }
    if( objc<3 || objc>5 ){
      Tcl_WrongNumArgs(interp, 2, objv, 
          "?OPTIONS? SQL ?ARRAY-NAME? ?SCRIPT?");
      return TCL_ERROR;
    }

    if( objc==3 ){
      DbEvalContext sEval;
      Tcl_Obj *pRet = Tcl_NewObj();
      Tcl_IncrRefCount(pRet);







|

|
|
>
|
|
|
>







|
>
|







|
|







2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
    if( rc==TCL_BREAK ){
      rc = TCL_OK;
    }
    break;
  }

  /*
  **    $db eval ?options? $sql ?varName? ?{  ...code... }?
  **
  ** The SQL statement in $sql is evaluated.  For each row, the values
  ** are placed in elements of the array or dict named $varName and
  ** ...code... is executed.  If $varName and $code are omitted, then
  ** no callback is ever invoked.  If $varName is an empty string,
  ** then the values are placed in variables that have the same name
  ** as the fields extracted by the query, and those variables are
  ** accessible during the eval of $code.
  */
  case DB_EVAL: {
    int evalFlags = 0;
    const char *zOpt;
    while( objc>3 && (zOpt = Tcl_GetString(objv[2]))!=0 && zOpt[0]=='-' ){
      if( strcmp(zOpt, "-withoutnulls")==0 ){
        evalFlags |= SQLITE_EVAL_WITHOUTNULLS;
      }else if( strcmp(zOpt, "-asdict")==0 ){
        evalFlags |= SQLITE_EVAL_ASDICT;
      }else{
        Tcl_AppendResult(interp, "unknown option: \"", zOpt, "\"", (void*)0);
        return TCL_ERROR;
      }
      objc--;
      objv++;
    }
    if( objc<3 || objc>5 ){
      Tcl_WrongNumArgs(interp, 2, objv,
          "?OPTIONS? SQL ?VAR-NAME? ?SCRIPT?");
      return TCL_ERROR;
    }

    if( objc==3 ){
      DbEvalContext sEval;
      Tcl_Obj *pRet = Tcl_NewObj();
      Tcl_IncrRefCount(pRet);
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
        Tcl_SetObjResult(interp, pRet);
        rc = TCL_OK;
      }
      Tcl_DecrRefCount(pRet);
    }else{
      ClientData cd2[2];
      DbEvalContext *p;
      Tcl_Obj *pArray = 0;
      Tcl_Obj *pScript;

      if( objc>=5 && *(char *)Tcl_GetString(objv[3]) ){
        pArray = objv[3];
      }
      pScript = objv[objc-1];
      Tcl_IncrRefCount(pScript);

      p = (DbEvalContext *)Tcl_Alloc(sizeof(DbEvalContext));
      dbEvalInit(p, pDb, objv[2], pArray, evalFlags);

      cd2[0] = (void *)p;
      cd2[1] = (void *)pScript;
      rc = DbEvalNextCmd(cd2, interp, TCL_OK);
    }
    break;
  }







|



|





|







2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
        Tcl_SetObjResult(interp, pRet);
        rc = TCL_OK;
      }
      Tcl_DecrRefCount(pRet);
    }else{
      ClientData cd2[2];
      DbEvalContext *p;
      Tcl_Obj *pVarName = 0;
      Tcl_Obj *pScript;

      if( objc>=5 && *(char *)Tcl_GetString(objv[3]) ){
        pVarName = objv[3];
      }
      pScript = objv[objc-1];
      Tcl_IncrRefCount(pScript);

      p = (DbEvalContext *)Tcl_Alloc(sizeof(DbEvalContext));
      dbEvalInit(p, pDb, objv[2], pVarName, evalFlags);

      cd2[0] = (void *)p;
      cd2[1] = (void *)pScript;
      rc = DbEvalNextCmd(cd2, interp, TCL_OK);
    }
    break;
  }
Changes to src/test1.c.
8531
8532
8533
8534
8535
8536
8537

8538




8539
8540
8541
8542
8543
8544
8545
    { "RESET_DB",           SQLITE_DBCONFIG_RESET_DATABASE },
    { "DEFENSIVE",          SQLITE_DBCONFIG_DEFENSIVE },
    { "WRITABLE_SCHEMA",    SQLITE_DBCONFIG_WRITABLE_SCHEMA },
    { "LEGACY_ALTER_TABLE", SQLITE_DBCONFIG_LEGACY_ALTER_TABLE },
    { "DQS_DML",            SQLITE_DBCONFIG_DQS_DML },
    { "DQS_DDL",            SQLITE_DBCONFIG_DQS_DDL },
    { "LEGACY_FILE_FORMAT", SQLITE_DBCONFIG_LEGACY_FILE_FORMAT },

    { "STMT_SCANSTATUS",    SQLITE_DBCONFIG_STMT_SCANSTATUS },




  };
  int i;
  int v = 0;
  const char *zSetting;
  sqlite3 *db;

  if( objc!=4 && objc!=3 ){







>

>
>
>
>







8531
8532
8533
8534
8535
8536
8537
8538
8539
8540
8541
8542
8543
8544
8545
8546
8547
8548
8549
8550
    { "RESET_DB",           SQLITE_DBCONFIG_RESET_DATABASE },
    { "DEFENSIVE",          SQLITE_DBCONFIG_DEFENSIVE },
    { "WRITABLE_SCHEMA",    SQLITE_DBCONFIG_WRITABLE_SCHEMA },
    { "LEGACY_ALTER_TABLE", SQLITE_DBCONFIG_LEGACY_ALTER_TABLE },
    { "DQS_DML",            SQLITE_DBCONFIG_DQS_DML },
    { "DQS_DDL",            SQLITE_DBCONFIG_DQS_DDL },
    { "LEGACY_FILE_FORMAT", SQLITE_DBCONFIG_LEGACY_FILE_FORMAT },
    { "TRUSTED_SCHEMA",     SQLITE_DBCONFIG_TRUSTED_SCHEMA },
    { "STMT_SCANSTATUS",    SQLITE_DBCONFIG_STMT_SCANSTATUS },
    { "REVERSE_SCANORDER",  SQLITE_DBCONFIG_REVERSE_SCANORDER },
    { "ATTACH_CREATE",      SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE },
    { "ATTACH_WRITE",       SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE },
    { "COMMENTS",           SQLITE_DBCONFIG_ENABLE_COMMENTS },
  };
  int i;
  int v = 0;
  const char *zSetting;
  sqlite3 *db;

  if( objc!=4 && objc!=3 ){
Changes to src/vacuum.c.
191
192
193
194
195
196
197
198

199
200
201
202
203
204
205
  ** restored before returning. Then set the writable-schema flag, and
  ** disable CHECK and foreign key constraints.  */
  saved_flags = db->flags;
  saved_mDbFlags = db->mDbFlags;
  saved_nChange = db->nChange;
  saved_nTotalChange = db->nTotalChange;
  saved_mTrace = db->mTrace;
  db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_Comments;

  db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum;
  db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder
                   | SQLITE_Defensive | SQLITE_CountRows);
  db->mTrace = 0;

  zDbMain = db->aDb[iDb].zDbSName;
  pMain = db->aDb[iDb].pBt;







|
>







191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
  ** restored before returning. Then set the writable-schema flag, and
  ** disable CHECK and foreign key constraints.  */
  saved_flags = db->flags;
  saved_mDbFlags = db->mDbFlags;
  saved_nChange = db->nChange;
  saved_nTotalChange = db->nTotalChange;
  saved_mTrace = db->mTrace;
  db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_Comments
               | SQLITE_AttachCreate | SQLITE_AttachWrite;
  db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum;
  db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder
                   | SQLITE_Defensive | SQLITE_CountRows);
  db->mTrace = 0;

  zDbMain = db->aDb[iDb].zDbSName;
  pMain = db->aDb[iDb].pBt;
Changes to src/vdbe.c.
2472
2473
2474
2475
2476
2477
2478

2479
2480
2481
2482
2483
2484
2485
    aPermute = pOp[-1].p4.ai + 1;
    assert( aPermute!=0 );
  }
  n = pOp->p3;
  pKeyInfo = pOp->p4.pKeyInfo;
  assert( n>0 );
  assert( pKeyInfo!=0 );

  p1 = pOp->p1;
  p2 = pOp->p2;
#ifdef SQLITE_DEBUG
  if( aPermute ){
    int k, mx = 0;
    for(k=0; k<n; k++) if( aPermute[k]>(u32)mx ) mx = aPermute[k];
    assert( p1>0 && p1+mx<=(p->nMem+1 - p->nCursor)+1 );







>







2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
    aPermute = pOp[-1].p4.ai + 1;
    assert( aPermute!=0 );
  }
  n = pOp->p3;
  pKeyInfo = pOp->p4.pKeyInfo;
  assert( n>0 );
  assert( pKeyInfo!=0 );
  assert( pKeyInfo->aSortFlags!=0 );
  p1 = pOp->p1;
  p2 = pOp->p2;
#ifdef SQLITE_DEBUG
  if( aPermute ){
    int k, mx = 0;
    for(k=0; k<n; k++) if( aPermute[k]>(u32)mx ) mx = aPermute[k];
    assert( p1>0 && p1+mx<=(p->nMem+1 - p->nCursor)+1 );
5381
5382
5383
5384
5385
5386
5387
5388
5389
5390
5391
5392
5393
5394
5395
    assert( r.aMem->flags & MEM_Blob );
    assert( pOp->opcode!=OP_NoConflict );
    rc = ExpandBlob(r.aMem);
    assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
    if( rc ) goto no_mem;
    pIdxKey = sqlite3VdbeAllocUnpackedRecord(pC->pKeyInfo);
    if( pIdxKey==0 ) goto no_mem;
    sqlite3VdbeRecordUnpack(pC->pKeyInfo, r.aMem->n, r.aMem->z, pIdxKey);
    pIdxKey->default_rc = 0;
    rc = sqlite3BtreeIndexMoveto(pC->uc.pCursor, pIdxKey, &pC->seekResult);
    sqlite3DbFreeNN(db, pIdxKey);
  }
  if( rc!=SQLITE_OK ){
    goto abort_due_to_error;
  }







|







5382
5383
5384
5385
5386
5387
5388
5389
5390
5391
5392
5393
5394
5395
5396
    assert( r.aMem->flags & MEM_Blob );
    assert( pOp->opcode!=OP_NoConflict );
    rc = ExpandBlob(r.aMem);
    assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
    if( rc ) goto no_mem;
    pIdxKey = sqlite3VdbeAllocUnpackedRecord(pC->pKeyInfo);
    if( pIdxKey==0 ) goto no_mem;
    sqlite3VdbeRecordUnpack(r.aMem->n, r.aMem->z, pIdxKey);
    pIdxKey->default_rc = 0;
    rc = sqlite3BtreeIndexMoveto(pC->uc.pCursor, pIdxKey, &pC->seekResult);
    sqlite3DbFreeNN(db, pIdxKey);
  }
  if( rc!=SQLITE_OK ){
    goto abort_due_to_error;
  }
Changes to src/vdbe.h.
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
void sqlite3VdbeSetVarmask(Vdbe*, int);
#ifndef SQLITE_OMIT_TRACE
  char *sqlite3VdbeExpandSql(Vdbe*, const char*);
#endif
int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
int sqlite3BlobCompare(const Mem*, const Mem*);

void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*);
int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*);
int sqlite3VdbeRecordCompareWithSkip(int, const void *, UnpackedRecord *, int);
UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo*);

typedef int (*RecordCompare)(int,const void*,UnpackedRecord*);
RecordCompare sqlite3VdbeFindCompare(UnpackedRecord*);








|







297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
void sqlite3VdbeSetVarmask(Vdbe*, int);
#ifndef SQLITE_OMIT_TRACE
  char *sqlite3VdbeExpandSql(Vdbe*, const char*);
#endif
int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
int sqlite3BlobCompare(const Mem*, const Mem*);

void sqlite3VdbeRecordUnpack(int,const void*,UnpackedRecord*);
int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*);
int sqlite3VdbeRecordCompareWithSkip(int, const void *, UnpackedRecord *, int);
UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo*);

typedef int (*RecordCompare)(int,const void*,UnpackedRecord*);
RecordCompare sqlite3VdbeFindCompare(UnpackedRecord*);

Changes to src/vdbeInt.h.
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
  i64 iKey1;                      /* First key value passed to hook */
  i64 iKey2;                      /* Second key value passed to hook */
  Mem oldipk;                     /* Memory cell holding "old" IPK value */
  Mem *aNew;                      /* Array of new.* values */
  Table *pTab;                    /* Schema object being updated */
  Index *pPk;                     /* PK index if pTab is WITHOUT ROWID */
  sqlite3_value **apDflt;         /* Array of default values, if required */
  u8 keyinfoSpace[SZ_KEYINFO(0)]; /* Space to hold pKeyinfo[0] content */
};

/*
** An instance of this object is used to pass an vector of values into
** OP_VFilter, the xFilter method of a virtual table.  The vector is the
** set of values on the right-hand side of an IN constraint.
**







|







553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
  i64 iKey1;                      /* First key value passed to hook */
  i64 iKey2;                      /* Second key value passed to hook */
  Mem oldipk;                     /* Memory cell holding "old" IPK value */
  Mem *aNew;                      /* Array of new.* values */
  Table *pTab;                    /* Schema object being updated */
  Index *pPk;                     /* PK index if pTab is WITHOUT ROWID */
  sqlite3_value **apDflt;         /* Array of default values, if required */
  u8 keyinfoSpace[SZ_KEYINFO_0];  /* Space to hold pKeyinfo[0] content */
};

/*
** An instance of this object is used to pass an vector of values into
** OP_VFilter, the xFilter method of a virtual table.  The vector is the
** set of values on the right-hand side of an IN constraint.
**
Changes to src/vdbeapi.c.
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
  const void *pKey
){
  UnpackedRecord *pRet;           /* Return value */

  pRet = sqlite3VdbeAllocUnpackedRecord(pKeyInfo);
  if( pRet ){
    memset(pRet->aMem, 0, sizeof(Mem)*(pKeyInfo->nKeyField+1));
    sqlite3VdbeRecordUnpack(pKeyInfo, nKey, pKey, pRet);
  }
  return pRet;
}

/*
** This function is called from within a pre-update callback to retrieve
** a field of the row currently being updated or deleted.







|







2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
  const void *pKey
){
  UnpackedRecord *pRet;           /* Return value */

  pRet = sqlite3VdbeAllocUnpackedRecord(pKeyInfo);
  if( pRet ){
    memset(pRet->aMem, 0, sizeof(Mem)*(pKeyInfo->nKeyField+1));
    sqlite3VdbeRecordUnpack(nKey, pKey, pRet);
  }
  return pRet;
}

/*
** This function is called from within a pre-update callback to retrieve
** a field of the row currently being updated or deleted.
Changes to src/vdbeaux.c.
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271

4272
4273
4274
4275
4276
4277
4278
      pMem->flags = aFlag[serial_type&1];
      return;
    }
  }
  return;
}
/*
** This routine is used to allocate sufficient space for an UnpackedRecord
** structure large enough to be used with sqlite3VdbeRecordUnpack() if
** the first argument is a pointer to KeyInfo structure pKeyInfo.
**
** The space is either allocated using sqlite3DbMallocRaw() or from within
** the unaligned buffer passed via the second and third arguments (presumably
** stack space). If the former, then *ppFree is set to a pointer that should
** be eventually freed by the caller using sqlite3DbFree(). Or, if the
** allocation comes from the pSpace/szSpace buffer, *ppFree is set to NULL
** before returning.
**
** If an OOM error occurs, NULL is returned.
*/
UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(
  KeyInfo *pKeyInfo               /* Description of the record */
){
  UnpackedRecord *p;              /* Unpacked record to return */
  int nByte;                      /* Number of bytes required for *p */
  assert( sizeof(UnpackedRecord) + sizeof(Mem)*65536 < 0x7fffffff );
  nByte = ROUND8P(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nKeyField+1);
  p = (UnpackedRecord *)sqlite3DbMallocRaw(pKeyInfo->db, nByte);
  if( !p ) return 0;
  p->aMem = (Mem*)&((char*)p)[ROUND8P(sizeof(UnpackedRecord))];
  assert( pKeyInfo->aSortFlags!=0 );
  p->pKeyInfo = pKeyInfo;
  p->nField = pKeyInfo->nKeyField + 1;
  return p;
}

/*
** Given the nKey-byte encoding of a record in pKey[], populate the
** UnpackedRecord structure indicated by the fourth argument with the
** contents of the decoded record.
*/
void sqlite3VdbeRecordUnpack(
  KeyInfo *pKeyInfo,     /* Information about the record format */
  int nKey,              /* Size of the binary record */
  const void *pKey,      /* The binary record */
  UnpackedRecord *p      /* Populate this structure before returning. */
){
  const unsigned char *aKey = (const unsigned char *)pKey;
  u32 d;
  u32 idx;                        /* Offset in aKey[] to read from */
  u16 u;                          /* Unsigned loop counter */
  u32 szHdr;
  Mem *pMem = p->aMem;


  p->default_rc = 0;
  assert( EIGHT_BYTE_ALIGNMENT(pMem) );
  idx = getVarint32(aKey, szHdr);
  d = szHdr;
  u = 0;
  while( idx<szHdr && d<=(u32)nKey ){







|
<
|

|
<
<
<
<
<
<
|





|





<











<










>







4219
4220
4221
4222
4223
4224
4225
4226

4227
4228
4229






4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241

4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252

4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
      pMem->flags = aFlag[serial_type&1];
      return;
    }
  }
  return;
}
/*
** Allocate sufficient space for an UnpackedRecord structure large enough

** to hold a decoded index record for pKeyInfo.
**
** The space is allocated using sqlite3DbMallocRaw().  If an OOM error






** occurs, NULL is returned.
*/
UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(
  KeyInfo *pKeyInfo               /* Description of the record */
){
  UnpackedRecord *p;              /* Unpacked record to return */
  u64 nByte;                      /* Number of bytes required for *p */
  assert( sizeof(UnpackedRecord) + sizeof(Mem)*65536 < 0x7fffffff );
  nByte = ROUND8P(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nKeyField+1);
  p = (UnpackedRecord *)sqlite3DbMallocRaw(pKeyInfo->db, nByte);
  if( !p ) return 0;
  p->aMem = (Mem*)&((char*)p)[ROUND8P(sizeof(UnpackedRecord))];

  p->pKeyInfo = pKeyInfo;
  p->nField = pKeyInfo->nKeyField + 1;
  return p;
}

/*
** Given the nKey-byte encoding of a record in pKey[], populate the
** UnpackedRecord structure indicated by the fourth argument with the
** contents of the decoded record.
*/
void sqlite3VdbeRecordUnpack(

  int nKey,              /* Size of the binary record */
  const void *pKey,      /* The binary record */
  UnpackedRecord *p      /* Populate this structure before returning. */
){
  const unsigned char *aKey = (const unsigned char *)pKey;
  u32 d;
  u32 idx;                        /* Offset in aKey[] to read from */
  u16 u;                          /* Unsigned loop counter */
  u32 szHdr;
  Mem *pMem = p->aMem;
  KeyInfo *pKeyInfo = p->pKeyInfo;

  p->default_rc = 0;
  assert( EIGHT_BYTE_ALIGNMENT(pMem) );
  idx = getVarint32(aKey, szHdr);
  d = szHdr;
  u = 0;
  while( idx<szHdr && d<=(u32)nKey ){
4292
4293
4294
4295
4296
4297
4298


4299
4300
4301
4302
4303
4304
4305
  if( d>(u32)nKey && u ){
    assert( CORRUPT_DB );
    /* In a corrupt record entry, the last pMem might have been set up using
    ** uninitialized memory. Overwrite its value with NULL, to prevent
    ** warnings from MSAN. */
    sqlite3VdbeMemSetNull(pMem-1);
  }


  assert( u<=pKeyInfo->nKeyField + 1 );
  p->nField = u;
}

#ifdef SQLITE_DEBUG
/*
** This function compares two index or table record keys in the same way







>
>







4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
  if( d>(u32)nKey && u ){
    assert( CORRUPT_DB );
    /* In a corrupt record entry, the last pMem might have been set up using
    ** uninitialized memory. Overwrite its value with NULL, to prevent
    ** warnings from MSAN. */
    sqlite3VdbeMemSetNull(pMem-1);
  }
  testcase( u == pKeyInfo->nKeyField + 1 );
  testcase( u < pKeyInfo->nKeyField + 1 );
  assert( u<=pKeyInfo->nKeyField + 1 );
  p->nField = u;
}

#ifdef SQLITE_DEBUG
/*
** This function compares two index or table record keys in the same way
5151
5152
5153
5154
5155
5156
5157

5158
5159
5160
5161
5162
5163
5164
  ** buffer passed to varintRecordCompareInt() this makes it convenient to
  ** limit the size of the header to 64 bytes in cases where the first field
  ** is an integer.
  **
  ** The easiest way to enforce this limit is to consider only records with
  ** 13 fields or less. If the first field is an integer, the maximum legal
  ** header size is (12*5 + 1 + 1) bytes.  */

  if( p->pKeyInfo->nAllField<=13 ){
    int flags = p->aMem[0].flags;
    if( p->pKeyInfo->aSortFlags[0] ){
      if( p->pKeyInfo->aSortFlags[0] & KEYINFO_ORDER_BIGNULL ){
        return sqlite3VdbeRecordCompare;
      }
      p->r1 = 1;







>







5145
5146
5147
5148
5149
5150
5151
5152
5153
5154
5155
5156
5157
5158
5159
  ** buffer passed to varintRecordCompareInt() this makes it convenient to
  ** limit the size of the header to 64 bytes in cases where the first field
  ** is an integer.
  **
  ** The easiest way to enforce this limit is to consider only records with
  ** 13 fields or less. If the first field is an integer, the maximum legal
  ** header size is (12*5 + 1 + 1) bytes.  */
  assert( p->pKeyInfo->aSortFlags!=0 );
  if( p->pKeyInfo->nAllField<=13 ){
    int flags = p->aMem[0].flags;
    if( p->pKeyInfo->aSortFlags[0] ){
      if( p->pKeyInfo->aSortFlags[0] & KEYINFO_ORDER_BIGNULL ){
        return sqlite3VdbeRecordCompare;
      }
      p->r1 = 1;
5509
5510
5511
5512
5513
5514
5515
5516
5517
5518
5519
5520
5521
5522
5523
  int iReg,                       /* Register for new.* record */
  int iBlobWrite
){
  sqlite3 *db = v->db;
  i64 iKey2;
  PreUpdate preupdate;
  const char *zTbl = pTab->zName;
  static const u8 fakeSortOrder = 0;
#ifdef SQLITE_DEBUG
  int nRealCol;
  if( pTab->tabFlags & TF_WithoutRowid ){
    nRealCol = sqlite3PrimaryKeyIndex(pTab)->nColumn;
  }else if( pTab->tabFlags & TF_HasVirtual ){
    nRealCol = pTab->nNVCol;
  }else{







<







5504
5505
5506
5507
5508
5509
5510

5511
5512
5513
5514
5515
5516
5517
  int iReg,                       /* Register for new.* record */
  int iBlobWrite
){
  sqlite3 *db = v->db;
  i64 iKey2;
  PreUpdate preupdate;
  const char *zTbl = pTab->zName;

#ifdef SQLITE_DEBUG
  int nRealCol;
  if( pTab->tabFlags & TF_WithoutRowid ){
    nRealCol = sqlite3PrimaryKeyIndex(pTab)->nColumn;
  }else if( pTab->tabFlags & TF_HasVirtual ){
    nRealCol = pTab->nNVCol;
  }else{
5548
5549
5550
5551
5552
5553
5554
5555
5556
5557
5558
5559
5560
5561
5562
  preupdate.pCsr = pCsr;
  preupdate.op = op;
  preupdate.iNewReg = iReg;
  preupdate.pKeyinfo = (KeyInfo*)&preupdate.keyinfoSpace;
  preupdate.pKeyinfo->db = db;
  preupdate.pKeyinfo->enc = ENC(db);
  preupdate.pKeyinfo->nKeyField = pTab->nCol;
  preupdate.pKeyinfo->aSortFlags = (u8*)&fakeSortOrder;
  preupdate.iKey1 = iKey1;
  preupdate.iKey2 = iKey2;
  preupdate.pTab = pTab;
  preupdate.iBlobWrite = iBlobWrite;

  db->pPreUpdate = &preupdate;
  db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);







|







5542
5543
5544
5545
5546
5547
5548
5549
5550
5551
5552
5553
5554
5555
5556
  preupdate.pCsr = pCsr;
  preupdate.op = op;
  preupdate.iNewReg = iReg;
  preupdate.pKeyinfo = (KeyInfo*)&preupdate.keyinfoSpace;
  preupdate.pKeyinfo->db = db;
  preupdate.pKeyinfo->enc = ENC(db);
  preupdate.pKeyinfo->nKeyField = pTab->nCol;
  preupdate.pKeyinfo->aSortFlags = 0; /* Indicate .aColl, .nAllField uninit */
  preupdate.iKey1 = iKey1;
  preupdate.iKey2 = iKey2;
  preupdate.pTab = pTab;
  preupdate.iBlobWrite = iBlobWrite;

  db->pPreUpdate = &preupdate;
  db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);
Changes to src/vdbesort.c.
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
  SortSubtask *pTask,             /* Subtask context (for pKeyInfo) */
  int *pbKey2Cached,              /* True if pTask->pUnpacked is pKey2 */
  const void *pKey1, int nKey1,   /* Left side of comparison */
  const void *pKey2, int nKey2    /* Right side of comparison */
){
  UnpackedRecord *r2 = pTask->pUnpacked;
  if( *pbKey2Cached==0 ){
    sqlite3VdbeRecordUnpack(pTask->pSorter->pKeyInfo, nKey2, pKey2, r2);
    *pbKey2Cached = 1;
  }
  return sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, r2, 1);
}

/*
** Compare key1 (buffer pKey1, size nKey1 bytes) with key2 (buffer pKey2,







|







762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
  SortSubtask *pTask,             /* Subtask context (for pKeyInfo) */
  int *pbKey2Cached,              /* True if pTask->pUnpacked is pKey2 */
  const void *pKey1, int nKey1,   /* Left side of comparison */
  const void *pKey2, int nKey2    /* Right side of comparison */
){
  UnpackedRecord *r2 = pTask->pUnpacked;
  if( *pbKey2Cached==0 ){
    sqlite3VdbeRecordUnpack(nKey2, pKey2, r2);
    *pbKey2Cached = 1;
  }
  return sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, r2, 1);
}

/*
** Compare key1 (buffer pKey1, size nKey1 bytes) with key2 (buffer pKey2,
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
  SortSubtask *pTask,             /* Subtask context (for pKeyInfo) */
  int *pbKey2Cached,              /* True if pTask->pUnpacked is pKey2 */
  const void *pKey1, int nKey1,   /* Left side of comparison */
  const void *pKey2, int nKey2    /* Right side of comparison */
){
  UnpackedRecord *r2 = pTask->pUnpacked;
  if( !*pbKey2Cached ){
    sqlite3VdbeRecordUnpack(pTask->pSorter->pKeyInfo, nKey2, pKey2, r2);
    *pbKey2Cached = 1;
  }
  return sqlite3VdbeRecordCompare(nKey1, pKey1, r2);
}

/*
** A specially optimized version of vdbeSorterCompare() that assumes that







|







789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
  SortSubtask *pTask,             /* Subtask context (for pKeyInfo) */
  int *pbKey2Cached,              /* True if pTask->pUnpacked is pKey2 */
  const void *pKey1, int nKey1,   /* Left side of comparison */
  const void *pKey2, int nKey2    /* Right side of comparison */
){
  UnpackedRecord *r2 = pTask->pUnpacked;
  if( !*pbKey2Cached ){
    sqlite3VdbeRecordUnpack(nKey2, pKey2, r2);
    *pbKey2Cached = 1;
  }
  return sqlite3VdbeRecordCompare(nKey1, pKey1, r2);
}

/*
** A specially optimized version of vdbeSorterCompare() that assumes that
829
830
831
832
833
834
835

836
837
838
839
840
841
842
  if( res==0 ){
    if( pTask->pSorter->pKeyInfo->nKeyField>1 ){
      res = vdbeSorterCompareTail(
          pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2
      );
    }
  }else{

    assert( !(pTask->pSorter->pKeyInfo->aSortFlags[0]&KEYINFO_ORDER_BIGNULL) );
    if( pTask->pSorter->pKeyInfo->aSortFlags[0] ){
      res = res * -1;
    }
  }

  return res;







>







829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
  if( res==0 ){
    if( pTask->pSorter->pKeyInfo->nKeyField>1 ){
      res = vdbeSorterCompareTail(
          pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2
      );
    }
  }else{
    assert( pTask->pSorter->pKeyInfo->aSortFlags!=0 );
    assert( !(pTask->pSorter->pKeyInfo->aSortFlags[0]&KEYINFO_ORDER_BIGNULL) );
    if( pTask->pSorter->pKeyInfo->aSortFlags[0] ){
      res = res * -1;
    }
  }

  return res;
892
893
894
895
896
897
898

899
900
901
902
903
904
905
    if( res>0 ){
      if( *v1 & 0x80 ) res = -1;
    }else{
      if( *v2 & 0x80 ) res = +1;
    }
  }


  if( res==0 ){
    if( pTask->pSorter->pKeyInfo->nKeyField>1 ){
      res = vdbeSorterCompareTail(
          pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2
      );
    }
  }else if( pTask->pSorter->pKeyInfo->aSortFlags[0] ){







>







893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
    if( res>0 ){
      if( *v1 & 0x80 ) res = -1;
    }else{
      if( *v2 & 0x80 ) res = +1;
    }
  }

  assert( pTask->pSorter->pKeyInfo->aSortFlags!=0 );
  if( res==0 ){
    if( pTask->pSorter->pKeyInfo->nKeyField>1 ){
      res = vdbeSorterCompareTail(
          pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2
      );
    }
  }else if( pTask->pSorter->pKeyInfo->aSortFlags[0] ){
965
966
967
968
969
970
971

972
973
974
975
976
977
978
979
980
981
982
983
984
985

986




987
988
989
990
991
992
993
#endif

  assert( pCsr->pKeyInfo );
  assert( !pCsr->isEphemeral );
  assert( pCsr->eCurType==CURTYPE_SORTER );
  assert( sizeof(KeyInfo) + UMXV(pCsr->pKeyInfo->nKeyField)*sizeof(CollSeq*)
               < 0x7fffffff );

  szKeyInfo = SZ_KEYINFO(pCsr->pKeyInfo->nKeyField+1);
  sz = SZ_VDBESORTER(nWorker+1);

  pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo);
  pCsr->uc.pSorter = pSorter;
  if( pSorter==0 ){
    rc = SQLITE_NOMEM_BKPT;
  }else{
    Btree *pBt = db->aDb[0].pBt;
    pSorter->pKeyInfo = pKeyInfo = (KeyInfo*)((u8*)pSorter + sz);
    memcpy(pKeyInfo, pCsr->pKeyInfo, szKeyInfo);
    pKeyInfo->db = 0;
    if( nField && nWorker==0 ){
      pKeyInfo->nKeyField = nField;

    }




    sqlite3BtreeEnter(pBt);
    pSorter->pgsz = pgsz = sqlite3BtreeGetPageSize(pBt);
    sqlite3BtreeLeave(pBt);
    pSorter->nTask = nWorker + 1;
    pSorter->iPrev = (u8)(nWorker - 1);
    pSorter->bUseThreads = (pSorter->nTask>1);
    pSorter->db = db;







>
|













>

>
>
>
>







967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
#endif

  assert( pCsr->pKeyInfo );
  assert( !pCsr->isEphemeral );
  assert( pCsr->eCurType==CURTYPE_SORTER );
  assert( sizeof(KeyInfo) + UMXV(pCsr->pKeyInfo->nKeyField)*sizeof(CollSeq*)
               < 0x7fffffff );
  assert( pCsr->pKeyInfo->nKeyField<=pCsr->pKeyInfo->nAllField );
  szKeyInfo = SZ_KEYINFO(pCsr->pKeyInfo->nAllField);
  sz = SZ_VDBESORTER(nWorker+1);

  pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo);
  pCsr->uc.pSorter = pSorter;
  if( pSorter==0 ){
    rc = SQLITE_NOMEM_BKPT;
  }else{
    Btree *pBt = db->aDb[0].pBt;
    pSorter->pKeyInfo = pKeyInfo = (KeyInfo*)((u8*)pSorter + sz);
    memcpy(pKeyInfo, pCsr->pKeyInfo, szKeyInfo);
    pKeyInfo->db = 0;
    if( nField && nWorker==0 ){
      pKeyInfo->nKeyField = nField;
      assert( nField<=pCsr->pKeyInfo->nAllField );
    }
    /* It is OK that pKeyInfo reuses the aSortFlags field from pCsr->pKeyInfo,
    ** since the pCsr->pKeyInfo->aSortFlags[] array is invariant and lives
    ** longer that pSorter. */
    assert( pKeyInfo->aSortFlags==pCsr->pKeyInfo->aSortFlags );
    sqlite3BtreeEnter(pBt);
    pSorter->pgsz = pgsz = sqlite3BtreeGetPageSize(pBt);
    sqlite3BtreeLeave(pBt);
    pSorter->nTask = nWorker + 1;
    pSorter->iPrev = (u8)(nWorker - 1);
    pSorter->bUseThreads = (pSorter->nTask>1);
    pSorter->db = db;
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
    r2 = pSorter->pUnpacked = sqlite3VdbeAllocUnpackedRecord(pKeyInfo);
    if( r2==0 ) return SQLITE_NOMEM_BKPT;
    r2->nField = nKeyCol;
  }
  assert( r2->nField==nKeyCol );

  pKey = vdbeSorterRowkey(pSorter, &nKey);
  sqlite3VdbeRecordUnpack(pKeyInfo, nKey, pKey, r2);
  for(i=0; i<nKeyCol; i++){
    if( r2->aMem[i].flags & MEM_Null ){
      *pRes = -1;
      return SQLITE_OK;
    }
  }

  *pRes = sqlite3VdbeRecordCompare(pVal->n, pVal->z, r2);
  return SQLITE_OK;
}







|










2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
    r2 = pSorter->pUnpacked = sqlite3VdbeAllocUnpackedRecord(pKeyInfo);
    if( r2==0 ) return SQLITE_NOMEM_BKPT;
    r2->nField = nKeyCol;
  }
  assert( r2->nField==nKeyCol );

  pKey = vdbeSorterRowkey(pSorter, &nKey);
  sqlite3VdbeRecordUnpack(nKey, pKey, r2);
  for(i=0; i<nKeyCol; i++){
    if( r2->aMem[i].flags & MEM_Null ){
      *pRes = -1;
      return SQLITE_OK;
    }
  }

  *pRes = sqlite3VdbeRecordCompare(pVal->n, pVal->z, r2);
  return SQLITE_OK;
}
Changes to src/wal.c.
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754

3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770

3771
3772
3773
3774
3775
3776
3777

  if( !useWal ){
    assert( rc==SQLITE_OK );
    if( pWal->bShmUnreliable==0 ){
      rc = walIndexReadHdr(pWal, pChanged);
    }
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
    walDisableBlocking(pWal);
    if( rc==SQLITE_BUSY_TIMEOUT ){
      rc = SQLITE_BUSY;
      *pCnt |= WAL_RETRY_BLOCKED_MASK;
    }
#endif
    if( rc==SQLITE_BUSY ){
      /* If there is not a recovery running in another thread or process
      ** then convert BUSY errors to WAL_RETRY.  If recovery is known to
      ** be running, convert BUSY to BUSY_RECOVERY.  There is a race here
      ** which might cause WAL_RETRY to be returned even if BUSY_RECOVERY
      ** would be technically correct.  But the race is benign since with
      ** WAL_RETRY this routine will be called again and will probably be
      ** right on the second iteration.
      */

      if( pWal->apWiData[0]==0 ){
        /* This branch is taken when the xShmMap() method returns SQLITE_BUSY.
        ** We assume this is a transient condition, so return WAL_RETRY. The
        ** xShmMap() implementation used by the default unix and win32 VFS
        ** modules may return SQLITE_BUSY due to a race condition in the
        ** code that determines whether or not the shared-memory region
        ** must be zeroed before the requested page is returned.
        */
        rc = WAL_RETRY;
      }else if( SQLITE_OK==(rc = walLockShared(pWal, WAL_RECOVER_LOCK)) ){
        walUnlockShared(pWal, WAL_RECOVER_LOCK);
        rc = WAL_RETRY;
      }else if( rc==SQLITE_BUSY ){
        rc = SQLITE_BUSY_RECOVERY;
      }
    }

    if( rc!=SQLITE_OK ){
      return rc;
    }
    else if( pWal->bShmUnreliable ){
      return walBeginShmUnreliable(pWal, pChanged);
    }
  }







<














>
















>







3733
3734
3735
3736
3737
3738
3739

3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778

  if( !useWal ){
    assert( rc==SQLITE_OK );
    if( pWal->bShmUnreliable==0 ){
      rc = walIndexReadHdr(pWal, pChanged);
    }
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT

    if( rc==SQLITE_BUSY_TIMEOUT ){
      rc = SQLITE_BUSY;
      *pCnt |= WAL_RETRY_BLOCKED_MASK;
    }
#endif
    if( rc==SQLITE_BUSY ){
      /* If there is not a recovery running in another thread or process
      ** then convert BUSY errors to WAL_RETRY.  If recovery is known to
      ** be running, convert BUSY to BUSY_RECOVERY.  There is a race here
      ** which might cause WAL_RETRY to be returned even if BUSY_RECOVERY
      ** would be technically correct.  But the race is benign since with
      ** WAL_RETRY this routine will be called again and will probably be
      ** right on the second iteration.
      */
      (void)walEnableBlocking(pWal);
      if( pWal->apWiData[0]==0 ){
        /* This branch is taken when the xShmMap() method returns SQLITE_BUSY.
        ** We assume this is a transient condition, so return WAL_RETRY. The
        ** xShmMap() implementation used by the default unix and win32 VFS
        ** modules may return SQLITE_BUSY due to a race condition in the
        ** code that determines whether or not the shared-memory region
        ** must be zeroed before the requested page is returned.
        */
        rc = WAL_RETRY;
      }else if( SQLITE_OK==(rc = walLockShared(pWal, WAL_RECOVER_LOCK)) ){
        walUnlockShared(pWal, WAL_RECOVER_LOCK);
        rc = WAL_RETRY;
      }else if( rc==SQLITE_BUSY ){
        rc = SQLITE_BUSY_RECOVERY;
      }
    }
    walDisableBlocking(pWal);
    if( rc!=SQLITE_OK ){
      return rc;
    }
    else if( pWal->bShmUnreliable ){
      return walBeginShmUnreliable(pWal, pChanged);
    }
  }
Changes to src/wherecode.c.
2197
2198
2199
2200
2201
2202
2203
2204

2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
      sqlite3VdbeAddOp4Int(v, OP_NotFound, iCur, addrCont,
                           iRowidReg, pPk->nKeyCol); VdbeCoverage(v);
    }

    if( pLevel->iLeftJoin==0 ){
      /* If a partial index is driving the loop, try to eliminate WHERE clause
      ** terms from the query that must be true due to the WHERE clause of
      ** the partial index.

      **
      ** 2019-11-02 ticket 623eff57e76d45f6: This optimization does not work
      ** for a LEFT JOIN.
      */
      if( pIdx->pPartIdxWhere ){
        whereApplyPartialIndexConstraints(pIdx->pPartIdxWhere, iCur, pWC);
      }
    }else{
      testcase( pIdx->pPartIdxWhere );
      /* The following assert() is not a requirement, merely an observation:
      ** The OR-optimization doesn't work for the right hand table of
      ** a LEFT JOIN: */







|
>

|
|

|







2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
      sqlite3VdbeAddOp4Int(v, OP_NotFound, iCur, addrCont,
                           iRowidReg, pPk->nKeyCol); VdbeCoverage(v);
    }

    if( pLevel->iLeftJoin==0 ){
      /* If a partial index is driving the loop, try to eliminate WHERE clause
      ** terms from the query that must be true due to the WHERE clause of
      ** the partial index.  This optimization does not work on an outer join,
      ** as shown by:
      **
      ** 2019-11-02 ticket 623eff57e76d45f6      (LEFT JOIN)
      ** 2025-05-29 forum post 7dee41d32506c4ae  (RIGHT JOIN)
      */
      if( pIdx->pPartIdxWhere && pLevel->pRJ==0 ){
        whereApplyPartialIndexConstraints(pIdx->pPartIdxWhere, iCur, pWC);
      }
    }else{
      testcase( pIdx->pPartIdxWhere );
      /* The following assert() is not a requirement, merely an observation:
      ** The OR-optimization doesn't work for the right hand table of
      ** a LEFT JOIN: */
Added test/dblwidth-a.sql.








































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
** Run this script using "sqlite3" to confirm that the command-line
** shell properly handles the output of double-width characters.
**
** https://sqlite.org/forum/forumpost/008ac80276
*/
.mode box
CREATE TABLE data(word TEXT, description TEXT);
INSERT INTO data VALUES('〈οὐκέτι〉','Greek without dblwidth <...>');
.print .mode box
SELECT * FROM data;
.mode table
.print .mode table
SELECT * FROM data;
.mode qbox
.print .mode qbox
SELECT * FROM data;
.mode column
.print .mode column
SELECT * FROM data;
Changes to test/fts3join.test.
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
do_eqp_test 4.2 {
  SELECT * FROM t4 LEFT JOIN (
      SELECT docid, * FROM ft4 WHERE ft4 MATCH ?
  ) AS rr ON t4.rowid=rr.docid 
  WHERE t4.y = ?;
} {
  QUERY PLAN
  |--MATERIALIZE rr
  |  `--SCAN ft4 VIRTUAL TABLE INDEX 3:
  |--SCAN t4
  |--BLOOM FILTER ON rr (docid=?)
  `--SEARCH rr USING AUTOMATIC COVERING INDEX (docid=?) LEFT-JOIN
}

finish_test







<
<

<
|



93
94
95
96
97
98
99


100

101
102
103
104
do_eqp_test 4.2 {
  SELECT * FROM t4 LEFT JOIN (
      SELECT docid, * FROM ft4 WHERE ft4 MATCH ?
  ) AS rr ON t4.rowid=rr.docid 
  WHERE t4.y = ?;
} {
  QUERY PLAN


  |--SCAN t4

  `--SCAN ft4 VIRTUAL TABLE INDEX 3: LEFT-JOIN
}

finish_test
Changes to test/join.test.
997
998
999
1000
1001
1002
1003















1004
1005
1006
1007
1008
1009
1010
  INSERT INTO t0(c0) VALUES (0);
  SELECT * FROM t0 LEFT JOIN t1 WHERE NULL IN (c1);
} {}
do_execsql_test join-20.2 {
  CREATE INDEX t1x ON t1(0) WHERE NULL IN (c1);
  SELECT * FROM t0 LEFT JOIN t1 WHERE NULL IN (c1);
} {}
















# 2019-11-30 ticket 7f39060a24b47353
# Do not allow a WHERE clause term to qualify a partial index on the
# right table of a LEFT JOIN.
#
do_execsql_test join-21.10 {
  DROP TABLE t0;







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
  INSERT INTO t0(c0) VALUES (0);
  SELECT * FROM t0 LEFT JOIN t1 WHERE NULL IN (c1);
} {}
do_execsql_test join-20.2 {
  CREATE INDEX t1x ON t1(0) WHERE NULL IN (c1);
  SELECT * FROM t0 LEFT JOIN t1 WHERE NULL IN (c1);
} {}

# 2025-05-29 forum post 7dee41d32506c4ae
# The complaint in the forum post appears to be the same as for the
# ticket on 2019-11-02, only for RIGHT JOIN instead of LEFT JOIN.  Note
# that RIGHT JOIN did not yet exist in SQLite when the ticket was
# written and fixed.
#
do_execsql_test join-20.3 {
  DROP TABLE t1;
  CREATE TABLE t1(x INT);      INSERT INTO t1(x) VALUES(1);
  CREATE TABLE t2(y BOOLEAN);  INSERT INTO t2(y) VALUES(false);
  CREATE TABLE t3(z INT);      INSERT INTO t3(z) VALUES(3);
  CREATE INDEX t2y ON t2(y) WHERE y;
  SELECT quote(z) FROM t1 RIGHT JOIN t2 ON y LEFT JOIN t3 ON y;
} {NULL}

# 2019-11-30 ticket 7f39060a24b47353
# Do not allow a WHERE clause term to qualify a partial index on the
# right table of a LEFT JOIN.
#
do_execsql_test join-21.10 {
  DROP TABLE t0;
1284
1285
1286
1287
1288
1289
1290
1291






































1292
   WHERE x <= y;
} {}
do_execsql_test join-30.3 {
  SELECT DISTINCT a, b
    FROM t0 JOIN t1 ON z=a RIGHT JOIN t2 ON a=b LEFT JOIN v5 ON false
   WHERE x <= y;
} {}







































finish_test








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
   WHERE x <= y;
} {}
do_execsql_test join-30.3 {
  SELECT DISTINCT a, b
    FROM t0 JOIN t1 ON z=a RIGHT JOIN t2 ON a=b LEFT JOIN v5 ON false
   WHERE x <= y;
} {}

# 2025-05-30 https://sqlite.org/forum/forumpost/4fc70203b61c7e12
#
# When converting a USING(x) or NATURAL into the constraint expression
# t1.x==t2.x, mark the t1.x term as EP_CanBeNull if it is the left table
# of a RIGHT JOIN.
#
reset_db
db null NULL
do_execsql_test join-31.1 {
  CREATE TABLE t1(c0 INT , c1 INT); INSERT INTO t1(c0, c1) VALUES(NULL,11);
  CREATE TABLE t2(c0 INT NOT NULL);
  CREATE TABLE t2n(c0 INT);
  CREATE TABLE t3(x INT);           INSERT INTO t3(x) VALUES(3);
  CREATE TABLE t4(y INT);           INSERT INTO t4(y) VALUES(4);
  CREATE TABLE t5(c0 INT, x INT);   INSERT INTO t5 VALUES(NULL, 5);
}
do_execsql_test join-31.2 {
  SELECT * FROM t2 RIGHT JOIN t3 ON true LEFT JOIN t1 USING(c0);
} {NULL 3 NULL}
do_execsql_test join-31.3 {
  SELECT * FROM t2 RIGHT JOIN t3 ON true NATURAL LEFT JOIN t1;
} {NULL 3 NULL}
do_execsql_test join-31.4 {
  SELECT * FROM t2n RIGHT JOIN t3 ON true LEFT JOIN t1 USING(c0);
} {NULL 3 NULL}
do_execsql_test join-31.5 {
  SELECT * FROM t5 LEFT JOIN t1 USING(c0);
} {NULL 5 NULL}
do_execsql_test join-31.6 {
  SELECT * FROM t3 LEFT JOIN t2 ON true LEFT JOIN t1 USING(c0);
} {3 NULL NULL}
do_execsql_test join-31.7 {
  SELECT * FROM t3 LEFT JOIN t2 ON true NATURAL LEFT JOIN t1;
} {3 NULL NULL}
do_execsql_test join-31.8 {
  SELECT * FROM t3 LEFT JOIN t2 ON true JOIN t4 ON true NATURAL LEFT JOIN t1;
} {3 NULL 4 NULL}

finish_test
Changes to test/joinH.test.
336
337
338
339
340
341
342






































































343
344
  SELECT a1.a, sum( a1.a+a1.b ) FROM t3 AS a1 RIGHT JOIN t4 ON a=x
   GROUP BY a1.a ORDER BY 1;
} {NULL NULL 1 -592 4 192 16 48}
do_execsql_test 13.4 {
  SELECT sum( a1.a+a1.b ) FROM t3 AS a1 RIGHT JOIN t3 ON true
   GROUP BY a1.a ORDER BY 1;
} {-1480 240 480}







































































finish_test







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
  SELECT a1.a, sum( a1.a+a1.b ) FROM t3 AS a1 RIGHT JOIN t4 ON a=x
   GROUP BY a1.a ORDER BY 1;
} {NULL NULL 1 -592 4 192 16 48}
do_execsql_test 13.4 {
  SELECT sum( a1.a+a1.b ) FROM t3 AS a1 RIGHT JOIN t3 ON true
   GROUP BY a1.a ORDER BY 1;
} {-1480 240 480}

#-------------------------------------------------------------------------
# 2025-05-30
# https://sqlite.org/forum/forumpost/5028c785b6
#
reset_db

do_execsql_test 14.0 {
  CREATE TABLE t1(c0 INT);
  CREATE TABLE t2(c0 BLOB);
  CREATE TABLE t3(c0 BLOB);
  CREATE TABLE t4(c4 BLOB);
  INSERT INTO t1(c0) VALUES(0);
  INSERT INTO t3(c0) VALUES('0');
}

do_execsql_test 14.1.1 {
  SELECT * FROM t1 NATURAL LEFT JOIN t2 NATURAL JOIN t3;
} {0}

do_execsql_test 14.1.2 {
  SELECT * FROM t1 NATURAL LEFT JOIN t2 NATURAL JOIN t3 FULL JOIN t4 ON true;
} {0 {}}

do_execsql_test 14.1.3 {
  SELECT * FROM (t1 NATURAL LEFT JOIN t2 NATURAL JOIN t3) FULL JOIN t4 ON true;
} {0 {}}

do_execsql_test 14.1.4 {
  SELECT * 
  FROM (t1 NATURAL LEFT JOIN t2 NATURAL JOIN t3) AS qq FULL JOIN t4 ON true;
} {0 {}}

do_execsql_test 14.2.1 {
  SELECT * FROM t3 NATURAL LEFT JOIN t2 NATURAL JOIN t1;
} {0}

do_execsql_test 14.2.2 {
  SELECT * FROM t3 NATURAL LEFT JOIN t2 NATURAL JOIN t1 FULL JOIN t4 ON true;
} {0 {}}

do_execsql_test 14.2.3 {
  SELECT * FROM (t3 NATURAL LEFT JOIN t2 NATURAL JOIN t1) FULL JOIN t4 ON true;
} {0 {}}

do_execsql_test 14.2.4 {
  SELECT * 
  FROM (t3 NATURAL LEFT JOIN t2 NATURAL JOIN t1) AS qq FULL JOIN t4 ON true;
} {0 {}}

# 2025-06-01 
#
reset_db
do_execsql_test 15.1 {
  CREATE TABLE t0(c0);
  CREATE TABLE t1(c0);
  CREATE TABLE t2(c0);
  INSERT INTO t0 VALUES ('1.0');
  INSERT INTO t2(c0) VALUES (9);
  SELECT t0.c0,t2.c0 FROM (SELECT CAST(t0.c0 as REAL) AS c0 FROM t0) as subquery NATURAL LEFT JOIN t1  NATURAL JOIN t0  RIGHT JOIN t2 ON 1;
} {1.0 9}
do_execsql_test 15.2 {
  CREATE TABLE x1(x COLLATE nocase);
  CREATE TABLE x2(x);
  CREATE TABLE x3(x);
  CREATE TABLE t4(y);
  INSERT INTO x1 VALUES('ABC');
  INSERT INTO x3 VALUES('abc');
  SELECT lower(x), quote(y) FROM x1 LEFT JOIN x2 USING (x) JOIN x3 USING (x) FULL JOIN t4;
} {abc NULL}

finish_test
Changes to test/snapshot3.test.
91
92
93
94
95
96
97



98
99
100
101
102
103
104
  file size test.db-wal
} 0

do_test 1.8 {
  execsql BEGIN db3
  list [catch { sqlite3_snapshot_open_blob db3 main $snap } msg] $msg
} {1 SQLITE_ERROR_SNAPSHOT}




#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
  PRAGMA journal_mode = wal;
  CREATE TABLE t1(a, b);
  INSERT INTO t1 VALUES(1, 2);







>
>
>







91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
  file size test.db-wal
} 0

do_test 1.8 {
  execsql BEGIN db3
  list [catch { sqlite3_snapshot_open_blob db3 main $snap } msg] $msg
} {1 SQLITE_ERROR_SNAPSHOT}

db3 close
db2 close

#-------------------------------------------------------------------------
reset_db
do_execsql_test 2.0 {
  PRAGMA journal_mode = wal;
  CREATE TABLE t1(a, b);
  INSERT INTO t1 VALUES(1, 2);
Changes to test/tclsqlite.test.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 2001 September 15
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for TCL interface to the
# SQLite library. 
#
# Actually, all tests are based on the TCL interface, so the main
# interface is pretty well tested.  This file contains some addition
# tests for fringe issues that the main test suite does not cover.
#
# $Id: tclsqlite.test,v 1.73 2009/03/16 13:19:36 danielk1977 Exp $












|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 2001 September 15
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for TCL interface to the
# SQLite library.
#
# Actually, all tests are based on the TCL interface, so the main
# interface is pretty well tested.  This file contains some addition
# tests for fringe issues that the main test suite does not cover.
#
# $Id: tclsqlite.test,v 1.73 2009/03/16 13:19:36 danielk1977 Exp $

117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
    set v [catch {db complete} msg]
    lappend v $msg
  } {1 {wrong # args: should be "db complete SQL"}}
}
do_test tcl-1.14 {
  set v [catch {db eval} msg]
  lappend v $msg
} {1 {wrong # args: should be "db eval ?OPTIONS? SQL ?ARRAY-NAME? ?SCRIPT?"}}
do_test tcl-1.15 {
  set v [catch {db function} msg]
  lappend v $msg
} {1 {wrong # args: should be "db function NAME ?SWITCHES? SCRIPT"}}
do_test tcl-1.16 {
  set v [catch {db last_insert_rowid xyz} msg]
  lappend v $msg







|







117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
    set v [catch {db complete} msg]
    lappend v $msg
  } {1 {wrong # args: should be "db complete SQL"}}
}
do_test tcl-1.14 {
  set v [catch {db eval} msg]
  lappend v $msg
} {1 {wrong # args: should be "db eval ?OPTIONS? SQL ?VAR-NAME? ?SCRIPT?"}}
do_test tcl-1.15 {
  set v [catch {db function} msg]
  lappend v $msg
} {1 {wrong # args: should be "db function NAME ?SWITCHES? SCRIPT"}}
do_test tcl-1.16 {
  set v [catch {db last_insert_rowid xyz} msg]
  lappend v $msg
354
355
356
357
358
359
360













361
362
363
364
365
366
367
  db function ret_dbl {return [expr {rand()*0.5}]}
  execsql {SELECT typeof(ret_dbl())}
} {real}
do_test tcl-9.3 {
  db function ret_int {return [expr {int(rand()*200)}]}
  execsql {SELECT typeof(ret_int())}
} {integer}














# Recursive calls to the same user-defined function
#
ifcapable tclvar {
  do_test tcl-9.10 {
    proc userfunc_r1 {n} {
      if {$n<=0} {return 0}







>
>
>
>
>
>
>
>
>
>
>
>
>







354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
  db function ret_dbl {return [expr {rand()*0.5}]}
  execsql {SELECT typeof(ret_dbl())}
} {real}
do_test tcl-9.3 {
  db function ret_int {return [expr {int(rand()*200)}]}
  execsql {SELECT typeof(ret_int())}
} {integer}

proc breakAsNullUdf args {
  if {"1" eq [lindex $args 0]} {return -code break}
}
do_test tcl-9.4 {
  db function banu breakAsNullUdf
  execsql {SELECT typeof(banu()), typeof(banu(1))}
} {text null}
do_test tcl-9.5 {
  db nullvalue banunull
  db eval {SELECT banu(), banu(1)}
} {{} banunull}


# Recursive calls to the same user-defined function
#
ifcapable tclvar {
  do_test tcl-9.10 {
    proc userfunc_r1 {n} {
      if {$n<=0} {return 0}
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
    }
  }]
} {2}
do_test tcl-10.13 {
  db eval {SELECT * FROM t4}
} {1 2 5 6 7}

# Now test that [db transaction] commands may be nested with 
# the expected results.
#
do_test tcl-10.14 {
  db transaction {
    db eval {
      DELETE FROM t4;
      INSERT INTO t4 VALUES('one');
    }

    catch { 
      db transaction {
        db eval { INSERT INTO t4 VALUES('two') }
        db transaction {
          db eval { INSERT INTO t4 VALUES('three') }
          error "throw an error!"
        }
      }







|









|







474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
    }
  }]
} {2}
do_test tcl-10.13 {
  db eval {SELECT * FROM t4}
} {1 2 5 6 7}

# Now test that [db transaction] commands may be nested with
# the expected results.
#
do_test tcl-10.14 {
  db transaction {
    db eval {
      DELETE FROM t4;
      INSERT INTO t4 VALUES('one');
    }

    catch {
      db transaction {
        db eval { INSERT INTO t4 VALUES('two') }
        db transaction {
          db eval { INSERT INTO t4 VALUES('three') }
          error "throw an error!"
        }
      }
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
  db exists {SELECT a FROM t1 WHERE a>2}
} {1}
do_test tcl-15.5 {
  db exists {SELECT a FROM t1 WHERE a>3}
} {0}


# 2017-06-26: The --withoutnulls flag to "db eval".
#
# In the "db eval --withoutnulls SQL ARRAY" form, NULL results cause the
# corresponding array entry to be unset.  The default behavior (without
# the -withoutnulls flags) is for the corresponding array value to get
# the [db nullvalue] string.
#
catch {db close}
forcedelete test.db
sqlite3 db test.db
do_execsql_test tcl-16.100 {
  CREATE TABLE t1(a,b);







|

|
|
|







683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
  db exists {SELECT a FROM t1 WHERE a>2}
} {1}
do_test tcl-15.5 {
  db exists {SELECT a FROM t1 WHERE a>3}
} {0}


# 2017-06-26: The -withoutnulls flag to "db eval".
#
# In the "db eval -withoutnulls SQL TARGET" form, NULL results cause the
# corresponding target entry to be unset.  The default behavior (without
# the -withoutnulls flags) is for the corresponding target value to get
# the [db nullvalue] string.
#
catch {db close}
forcedelete test.db
sqlite3 db test.db
do_execsql_test tcl-16.100 {
  CREATE TABLE t1(a,b);
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
#-------------------------------------------------------------------------
# Test the -type option to [db function].
#
reset_db
proc add {a b} { return [expr $a + $b] }
proc ret {a} { return $a }

db function add_i -returntype integer add 
db function add_r -ret        real    add
db function add_t -return     text    add 
db function add_b -returntype blob    add 
db function add_a -returntype any     add 

db function ret_i -returntype int     ret 
db function ret_r -returntype real    ret
db function ret_t -returntype text    ret 
db function ret_b -returntype blob    ret 
db function ret_a -r          any     ret 

do_execsql_test 17.0 {
  SELECT quote( add_i(2, 3) );
  SELECT quote( add_r(2, 3) ); 
  SELECT quote( add_t(2, 3) ); 
  SELECT quote( add_b(2, 3) ); 
  SELECT quote( add_a(2, 3) ); 
} {5 5.0 '5' X'35' 5}

do_execsql_test 17.1 {
  SELECT quote( add_i(2.2, 3.3) );
  SELECT quote( add_r(2.2, 3.3) ); 
  SELECT quote( add_t(2.2, 3.3) ); 
  SELECT quote( add_b(2.2, 3.3) ); 
  SELECT quote( add_a(2.2, 3.3) ); 
} {5.5 5.5 '5.5' X'352E35' 5.5}

do_execsql_test 17.2 {
  SELECT quote( ret_i(2.5) );
  SELECT quote( ret_r(2.5) ); 
  SELECT quote( ret_t(2.5) ); 
  SELECT quote( ret_b(2.5) ); 
  SELECT quote( ret_a(2.5) ); 
} {2.5 2.5 '2.5' X'322E35' 2.5}

do_execsql_test 17.3 {
  SELECT quote( ret_i('2.5') );
  SELECT quote( ret_r('2.5') ); 
  SELECT quote( ret_t('2.5') ); 
  SELECT quote( ret_b('2.5') ); 
  SELECT quote( ret_a('2.5') ); 
} {2.5 2.5 '2.5' X'322E35' '2.5'}

do_execsql_test 17.4 {
  SELECT quote( ret_i('abc') );
  SELECT quote( ret_r('abc') ); 
  SELECT quote( ret_t('abc') ); 
  SELECT quote( ret_b('abc') ); 
  SELECT quote( ret_a('abc') ); 
} {'abc' 'abc' 'abc' X'616263' 'abc'}

do_execsql_test 17.5 {
  SELECT quote( ret_i(X'616263') );
  SELECT quote( ret_r(X'616263') ); 
  SELECT quote( ret_t(X'616263') ); 
  SELECT quote( ret_b(X'616263') ); 
  SELECT quote( ret_a(X'616263') ); 
} {'abc' 'abc' 'abc' X'616263' X'616263'}

do_test 17.6.1 {
  list [catch { db function xyz -return object ret } msg] $msg
} {1 {bad type "object": must be integer, real, text, blob, or any}}

do_test 17.6.2 {







|

|
|
|

|

|
|
|



|
|
|
|




|
|
|
|




|
|
|
|




|
|
|
|




|
|
|
|




|
|
|
|







729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
#-------------------------------------------------------------------------
# Test the -type option to [db function].
#
reset_db
proc add {a b} { return [expr $a + $b] }
proc ret {a} { return $a }

db function add_i -returntype integer add
db function add_r -ret        real    add
db function add_t -return     text    add
db function add_b -returntype blob    add
db function add_a -returntype any     add

db function ret_i -returntype int     ret
db function ret_r -returntype real    ret
db function ret_t -returntype text    ret
db function ret_b -returntype blob    ret
db function ret_a -r          any     ret

do_execsql_test 17.0 {
  SELECT quote( add_i(2, 3) );
  SELECT quote( add_r(2, 3) );
  SELECT quote( add_t(2, 3) );
  SELECT quote( add_b(2, 3) );
  SELECT quote( add_a(2, 3) );
} {5 5.0 '5' X'35' 5}

do_execsql_test 17.1 {
  SELECT quote( add_i(2.2, 3.3) );
  SELECT quote( add_r(2.2, 3.3) );
  SELECT quote( add_t(2.2, 3.3) );
  SELECT quote( add_b(2.2, 3.3) );
  SELECT quote( add_a(2.2, 3.3) );
} {5.5 5.5 '5.5' X'352E35' 5.5}

do_execsql_test 17.2 {
  SELECT quote( ret_i(2.5) );
  SELECT quote( ret_r(2.5) );
  SELECT quote( ret_t(2.5) );
  SELECT quote( ret_b(2.5) );
  SELECT quote( ret_a(2.5) );
} {2.5 2.5 '2.5' X'322E35' 2.5}

do_execsql_test 17.3 {
  SELECT quote( ret_i('2.5') );
  SELECT quote( ret_r('2.5') );
  SELECT quote( ret_t('2.5') );
  SELECT quote( ret_b('2.5') );
  SELECT quote( ret_a('2.5') );
} {2.5 2.5 '2.5' X'322E35' '2.5'}

do_execsql_test 17.4 {
  SELECT quote( ret_i('abc') );
  SELECT quote( ret_r('abc') );
  SELECT quote( ret_t('abc') );
  SELECT quote( ret_b('abc') );
  SELECT quote( ret_a('abc') );
} {'abc' 'abc' 'abc' X'616263' 'abc'}

do_execsql_test 17.5 {
  SELECT quote( ret_i(X'616263') );
  SELECT quote( ret_r(X'616263') );
  SELECT quote( ret_t(X'616263') );
  SELECT quote( ret_b(X'616263') );
  SELECT quote( ret_a(X'616263') );
} {'abc' 'abc' 'abc' X'616263' X'616263'}

do_test 17.6.1 {
  list [catch { db function xyz -return object ret } msg] $msg
} {1 {bad type "object": must be integer, real, text, blob, or any}}

do_test 17.6.2 {
844
845
846
847
848
849
850
















































851
852
853
854
855
856
857
858
859
860
861
862
863
864
865

866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891

892
  db bind_fallback bind_fallback_does_not_exist
} {}
do_catchsql_test 19.911 {
  SELECT $abc, typeof($abc), $def, typeof($def), $ghi, typeof($ghi);
} {1 {invalid command name "bind_fallback_does_not_exist"}}
db bind_fallback {}

















































#-------------------------------------------------------------------------
do_test 20.0 {
  db transaction {
    db close
  }
} {}

do_test 20.1 {
  sqlite3 db test.db
  set rc [catch {
    db eval {SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3} { db close }
  } msg]
  list $rc $msg
} {1 {invalid command name "db"}}
  


proc closedb {} {
  db close
  return 10
}
proc func1 {} { return 1 }

sqlite3 db test.db
db func closedb closedb
db func func1 func1

do_test 20.2 {
  set rc [catch {
    db eval {
      SELECT closedb(),func1() UNION ALL SELECT 20,30 UNION ALL SELECT 30,40
    }
  } msg]
  list $rc $msg
} {0 {10 1 20 30 30 40}}

sqlite3 db :memory:
do_test 21.1 {
  catch {db eval {SELECT 1 2 3;}} msg
  db erroroffset
} {9}


finish_test







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

|





|






|
>











|









|




>

857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
  db bind_fallback bind_fallback_does_not_exist
} {}
do_catchsql_test 19.911 {
  SELECT $abc, typeof($abc), $def, typeof($def), $ghi, typeof($ghi);
} {1 {invalid command name "bind_fallback_does_not_exist"}}
db bind_fallback {}

# 2025-05-05: the -asdict eval flag
#
do_test 20.0 {
  execsql {CREATE TABLE tad(a,b)}
  execsql {INSERT INTO tad(a,b) VALUES('aa','bb'),('AA','BB')}
  db eval -asdict {
    SELECT a, b FROM tad WHERE 0
  } D {}
  set D
} {* {a b}}

do_test 20.1 {
  unset D
  set i 0
  set res {}
  set colNames {}
  db eval -asdict {
    SELECT a, b FROM tad ORDER BY a
  } D {
    dict set D i [incr i]
    lappend res $i [dict get $D a] [dict get $D b]
    if {1 == $i} {
      set colNames [dict get $D *]
    }
  }
  lappend res $colNames
  unset D
  set res
} {1 AA BB 2 aa bb {a b}}

do_test 20.2 {
  set res {}
  db eval -asdict -withoutnulls {
    SELECT n, a, b FROM (
      SELECT 1 as n, 'aa' as a, NULL as b
      UNION ALL
      SELECT 2 as n, NULL as a, 'bb' as b
    )
    ORDER BY n
  } D {
    dict unset D *
    lappend res [dict values $D]
  }
  unset D
  execsql {DROP TABLE tad}
  set res
} {{1 aa} {2 bb}}

#-------------------------------------------------------------------------
do_test 21.0 {
  db transaction {
    db close
  }
} {}

do_test 21.1 {
  sqlite3 db test.db
  set rc [catch {
    db eval {SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3} { db close }
  } msg]
  list $rc $msg
} {1 {invalid command name "db"}}



proc closedb {} {
  db close
  return 10
}
proc func1 {} { return 1 }

sqlite3 db test.db
db func closedb closedb
db func func1 func1

do_test 21.2 {
  set rc [catch {
    db eval {
      SELECT closedb(),func1() UNION ALL SELECT 20,30 UNION ALL SELECT 30,40
    }
  } msg]
  list $rc $msg
} {0 {10 1 20 30 30 40}}

sqlite3 db :memory:
do_test 22.1 {
  catch {db eval {SELECT 1 2 3;}} msg
  db erroroffset
} {9}


finish_test
Changes to test/vacuum.test.
396
397
398
399
400
401
402





















403
404
    CREATE TABLE t8(a, b);
    INSERT INTO t8 VALUES('a', 'b');
    INSERT INTO t8 VALUES('c', 'd');
    PRAGMA count_changes = 1;
  }
} {}
do_test vacuum-10.2 { execsql VACUUM } {}






















finish_test







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
    CREATE TABLE t8(a, b);
    INSERT INTO t8 VALUES('a', 'b');
    INSERT INTO t8 VALUES('c', 'd');
    PRAGMA count_changes = 1;
  }
} {}
do_test vacuum-10.2 { execsql VACUUM } {}

# Verify that VACUUM still works if ATTACH is disabled.
#
do_execsql_test vacuum-11.1 {
  PRAGMA page_size=1024;
  VACUUM;
  PRAGMA page_size;
} {1024}
sqlite3_db_config db ATTACH_CREATE 0
do_execsql_test vacuum-11.2 {
  PRAGMA page_size=2048;
  VACUUM;
  PRAGMA page_size;
} {2048}
sqlite3_db_config db ATTACH_CREATE 1
sqlite3_db_config db ATTACH_WRITE 0
do_execsql_test vacuum-11.3 {
  PRAGMA page_size=4096;
  VACUUM;
  PRAGMA page_size;
} {4096}

finish_test
Added test/walsetlk_recover.test.
















































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# 2025 May 30
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#
# TESTRUNNER: slow
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
set testprefix walsetlk_recover

ifcapable !wal {finish_test ; return }
# ifcapable !setlk_timeout {finish_test ; return }

do_execsql_test 1.0 {
  PRAGMA journal_mode = wal;
  CREATE TABLE t1(a, b);
  INSERT INTO t1 VALUES(1, 2);
  INSERT INTO t1 VALUES(3, 4);
  INSERT INTO t1 VALUES(5, 6);
} {wal}

db_save_and_close
db_restore

testfixture_nb myvar {

  testvfs tvfs -fullshm 1
  sqlite3 db test.db -vfs tvfs
  tvfs script vfs_callback
  tvfs filter xRead

  set ::done 0
  proc vfs_callback {method file args} {
    if {$::done==0 && [string match *wal $file]} {
      after 4000
      set ::done 1
    }
    return "SQLITE_OK"
  }

  db eval {
    SELECT * FROM t1
  }

  db close
}

# Give the [testfixture_nb] command time to start
after 1000 {set xyz 1}
vwait xyz

testvfs tvfs -fullshm 1
sqlite3 db test.db -vfs tvfs

tvfs script sleep_callback
tvfs filter xSleep
set ::sleep_count 0
proc sleep_callback {args} {
  incr ::sleep_count
}

sqlite3 db test.db -vfs tvfs
db timeout 500
set tm [lindex [time {
  catch {
    db eval {SELECT * FROM t1}
  } msg
}] 0]

do_test 1.2         { set ::msg } {database is locked}
do_test 1.3.($::tm) { expr $::tm>400000 && $::tm<2000000 } 1

vwait myvar

do_execsql_test 1.4 {
  SELECT * FROM t1
} {1 2 3 4 5 6}

db close
tvfs delete

# All SQLite builds should pass the tests above. SQLITE_ENABLE_SETLK_TIMEOUT=1
# builds do so without calling the VFS xSleep method.
if {$::sqlite_options(setlk_timeout)==1} {
  do_test 1.5.1 {
    set ::sleep_count
  } 0
} else {
  do_test 1.5.2 {
    expr $::sleep_count>0
  } 1
}

finish_test

Added test/walsetlk_snapshot.test.


























































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# 2025 May 30
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#
# TESTRUNNER: slow
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
set testprefix walsetlk_snapshot

ifcapable !wal {finish_test ; return }
ifcapable !snapshot {finish_test; return}

db close
testvfs tvfs -fullshm 1
sqlite3 db test.db -vfs tvfs
tvfs script sleep_callback
tvfs filter xSleep
set ::sleep_count 0
proc sleep_callback {args} {
  incr ::sleep_count
}

do_execsql_test 1.0 {
  PRAGMA journal_mode = wal;
  CREATE TABLE t1(a, b);
  INSERT INTO t1 VALUES(1, 2);
  INSERT INTO t1 VALUES(3, 4);
  INSERT INTO t1 VALUES(5, 6);
} {wal}

do_test 1.1 {
  db eval BEGIN
  set ::snap [sqlite3_snapshot_get db main]
  db eval {
    INSERT INTO t1 VALUES(7, 8);
    COMMIT;
  }
} {}

testfixture_nb myvar {

  testvfs tvfs -fullshm 1
  sqlite3 db test.db -vfs tvfs
  tvfs script vfs_callback
  tvfs filter {xWrite}

  set ::done 0
  proc vfs_callback {args} {
    if {$::done==0} {
      after 4000
      set ::done 1
    }
    return "SQLITE_OK"
  }

  db eval {
    PRAGMA wal_checkpoint;
  }

  db close
}

# Give the [testfixture_nb] command time to start
after 1000 {set xyz 1}
vwait xyz

db timeout 500
set tm [lindex [time {
  catch {
    db eval BEGIN
      sqlite3_snapshot_open db main $::snap
  } msg
}] 0]

do_test 1.2 { set ::msg } {SQLITE_BUSY}
do_test 1.3.($::tm) { expr $::tm<2000000 } 1

do_execsql_test 1.4 {
  SELECT * FROM t1
} {1 2 3 4 5 6 7 8}

sqlite3_snapshot_free $::snap

vwait myvar

# All SQLite builds should pass the tests above. SQLITE_ENABLE_SETLK_TIMEOUT=1
# builds do so without calling the VFS xSleep method.
if {$::sqlite_options(setlk_timeout)==1} {
  do_test 1.5.1 {
    set ::sleep_count
  } 0
} else {
  do_test 1.5.2 {
    expr $::sleep_count>0
  } 1
}

finish_test

Changes to tool/lemon.c.
5921
5922
5923
5924
5925
5926
5927
5928
5929
5930
5931
5932
5933
5934
5935
  return array;
}

/* Hash a configuration */
PRIVATE unsigned confighash(struct config *a)
{
  unsigned h=0;
  h = h*571 + a->rp->index*37 + a->dot;
  return h;
}

/* There is one instance of the following structure for each
** associative array of type "x4".
*/
struct s_x4 {







|







5921
5922
5923
5924
5925
5926
5927
5928
5929
5930
5931
5932
5933
5934
5935
  return array;
}

/* Hash a configuration */
PRIVATE unsigned confighash(struct config *a)
{
  unsigned h=0;
  h = a->rp->index*37 + a->dot;
  return h;
}

/* There is one instance of the following structure for each
** associative array of type "x4".
*/
struct s_x4 {
Changes to tool/sqlite3_rsync.c.
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
          if( p->zDebugFile ) debugMessage(p, "-> ORIGIN_READY\n");
        }else{
          sqlite3_finalize(pCkHash);
          sqlite3_finalize(pCkHashN);
          sqlite3_finalize(pInsHash);
          pCkHash = 0;
          pInsHash = 0;
          if( mxHash<p->nPage ){
            runSql(p, "WITH RECURSIVE c(n) AS"
                      " (VALUES(%d) UNION ALL SELECT n+1 FROM c WHERE n<%d)"
                      " INSERT INTO badHash SELECT n, 1 FROM c",
                      mxHash, p->nPage);
          }
          runSql(p, "DELETE FROM badHash WHERE pgno=%d", lockBytePage);
          pStmt = prepareStmt(p,







|







1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
          if( p->zDebugFile ) debugMessage(p, "-> ORIGIN_READY\n");
        }else{
          sqlite3_finalize(pCkHash);
          sqlite3_finalize(pCkHashN);
          sqlite3_finalize(pInsHash);
          pCkHash = 0;
          pInsHash = 0;
          if( mxHash<=p->nPage ){
            runSql(p, "WITH RECURSIVE c(n) AS"
                      " (VALUES(%d) UNION ALL SELECT n+1 FROM c WHERE n<%d)"
                      " INSERT INTO badHash SELECT n, 1 FROM c",
                      mxHash, p->nPage);
          }
          runSql(p, "DELETE FROM badHash WHERE pgno=%d", lockBytePage);
          pStmt = prepareStmt(p,
1793
1794
1795
1796
1797
1798
1799

1800
1801
1802
1803
1804
1805
1806
        rc = sqlite3_open(":memory:", &p->db);
        if( rc ){
          reportError(p, "cannot open in-memory database: %s",
                      sqlite3_errmsg(p->db));
          closeDb(p);
          break;
        }

        runSql(p, "ATTACH %Q AS 'replica'", p->zReplica);
        if( p->wrongEncoding ){
          p->wrongEncoding = 0;
          runSql(p, "PRAGMA encoding=utf16le");
          runSql(p, "ATTACH %Q AS 'replica'", p->zReplica);
          if( p->wrongEncoding ){
            p->wrongEncoding = 0;







>







1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
        rc = sqlite3_open(":memory:", &p->db);
        if( rc ){
          reportError(p, "cannot open in-memory database: %s",
                      sqlite3_errmsg(p->db));
          closeDb(p);
          break;
        }
        sqlite3_db_config(p->db, SQLITE_DBCONFIG_WRITABLE_SCHEMA, 1, 0);
        runSql(p, "ATTACH %Q AS 'replica'", p->zReplica);
        if( p->wrongEncoding ){
          p->wrongEncoding = 0;
          runSql(p, "PRAGMA encoding=utf16le");
          runSql(p, "ATTACH %Q AS 'replica'", p->zReplica);
          if( p->wrongEncoding ){
            p->wrongEncoding = 0;