xref: /sqlite-3.40.0/test/tclsqlite.test (revision cbf1c8c2)
1# 2001 September 15
2#
3# The author disclaims copyright to this source code.  In place of
4# a legal notice, here is a blessing:
5#
6#    May you do good and not evil.
7#    May you find forgiveness for yourself and forgive others.
8#    May you share freely, never taking more than you give.
9#
10#***********************************************************************
11# This file implements regression tests for TCL interface to the
12# SQLite library.
13#
14# Actually, all tests are based on the TCL interface, so the main
15# interface is pretty well tested.  This file contains some addition
16# tests for fringe issues that the main test suite does not cover.
17#
18# $Id: tclsqlite.test,v 1.73 2009/03/16 13:19:36 danielk1977 Exp $
19
20catch {sqlite3}
21
22set testdir [file dirname $argv0]
23source $testdir/tester.tcl
24set testprefix tcl
25
26# Check the error messages generated by tclsqlite
27#
28set r "sqlite_orig HANDLE ?FILENAME? ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN? ?-nofollow BOOLEAN? ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN? ?-uri BOOLEAN?"
29if {[sqlite3 -has-codec]} {
30  append r " ?-key CODECKEY?"
31}
32do_test tcl-1.1 {
33  set v [catch {sqlite3 -bogus} msg]
34  regsub {really_sqlite3} $msg {sqlite3} msg
35  lappend v $msg
36} [list 1 "wrong # args: should be \"$r\""]
37do_test tcl-1.1.1 {
38  set v [catch {sqlite3} msg]
39  regsub {really_sqlite3} $msg {sqlite3} msg
40  lappend v $msg
41} [list 1 "wrong # args: should be \"$r\""]
42do_test tcl-1.2 {
43  set v [catch {db bogus} msg]
44  lappend v $msg
45} {1 {bad option "bogus": must be authorizer, backup, bind_fallback, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, config, copy, deserialize, enable_load_extension, errorcode, eval, exists, function, incrblob, 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, or wal_hook}}
46do_test tcl-1.2.1 {
47  set v [catch {db cache bogus} msg]
48  lappend v $msg
49} {1 {bad option "bogus": must be flush or size}}
50do_test tcl-1.2.2 {
51  set v [catch {db cache} msg]
52  lappend v $msg
53} {1 {wrong # args: should be "db cache option ?arg?"}}
54do_test tcl-1.3 {
55  execsql {CREATE TABLE t1(a int, b int)}
56  execsql {INSERT INTO t1 VALUES(10,20)}
57  set v [catch {
58    db eval {SELECT * FROM t1} data {
59      error "The error message"
60    }
61  } msg]
62  lappend v $msg
63} {1 {The error message}}
64do_test tcl-1.4 {
65  set v [catch {
66    db eval {SELECT * FROM t2} data {
67      error "The error message"
68    }
69  } msg]
70  lappend v $msg
71} {1 {no such table: t2}}
72do_test tcl-1.5 {
73  set v [catch {
74    db eval {SELECT * FROM t1} data {
75      break
76    }
77  } msg]
78  lappend v $msg
79} {0 {}}
80catch {expr x*} msg
81do_test tcl-1.6 {
82  set v [catch {
83    db eval {SELECT * FROM t1} data {
84      expr x*
85    }
86  } msg]
87  lappend v $msg
88} [list 1 $msg]
89do_test tcl-1.7 {
90  set v [catch {db} msg]
91  lappend v $msg
92} {1 {wrong # args: should be "db SUBCOMMAND ..."}}
93if {[catch {db auth {}}]==0} {
94  do_test tcl-1.8 {
95    set v [catch {db authorizer 1 2 3} msg]
96    lappend v $msg
97  } {1 {wrong # args: should be "db authorizer ?CALLBACK?"}}
98}
99do_test tcl-1.9 {
100  set v [catch {db busy 1 2 3} msg]
101  lappend v $msg
102} {1 {wrong # args: should be "db busy CALLBACK"}}
103do_test tcl-1.10 {
104  set v [catch {db progress 1} msg]
105  lappend v $msg
106} {1 {wrong # args: should be "db progress N CALLBACK"}}
107do_test tcl-1.11 {
108  set v [catch {db changes xyz} msg]
109  lappend v $msg
110} {1 {wrong # args: should be "db changes "}}
111do_test tcl-1.12 {
112  set v [catch {db commit_hook a b c} msg]
113  lappend v $msg
114} {1 {wrong # args: should be "db commit_hook ?CALLBACK?"}}
115ifcapable {complete} {
116  do_test tcl-1.13 {
117    set v [catch {db complete} msg]
118    lappend v $msg
119  } {1 {wrong # args: should be "db complete SQL"}}
120}
121do_test tcl-1.14 {
122  set v [catch {db eval} msg]
123  lappend v $msg
124} {1 {wrong # args: should be "db eval ?OPTIONS? SQL ?ARRAY-NAME? ?SCRIPT?"}}
125do_test tcl-1.15 {
126  set v [catch {db function} msg]
127  lappend v $msg
128} {1 {wrong # args: should be "db function NAME ?SWITCHES? SCRIPT"}}
129do_test tcl-1.16 {
130  set v [catch {db last_insert_rowid xyz} msg]
131  lappend v $msg
132} {1 {wrong # args: should be "db last_insert_rowid "}}
133do_test tcl-1.17 {
134  set v [catch {db rekey} msg]
135  lappend v $msg
136} {1 {wrong # args: should be "db rekey KEY"}}
137do_test tcl-1.18 {
138  set v [catch {db timeout} msg]
139  lappend v $msg
140} {1 {wrong # args: should be "db timeout MILLISECONDS"}}
141do_test tcl-1.19 {
142  set v [catch {db collate} msg]
143  lappend v $msg
144} {1 {wrong # args: should be "db collate NAME SCRIPT"}}
145do_test tcl-1.20 {
146  set v [catch {db collation_needed} msg]
147  lappend v $msg
148} {1 {wrong # args: should be "db collation_needed SCRIPT"}}
149do_test tcl-1.21 {
150  set v [catch {db total_changes xyz} msg]
151  lappend v $msg
152} {1 {wrong # args: should be "db total_changes "}}
153do_test tcl-1.22 {
154  set v [catch {db copy} msg]
155  lappend v $msg
156} {1 {wrong # args: should be "db copy CONFLICT-ALGORITHM TABLE FILENAME ?SEPARATOR? ?NULLINDICATOR?"}}
157do_test tcl-1.23 {
158  set v [catch {sqlite3 db2 test.db -vfs nosuchvfs} msg]
159  lappend v $msg
160} {1 {no such vfs: nosuchvfs}}
161
162catch {unset ::result}
163do_test tcl-2.1 {
164  execsql "CREATE TABLE t\u0123x(a int, b\u1235 float)"
165} {}
166ifcapable schema_pragmas {
167  do_test tcl-2.2 {
168    execsql "PRAGMA table_info(t\u0123x)"
169  } "0 a int 0 {} 0 1 b\u1235 float 0 {} 0"
170}
171do_test tcl-2.3 {
172  execsql "INSERT INTO t\u0123x VALUES(1,2.3)"
173  db eval "SELECT * FROM t\u0123x" result break
174  set result(*)
175} "a b\u1235"
176
177
178# Test the onecolumn method
179#
180do_test tcl-3.1 {
181  execsql {
182    INSERT INTO t1 SELECT a*2, b*2 FROM t1;
183    INSERT INTO t1 SELECT a*2+1, b*2+1 FROM t1;
184    INSERT INTO t1 SELECT a*2+3, b*2+3 FROM t1;
185  }
186  set rc [catch {db onecolumn {SELECT * FROM t1 ORDER BY a}} msg]
187  lappend rc $msg
188} {0 10}
189do_test tcl-3.2 {
190  db onecolumn {SELECT * FROM t1 WHERE a<0}
191} {}
192do_test tcl-3.3 {
193  set rc [catch {db onecolumn} errmsg]
194  lappend rc $errmsg
195} {1 {wrong # args: should be "db onecolumn SQL"}}
196do_test tcl-3.4 {
197  set rc [catch {db onecolumn {SELECT bogus}} errmsg]
198  lappend rc $errmsg
199} {1 {no such column: bogus}}
200ifcapable {tclvar} {
201  do_test tcl-3.5 {
202    set b 50
203    set rc [catch {db one {SELECT * FROM t1 WHERE b>$b}} msg]
204    lappend rc $msg
205  } {0 41}
206  do_test tcl-3.6 {
207    set b 500
208    set rc [catch {db one {SELECT * FROM t1 WHERE b>$b}} msg]
209    lappend rc $msg
210  } {0 {}}
211  do_test tcl-3.7 {
212    set b 500
213    set rc [catch {db one {
214      INSERT INTO t1 VALUES(99,510);
215      SELECT * FROM t1 WHERE b>$b
216    }} msg]
217    lappend rc $msg
218  } {0 99}
219}
220ifcapable {!tclvar} {
221   execsql {INSERT INTO t1 VALUES(99,510)}
222}
223
224# Turn the busy handler on and off
225#
226do_test tcl-4.1 {
227  proc busy_callback {cnt} {
228    break
229  }
230  db busy busy_callback
231  db busy
232} {busy_callback}
233do_test tcl-4.2 {
234  db busy {}
235  db busy
236} {}
237
238ifcapable {tclvar} {
239  # Parsing of TCL variable names within SQL into bound parameters.
240  #
241  do_test tcl-5.1 {
242    execsql {CREATE TABLE t3(a,b,c)}
243    catch {unset x}
244    set x(1) A
245    set x(2) B
246    execsql {
247      INSERT INTO t3 VALUES($::x(1),$::x(2),$::x(3));
248      SELECT * FROM t3
249    }
250  } {A B {}}
251  do_test tcl-5.2 {
252    execsql {
253      SELECT typeof(a), typeof(b), typeof(c) FROM t3
254    }
255  } {text text null}
256  do_test tcl-5.3 {
257    catch {unset x}
258    set x [binary format h12 686900686f00]
259    execsql {
260      UPDATE t3 SET a=$::x;
261    }
262    db eval {
263      SELECT a FROM t3
264    } break
265    binary scan $a h12 adata
266    set adata
267  } {686900686f00}
268  do_test tcl-5.4 {
269    execsql {
270      SELECT typeof(a), typeof(b), typeof(c) FROM t3
271    }
272  } {blob text null}
273}
274
275# Operation of "break" and "continue" within row scripts
276#
277do_test tcl-6.1 {
278  db eval {SELECT * FROM t1} {
279    break
280  }
281  lappend a $b
282} {10 20}
283do_test tcl-6.2 {
284  set cnt 0
285  db eval {SELECT * FROM t1} {
286    if {$a>40} continue
287    incr cnt
288  }
289  set cnt
290} {4}
291do_test tcl-6.3 {
292  set cnt 0
293  db eval {SELECT * FROM t1} {
294    if {$a<40} continue
295    incr cnt
296  }
297  set cnt
298} {5}
299do_test tcl-6.4 {
300  proc return_test {x} {
301    db eval {SELECT * FROM t1} {
302      if {$a==$x} {return $b}
303    }
304  }
305  return_test 10
306} 20
307do_test tcl-6.5 {
308  return_test 20
309} 40
310do_test tcl-6.6 {
311  return_test 99
312} 510
313do_test tcl-6.7 {
314  return_test 0
315} {}
316
317do_test tcl-7.1 {
318  db version
319  expr 0
320} {0}
321
322# modify and reset the NULL representation
323#
324do_test tcl-8.1 {
325  db nullvalue NaN
326  execsql {INSERT INTO t1 VALUES(30,NULL)}
327  db eval {SELECT * FROM t1 WHERE b IS NULL}
328} {30 NaN}
329proc concatFunc args {return [join $args {}]}
330do_test tcl-8.2 {
331  db function concat concatFunc
332  db eval {SELECT concat('a', b, 'z') FROM t1 WHERE b is NULL}
333} {aNaNz}
334do_test tcl-8.3 {
335  db nullvalue NULL
336  db nullvalue
337} {NULL}
338do_test tcl-8.4 {
339  db nullvalue {}
340  db eval {SELECT * FROM t1 WHERE b IS NULL}
341} {30 {}}
342do_test tcl-8.5 {
343  db function concat concatFunc
344  db eval {SELECT concat('a', b, 'z') FROM t1 WHERE b is NULL}
345} {az}
346
347# Test the return type of user-defined functions
348#
349do_test tcl-9.1 {
350  db function ret_str {return "hi"}
351  execsql {SELECT typeof(ret_str())}
352} {text}
353do_test tcl-9.2 {
354  db function ret_dbl {return [expr {rand()*0.5}]}
355  execsql {SELECT typeof(ret_dbl())}
356} {real}
357do_test tcl-9.3 {
358  db function ret_int {return [expr {int(rand()*200)}]}
359  execsql {SELECT typeof(ret_int())}
360} {integer}
361
362# Recursive calls to the same user-defined function
363#
364ifcapable tclvar {
365  do_test tcl-9.10 {
366    proc userfunc_r1 {n} {
367      if {$n<=0} {return 0}
368      set nm1 [expr {$n-1}]
369      return [expr {[db eval {SELECT r1($nm1)}]+$n}]
370    }
371    db function r1 userfunc_r1
372    execsql {SELECT r1(10)}
373  } {55}
374  # Fails under -fsanitize=address,undefined due to stack overflow
375  # do_test tcl-9.11 {
376  #   execsql {SELECT r1(100)}
377  # } {5050}
378}
379
380# Tests for the new transaction method
381#
382do_test tcl-10.1 {
383  db transaction {}
384} {}
385do_test tcl-10.2 {
386  db transaction deferred {}
387} {}
388do_test tcl-10.3 {
389  db transaction immediate {}
390} {}
391do_test tcl-10.4 {
392  db transaction exclusive {}
393} {}
394do_test tcl-10.5 {
395  set rc [catch {db transaction xyzzy {}} msg]
396  lappend rc $msg
397} {1 {bad transaction type "xyzzy": must be deferred, exclusive, or immediate}}
398do_test tcl-10.6 {
399  set rc [catch {db transaction {error test-error}} msg]
400  lappend rc $msg
401} {1 test-error}
402do_test tcl-10.7 {
403  db transaction {
404    db eval {CREATE TABLE t4(x)}
405    db transaction {
406      db eval {INSERT INTO t4 VALUES(1)}
407    }
408  }
409  db eval {SELECT * FROM t4}
410} 1
411do_test tcl-10.8 {
412  catch {
413    db transaction {
414      db eval {INSERT INTO t4 VALUES(2)}
415      db eval {INSERT INTO t4 VALUES(3)}
416      db eval {INSERT INTO t4 VALUES(4)}
417      error test-error
418    }
419  }
420  db eval {SELECT * FROM t4}
421} 1
422do_test tcl-10.9 {
423  db transaction {
424    db eval {INSERT INTO t4 VALUES(2)}
425    catch {
426      db transaction {
427        db eval {INSERT INTO t4 VALUES(3)}
428        db eval {INSERT INTO t4 VALUES(4)}
429        error test-error
430      }
431    }
432  }
433  db eval {SELECT * FROM t4}
434} {1 2}
435do_test tcl-10.10 {
436  for {set i 0} {$i<1} {incr i} {
437    db transaction {
438      db eval {INSERT INTO t4 VALUES(5)}
439      continue
440    }
441    error "This line should not be run"
442  }
443  db eval {SELECT * FROM t4}
444} {1 2 5}
445do_test tcl-10.11 {
446  for {set i 0} {$i<10} {incr i} {
447    db transaction {
448      db eval {INSERT INTO t4 VALUES(6)}
449      break
450    }
451  }
452  db eval {SELECT * FROM t4}
453} {1 2 5 6}
454do_test tcl-10.12 {
455  set rc [catch {
456    for {set i 0} {$i<10} {incr i} {
457      db transaction {
458        db eval {INSERT INTO t4 VALUES(7)}
459        return
460      }
461    }
462  }]
463} {2}
464do_test tcl-10.13 {
465  db eval {SELECT * FROM t4}
466} {1 2 5 6 7}
467
468# Now test that [db transaction] commands may be nested with
469# the expected results.
470#
471do_test tcl-10.14 {
472  db transaction {
473    db eval {
474      DELETE FROM t4;
475      INSERT INTO t4 VALUES('one');
476    }
477
478    catch {
479      db transaction {
480        db eval { INSERT INTO t4 VALUES('two') }
481        db transaction {
482          db eval { INSERT INTO t4 VALUES('three') }
483          error "throw an error!"
484        }
485      }
486    }
487  }
488
489  db eval {SELECT * FROM t4}
490} {one}
491do_test tcl-10.15 {
492  # Make sure a transaction has not been left open.
493  db eval {BEGIN ; COMMIT}
494} {}
495do_test tcl-10.16 {
496  db transaction {
497    db eval { INSERT INTO t4 VALUES('two'); }
498    db transaction {
499      db eval { INSERT INTO t4 VALUES('three') }
500      db transaction {
501        db eval { INSERT INTO t4 VALUES('four') }
502      }
503    }
504  }
505  db eval {SELECT * FROM t4}
506} {one two three four}
507do_test tcl-10.17 {
508  catch {
509    db transaction {
510      db eval { INSERT INTO t4 VALUES('A'); }
511      db transaction {
512        db eval { INSERT INTO t4 VALUES('B') }
513        db transaction {
514          db eval { INSERT INTO t4 VALUES('C') }
515          error "throw an error!"
516        }
517      }
518    }
519  }
520  db eval {SELECT * FROM t4}
521} {one two three four}
522do_test tcl-10.18 {
523  # Make sure a transaction has not been left open.
524  db eval {BEGIN ; COMMIT}
525} {}
526
527# Mess up a [db transaction] command by locking the database using a
528# second connection when it tries to commit. Make sure the transaction
529# is not still open after the "database is locked" exception is thrown.
530#
531do_test tcl-10.18 {
532  sqlite3 db2 test.db
533  db2 eval {
534    BEGIN;
535    SELECT * FROM sqlite_master;
536  }
537
538  set rc [catch {
539    db transaction {
540      db eval {INSERT INTO t4 VALUES('five')}
541    }
542  } msg]
543  list $rc $msg
544} {1 {database is locked}}
545do_test tcl-10.19 {
546  db eval {BEGIN ; COMMIT}
547} {}
548
549# Thwart a [db transaction] command by locking the database using a
550# second connection with "BEGIN EXCLUSIVE". Make sure no transaction is
551# open after the "database is locked" exception is thrown.
552#
553do_test tcl-10.20 {
554  db2 eval {
555    COMMIT;
556    BEGIN EXCLUSIVE;
557  }
558  set rc [catch {
559    db transaction {
560      db eval {INSERT INTO t4 VALUES('five')}
561    }
562  } msg]
563  list $rc $msg
564} {1 {database is locked}}
565do_test tcl-10.21 {
566  db2 close
567  db eval {BEGIN ; COMMIT}
568} {}
569do_test tcl-10.22 {
570  sqlite3 db2 test.db
571  db transaction exclusive {
572    catch { db2 eval {SELECT * FROM sqlite_master} } msg
573    set msg "db2: $msg"
574  }
575  set msg
576} {db2: database is locked}
577db2 close
578
579do_test tcl-11.1 {
580  db eval {INSERT INTO t4 VALUES(6)}
581  db exists {SELECT x,x*2,x+x FROM t4 WHERE x==6}
582} {1}
583do_test tcl-11.2 {
584  db exists {SELECT 0 FROM t4 WHERE x==6}
585} {1}
586do_test tcl-11.3 {
587  db exists {SELECT 1 FROM t4 WHERE x==8}
588} {0}
589do_test tcl-11.3.1 {
590  tcl_objproc db exists {SELECT 1 FROM t4 WHERE x==8}
591} {0}
592
593do_test tcl-12.1 {
594  unset -nocomplain a b c version
595  set version [db version]
596  scan $version "%d.%d.%d" a b c
597  expr $a*1000000 + $b*1000 + $c
598} [sqlite3_libversion_number]
599
600
601# Check to see that when bindings of the form @aaa are used instead
602# of $aaa, that objects are treated as bytearray and are inserted
603# as BLOBs.
604#
605ifcapable tclvar {
606  do_test tcl-13.1 {
607    db eval {CREATE TABLE t5(x BLOB)}
608    set x abc123
609    db eval {INSERT INTO t5 VALUES($x)}
610    db eval {SELECT typeof(x) FROM t5}
611  } {text}
612  do_test tcl-13.2 {
613    binary scan $x H notUsed
614    db eval {
615      DELETE FROM t5;
616      INSERT INTO t5 VALUES($x);
617      SELECT typeof(x) FROM t5;
618    }
619  } {text}
620  do_test tcl-13.3 {
621    db eval {
622      DELETE FROM t5;
623      INSERT INTO t5 VALUES(@x);
624      SELECT typeof(x) FROM t5;
625    }
626  } {blob}
627  do_test tcl-13.4 {
628    set y 1234
629    db eval {
630      DELETE FROM t5;
631      INSERT INTO t5 VALUES(@y);
632      SELECT hex(x), typeof(x) FROM t5
633    }
634  } {31323334 blob}
635}
636
637db func xCall xCall
638proc xCall {} { return "value" }
639do_execsql_test tcl-14.1 {
640  CREATE TABLE t6(x);
641  INSERT INTO t6 VALUES(1);
642}
643do_test tcl-14.2 {
644  db one {SELECT x FROM t6 WHERE xCall()!='value'}
645} {}
646
647# Verify that the "exists" and "onecolumn" methods work when
648# a "profile" is registered.
649#
650catch {db close}
651sqlite3 db :memory:
652proc noop-profile {args} {
653  return
654}
655do_test tcl-15.0 {
656  db eval {CREATE TABLE t1(a); INSERT INTO t1 VALUES(1),(2),(3);}
657  db onecolumn {SELECT a FROM t1 WHERE a>2}
658} {3}
659do_test tcl-15.1 {
660  db exists {SELECT a FROM t1 WHERE a>2}
661} {1}
662do_test tcl-15.2 {
663  db exists {SELECT a FROM t1 WHERE a>3}
664} {0}
665db profile noop-profile
666do_test tcl-15.3 {
667  db onecolumn {SELECT a FROM t1 WHERE a>2}
668} {3}
669do_test tcl-15.4 {
670  db exists {SELECT a FROM t1 WHERE a>2}
671} {1}
672do_test tcl-15.5 {
673  db exists {SELECT a FROM t1 WHERE a>3}
674} {0}
675
676
677# 2017-06-26: The --withoutnulls flag to "db eval".
678#
679# In the "db eval --withoutnulls SQL ARRAY" form, NULL results cause the
680# corresponding array entry to be unset.  The default behavior (without
681# the -withoutnulls flags) is for the corresponding array value to get
682# the [db nullvalue] string.
683#
684catch {db close}
685forcedelete test.db
686sqlite3 db test.db
687do_execsql_test tcl-16.100 {
688  CREATE TABLE t1(a,b);
689  INSERT INTO t1 VALUES(1,2),(2,NULL),(3,'xyz');
690}
691do_test tcl-16.101 {
692  set res {}
693  unset -nocomplain x
694  db eval {SELECT * FROM t1} x {
695    lappend res $x(a) [array names x]
696  }
697  set res
698} {1 {a b *} 2 {a b *} 3 {a b *}}
699do_test tcl-16.102 {
700  set res [catch {
701    db eval -unknown {SELECT * FROM t1} x {
702      lappend res $x(a) [array names x]
703    }
704  } rc]
705  lappend res $rc
706} {1 {unknown option: "-unknown"}}
707do_test tcl-16.103 {
708  set res {}
709  unset -nocomplain x
710  db eval -withoutnulls {SELECT * FROM t1} x {
711    lappend res $x(a) [array names x]
712  }
713  set res
714} {1 {a b *} 2 {a *} 3 {a b *}}
715
716#-------------------------------------------------------------------------
717# Test the -type option to [db function].
718#
719reset_db
720proc add {a b} { return [expr $a + $b] }
721proc ret {a} { return $a }
722
723db function add_i -returntype integer add
724db function add_r -ret        real    add
725db function add_t -return     text    add
726db function add_b -returntype blob    add
727db function add_a -returntype any     add
728
729db function ret_i -returntype int     ret
730db function ret_r -returntype real    ret
731db function ret_t -returntype text    ret
732db function ret_b -returntype blob    ret
733db function ret_a -r          any     ret
734
735do_execsql_test 17.0 {
736  SELECT quote( add_i(2, 3) );
737  SELECT quote( add_r(2, 3) );
738  SELECT quote( add_t(2, 3) );
739  SELECT quote( add_b(2, 3) );
740  SELECT quote( add_a(2, 3) );
741} {5 5.0 '5' X'35' 5}
742
743do_execsql_test 17.1 {
744  SELECT quote( add_i(2.2, 3.3) );
745  SELECT quote( add_r(2.2, 3.3) );
746  SELECT quote( add_t(2.2, 3.3) );
747  SELECT quote( add_b(2.2, 3.3) );
748  SELECT quote( add_a(2.2, 3.3) );
749} {5.5 5.5 '5.5' X'352E35' 5.5}
750
751do_execsql_test 17.2 {
752  SELECT quote( ret_i(2.5) );
753  SELECT quote( ret_r(2.5) );
754  SELECT quote( ret_t(2.5) );
755  SELECT quote( ret_b(2.5) );
756  SELECT quote( ret_a(2.5) );
757} {2.5 2.5 '2.5' X'322E35' 2.5}
758
759do_execsql_test 17.3 {
760  SELECT quote( ret_i('2.5') );
761  SELECT quote( ret_r('2.5') );
762  SELECT quote( ret_t('2.5') );
763  SELECT quote( ret_b('2.5') );
764  SELECT quote( ret_a('2.5') );
765} {2.5 2.5 '2.5' X'322E35' '2.5'}
766
767do_execsql_test 17.4 {
768  SELECT quote( ret_i('abc') );
769  SELECT quote( ret_r('abc') );
770  SELECT quote( ret_t('abc') );
771  SELECT quote( ret_b('abc') );
772  SELECT quote( ret_a('abc') );
773} {'abc' 'abc' 'abc' X'616263' 'abc'}
774
775do_execsql_test 17.5 {
776  SELECT quote( ret_i(X'616263') );
777  SELECT quote( ret_r(X'616263') );
778  SELECT quote( ret_t(X'616263') );
779  SELECT quote( ret_b(X'616263') );
780  SELECT quote( ret_a(X'616263') );
781} {'abc' 'abc' 'abc' X'616263' X'616263'}
782
783do_test 17.6.1 {
784  list [catch { db function xyz -return object ret } msg] $msg
785} {1 {bad type "object": must be integer, real, text, blob, or any}}
786
787do_test 17.6.2 {
788  list [catch { db function xyz -return ret } msg] $msg
789} {1 {option requires an argument: -return}}
790
791do_test 17.6.3 {
792  list [catch { db function xyz -n object ret } msg] $msg
793} {1 {bad option "-n": must be -argcount, -deterministic, -directonly, -innocuous, or -returntype}}
794
795# 2019-02-28: The "bind_fallback" command.
796#
797do_test 18.100 {
798  unset -nocomplain bindings abc def ghi jkl mno e01 e02
799  set bindings(abc) [expr {1+2}]
800  set bindings(def) {hello}
801  set bindings(ghi) [expr {3.1415926*1.0}]
802  proc bind_callback {nm} {
803    global bindings
804    set n2 [string range $nm 1 end]
805    if {[info exists bindings($n2)]} {
806      return $bindings($n2)
807    }
808    if {[string match e* $n2]} {
809      error "no such variable: $nm"
810    }
811    return -code return {}
812  }
813  db bind_fallback bind_callback
814  db eval {SELECT $abc, typeof($abc), $def, typeof($def), $ghi, typeof($ghi)}
815} {3 integer hello text 3.1415926 real}
816do_test 18.110 {
817  db eval {SELECT quote(@def), typeof(@def)}
818} {X'68656C6C6F' blob}
819do_execsql_test 18.120 {
820  SELECT typeof($mno);
821} {null}
822do_catchsql_test 18.130 {
823  SELECT $e01;
824} {1 {no such variable: $e01}}
825do_test 18.140 {
826  db bind_fallback
827} {bind_callback}
828do_test 18.200 {
829  db bind_fallback {}
830  db eval {SELECT $abc, typeof($abc), $def, typeof($def), $ghi, typeof($ghi)}
831} {{} null {} null {} null}
832do_test 18.300 {
833  unset -nocomplain bindings
834  proc bind_callback {nm} {lappend ::bindings $nm}
835  db bind_fallback bind_callback
836  db eval {SELECT $abc, @def, $ghi(123), :mno}
837  set bindings
838} {{$abc} @def {$ghi(123)} :mno}
839do_test 18.900 {
840  set rc [catch {db bind_fallback a b} msg]
841  lappend rc $msg
842} {1 {wrong # args: should be "db bind_fallback ?CALLBACK?"}}
843do_test 18.910 {
844  db bind_fallback bind_fallback_does_not_exist
845} {}
846do_catchsql_test 19.911 {
847  SELECT $abc, typeof($abc), $def, typeof($def), $ghi, typeof($ghi);
848} {1 {invalid command name "bind_fallback_does_not_exist"}}
849db bind_fallback {}
850
851finish_test
852