xref: /sqlite-3.40.0/test/tclsqlite.test (revision 7ac2ee0a)
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? ?-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, 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  do_test tcl-9.11 {
375    execsql {SELECT r1(100)}
376  } {5050}
377}
378
379# Tests for the new transaction method
380#
381do_test tcl-10.1 {
382  db transaction {}
383} {}
384do_test tcl-10.2 {
385  db transaction deferred {}
386} {}
387do_test tcl-10.3 {
388  db transaction immediate {}
389} {}
390do_test tcl-10.4 {
391  db transaction exclusive {}
392} {}
393do_test tcl-10.5 {
394  set rc [catch {db transaction xyzzy {}} msg]
395  lappend rc $msg
396} {1 {bad transaction type "xyzzy": must be deferred, exclusive, or immediate}}
397do_test tcl-10.6 {
398  set rc [catch {db transaction {error test-error}} msg]
399  lappend rc $msg
400} {1 test-error}
401do_test tcl-10.7 {
402  db transaction {
403    db eval {CREATE TABLE t4(x)}
404    db transaction {
405      db eval {INSERT INTO t4 VALUES(1)}
406    }
407  }
408  db eval {SELECT * FROM t4}
409} 1
410do_test tcl-10.8 {
411  catch {
412    db transaction {
413      db eval {INSERT INTO t4 VALUES(2)}
414      db eval {INSERT INTO t4 VALUES(3)}
415      db eval {INSERT INTO t4 VALUES(4)}
416      error test-error
417    }
418  }
419  db eval {SELECT * FROM t4}
420} 1
421do_test tcl-10.9 {
422  db transaction {
423    db eval {INSERT INTO t4 VALUES(2)}
424    catch {
425      db transaction {
426        db eval {INSERT INTO t4 VALUES(3)}
427        db eval {INSERT INTO t4 VALUES(4)}
428        error test-error
429      }
430    }
431  }
432  db eval {SELECT * FROM t4}
433} {1 2}
434do_test tcl-10.10 {
435  for {set i 0} {$i<1} {incr i} {
436    db transaction {
437      db eval {INSERT INTO t4 VALUES(5)}
438      continue
439    }
440    error "This line should not be run"
441  }
442  db eval {SELECT * FROM t4}
443} {1 2 5}
444do_test tcl-10.11 {
445  for {set i 0} {$i<10} {incr i} {
446    db transaction {
447      db eval {INSERT INTO t4 VALUES(6)}
448      break
449    }
450  }
451  db eval {SELECT * FROM t4}
452} {1 2 5 6}
453do_test tcl-10.12 {
454  set rc [catch {
455    for {set i 0} {$i<10} {incr i} {
456      db transaction {
457        db eval {INSERT INTO t4 VALUES(7)}
458        return
459      }
460    }
461  }]
462} {2}
463do_test tcl-10.13 {
464  db eval {SELECT * FROM t4}
465} {1 2 5 6 7}
466
467# Now test that [db transaction] commands may be nested with
468# the expected results.
469#
470do_test tcl-10.14 {
471  db transaction {
472    db eval {
473      DELETE FROM t4;
474      INSERT INTO t4 VALUES('one');
475    }
476
477    catch {
478      db transaction {
479        db eval { INSERT INTO t4 VALUES('two') }
480        db transaction {
481          db eval { INSERT INTO t4 VALUES('three') }
482          error "throw an error!"
483        }
484      }
485    }
486  }
487
488  db eval {SELECT * FROM t4}
489} {one}
490do_test tcl-10.15 {
491  # Make sure a transaction has not been left open.
492  db eval {BEGIN ; COMMIT}
493} {}
494do_test tcl-10.16 {
495  db transaction {
496    db eval { INSERT INTO t4 VALUES('two'); }
497    db transaction {
498      db eval { INSERT INTO t4 VALUES('three') }
499      db transaction {
500        db eval { INSERT INTO t4 VALUES('four') }
501      }
502    }
503  }
504  db eval {SELECT * FROM t4}
505} {one two three four}
506do_test tcl-10.17 {
507  catch {
508    db transaction {
509      db eval { INSERT INTO t4 VALUES('A'); }
510      db transaction {
511        db eval { INSERT INTO t4 VALUES('B') }
512        db transaction {
513          db eval { INSERT INTO t4 VALUES('C') }
514          error "throw an error!"
515        }
516      }
517    }
518  }
519  db eval {SELECT * FROM t4}
520} {one two three four}
521do_test tcl-10.18 {
522  # Make sure a transaction has not been left open.
523  db eval {BEGIN ; COMMIT}
524} {}
525
526# Mess up a [db transaction] command by locking the database using a
527# second connection when it tries to commit. Make sure the transaction
528# is not still open after the "database is locked" exception is thrown.
529#
530do_test tcl-10.18 {
531  sqlite3 db2 test.db
532  db2 eval {
533    BEGIN;
534    SELECT * FROM sqlite_master;
535  }
536
537  set rc [catch {
538    db transaction {
539      db eval {INSERT INTO t4 VALUES('five')}
540    }
541  } msg]
542  list $rc $msg
543} {1 {database is locked}}
544do_test tcl-10.19 {
545  db eval {BEGIN ; COMMIT}
546} {}
547
548# Thwart a [db transaction] command by locking the database using a
549# second connection with "BEGIN EXCLUSIVE". Make sure no transaction is
550# open after the "database is locked" exception is thrown.
551#
552do_test tcl-10.20 {
553  db2 eval {
554    COMMIT;
555    BEGIN EXCLUSIVE;
556  }
557  set rc [catch {
558    db transaction {
559      db eval {INSERT INTO t4 VALUES('five')}
560    }
561  } msg]
562  list $rc $msg
563} {1 {database is locked}}
564do_test tcl-10.21 {
565  db2 close
566  db eval {BEGIN ; COMMIT}
567} {}
568do_test tcl-10.22 {
569  sqlite3 db2 test.db
570  db transaction exclusive {
571    catch { db2 eval {SELECT * FROM sqlite_master} } msg
572    set msg "db2: $msg"
573  }
574  set msg
575} {db2: database is locked}
576db2 close
577
578do_test tcl-11.1 {
579  db eval {INSERT INTO t4 VALUES(6)}
580  db exists {SELECT x,x*2,x+x FROM t4 WHERE x==6}
581} {1}
582do_test tcl-11.2 {
583  db exists {SELECT 0 FROM t4 WHERE x==6}
584} {1}
585do_test tcl-11.3 {
586  db exists {SELECT 1 FROM t4 WHERE x==8}
587} {0}
588do_test tcl-11.3.1 {
589  tcl_objproc db exists {SELECT 1 FROM t4 WHERE x==8}
590} {0}
591
592do_test tcl-12.1 {
593  unset -nocomplain a b c version
594  set version [db version]
595  scan $version "%d.%d.%d" a b c
596  expr $a*1000000 + $b*1000 + $c
597} [sqlite3_libversion_number]
598
599
600# Check to see that when bindings of the form @aaa are used instead
601# of $aaa, that objects are treated as bytearray and are inserted
602# as BLOBs.
603#
604ifcapable tclvar {
605  do_test tcl-13.1 {
606    db eval {CREATE TABLE t5(x BLOB)}
607    set x abc123
608    db eval {INSERT INTO t5 VALUES($x)}
609    db eval {SELECT typeof(x) FROM t5}
610  } {text}
611  do_test tcl-13.2 {
612    binary scan $x H notUsed
613    db eval {
614      DELETE FROM t5;
615      INSERT INTO t5 VALUES($x);
616      SELECT typeof(x) FROM t5;
617    }
618  } {text}
619  do_test tcl-13.3 {
620    db eval {
621      DELETE FROM t5;
622      INSERT INTO t5 VALUES(@x);
623      SELECT typeof(x) FROM t5;
624    }
625  } {blob}
626  do_test tcl-13.4 {
627    set y 1234
628    db eval {
629      DELETE FROM t5;
630      INSERT INTO t5 VALUES(@y);
631      SELECT hex(x), typeof(x) FROM t5
632    }
633  } {31323334 blob}
634}
635
636db func xCall xCall
637proc xCall {} { return "value" }
638do_execsql_test tcl-14.1 {
639  CREATE TABLE t6(x);
640  INSERT INTO t6 VALUES(1);
641}
642do_test tcl-14.2 {
643  db one {SELECT x FROM t6 WHERE xCall()!='value'}
644} {}
645
646# Verify that the "exists" and "onecolumn" methods work when
647# a "profile" is registered.
648#
649catch {db close}
650sqlite3 db :memory:
651proc noop-profile {args} {
652  return
653}
654do_test tcl-15.0 {
655  db eval {CREATE TABLE t1(a); INSERT INTO t1 VALUES(1),(2),(3);}
656  db onecolumn {SELECT a FROM t1 WHERE a>2}
657} {3}
658do_test tcl-15.1 {
659  db exists {SELECT a FROM t1 WHERE a>2}
660} {1}
661do_test tcl-15.2 {
662  db exists {SELECT a FROM t1 WHERE a>3}
663} {0}
664db profile noop-profile
665do_test tcl-15.3 {
666  db onecolumn {SELECT a FROM t1 WHERE a>2}
667} {3}
668do_test tcl-15.4 {
669  db exists {SELECT a FROM t1 WHERE a>2}
670} {1}
671do_test tcl-15.5 {
672  db exists {SELECT a FROM t1 WHERE a>3}
673} {0}
674
675
676# 2017-06-26: The --withoutnulls flag to "db eval".
677#
678# In the "db eval --withoutnulls SQL ARRAY" form, NULL results cause the
679# corresponding array entry to be unset.  The default behavior (without
680# the -withoutnulls flags) is for the corresponding array value to get
681# the [db nullvalue] string.
682#
683catch {db close}
684forcedelete test.db
685sqlite3 db test.db
686do_execsql_test tcl-16.100 {
687  CREATE TABLE t1(a,b);
688  INSERT INTO t1 VALUES(1,2),(2,NULL),(3,'xyz');
689}
690do_test tcl-16.101 {
691  set res {}
692  unset -nocomplain x
693  db eval {SELECT * FROM t1} x {
694    lappend res $x(a) [array names x]
695  }
696  set res
697} {1 {a b *} 2 {a b *} 3 {a b *}}
698do_test tcl-16.102 {
699  set res [catch {
700    db eval -unknown {SELECT * FROM t1} x {
701      lappend res $x(a) [array names x]
702    }
703  } rc]
704  lappend res $rc
705} {1 {unknown option: "-unknown"}}
706do_test tcl-16.103 {
707  set res {}
708  unset -nocomplain x
709  db eval -withoutnulls {SELECT * FROM t1} x {
710    lappend res $x(a) [array names x]
711  }
712  set res
713} {1 {a b *} 2 {a *} 3 {a b *}}
714
715#-------------------------------------------------------------------------
716# Test the -type option to [db function].
717#
718reset_db
719proc add {a b} { return [expr $a + $b] }
720proc ret {a} { return $a }
721
722db function add_i -returntype integer add
723db function add_r -ret        real    add
724db function add_t -return     text    add
725db function add_b -returntype blob    add
726db function add_a -returntype any     add
727
728db function ret_i -returntype int     ret
729db function ret_r -returntype real    ret
730db function ret_t -returntype text    ret
731db function ret_b -returntype blob    ret
732db function ret_a -r          any     ret
733
734do_execsql_test 17.0 {
735  SELECT quote( add_i(2, 3) );
736  SELECT quote( add_r(2, 3) );
737  SELECT quote( add_t(2, 3) );
738  SELECT quote( add_b(2, 3) );
739  SELECT quote( add_a(2, 3) );
740} {5 5.0 '5' X'35' 5}
741
742do_execsql_test 17.1 {
743  SELECT quote( add_i(2.2, 3.3) );
744  SELECT quote( add_r(2.2, 3.3) );
745  SELECT quote( add_t(2.2, 3.3) );
746  SELECT quote( add_b(2.2, 3.3) );
747  SELECT quote( add_a(2.2, 3.3) );
748} {5.5 5.5 '5.5' X'352E35' 5.5}
749
750do_execsql_test 17.2 {
751  SELECT quote( ret_i(2.5) );
752  SELECT quote( ret_r(2.5) );
753  SELECT quote( ret_t(2.5) );
754  SELECT quote( ret_b(2.5) );
755  SELECT quote( ret_a(2.5) );
756} {2.5 2.5 '2.5' X'322E35' 2.5}
757
758do_execsql_test 17.3 {
759  SELECT quote( ret_i('2.5') );
760  SELECT quote( ret_r('2.5') );
761  SELECT quote( ret_t('2.5') );
762  SELECT quote( ret_b('2.5') );
763  SELECT quote( ret_a('2.5') );
764} {2.5 2.5 '2.5' X'322E35' '2.5'}
765
766do_execsql_test 17.4 {
767  SELECT quote( ret_i('abc') );
768  SELECT quote( ret_r('abc') );
769  SELECT quote( ret_t('abc') );
770  SELECT quote( ret_b('abc') );
771  SELECT quote( ret_a('abc') );
772} {'abc' 'abc' 'abc' X'616263' 'abc'}
773
774do_execsql_test 17.5 {
775  SELECT quote( ret_i(X'616263') );
776  SELECT quote( ret_r(X'616263') );
777  SELECT quote( ret_t(X'616263') );
778  SELECT quote( ret_b(X'616263') );
779  SELECT quote( ret_a(X'616263') );
780} {'abc' 'abc' 'abc' X'616263' X'616263'}
781
782do_test 17.6.1 {
783  list [catch { db function xyz -return object ret } msg] $msg
784} {1 {bad type "object": must be integer, real, text, blob, or any}}
785
786do_test 17.6.2 {
787  list [catch { db function xyz -return ret } msg] $msg
788} {1 {option requires an argument: -return}}
789
790do_test 17.6.3 {
791  list [catch { db function xyz -n object ret } msg] $msg
792} {1 {bad option "-n": must be -argcount, -deterministic or -returntype}}
793
794# 2019-02-28: The "bind_fallback" command.
795#
796do_test 18.100 {
797  unset -nocomplain bindings abc def ghi jkl mno e01 e02
798  set bindings(abc) [expr {1+2}]
799  set bindings(def) {hello}
800  set bindings(ghi) [expr {3.1415926*1.0}]
801  proc bind_callback {nm} {
802    global bindings
803    set n2 [string range $nm 1 end]
804    if {[info exists bindings($n2)]} {
805      return $bindings($n2)
806    }
807    if {[string match e* $n2]} {
808      error "no such variable: $nm"
809    }
810    return -code return {}
811  }
812  db bind_fallback bind_callback
813  db eval {SELECT $abc, typeof($abc), $def, typeof($def), $ghi, typeof($ghi)}
814} {3 integer hello text 3.1415926 real}
815do_test 18.110 {
816  db eval {SELECT quote(@def), typeof(@def)}
817} {X'68656C6C6F' blob}
818do_execsql_test 18.120 {
819  SELECT typeof($mno);
820} {null}
821do_catchsql_test 18.130 {
822  SELECT $e01;
823} {1 {no such variable: $e01}}
824do_test 18.140 {
825  db bind_fallback
826} {bind_callback}
827do_test 18.200 {
828  db bind_fallback {}
829  db eval {SELECT $abc, typeof($abc), $def, typeof($def), $ghi, typeof($ghi)}
830} {{} null {} null {} null}
831do_test 18.300 {
832  unset -nocomplain bindings
833  proc bind_callback {nm} {lappend ::bindings $nm}
834  db bind_fallback bind_callback
835  db eval {SELECT $abc, @def, $ghi(123), :mno}
836  set bindings
837} {{$abc} @def {$ghi(123)} :mno}
838do_test 18.900 {
839  set rc [catch {db bind_fallback a b} msg]
840  lappend rc $msg
841} {1 {wrong # args: should be "db bind_fallback ?CALLBACK?"}}
842do_test 18.910 {
843  db bind_fallback bind_fallback_does_not_exist
844} {}
845do_catchsql_test 19.911 {
846  SELECT $abc, typeof($abc), $def, typeof($def), $ghi, typeof($ghi);
847} {1 {invalid command name "bind_fallback_does_not_exist"}}
848db bind_fallback {}
849
850finish_test
851