1# 2006 July 14
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 SQLite library.  The
12# focus of this script is extension loading.
13#
14# $Id: loadext.test,v 1.17 2009/03/20 09:09:37 danielk1977 Exp $
15
16set testdir [file dirname $argv0]
17source $testdir/tester.tcl
18
19ifcapable !load_ext {
20  finish_test
21  return
22}
23
24# The name of the test extension varies by operating system.
25#
26if {$::tcl_platform(platform) eq "windows" || $::tcl_platform(platform) eq "os2"} {
27  set testextension ./testloadext.dll
28} else {
29  set testextension ./libtestloadext.so
30}
31set gcc_shared "-shared -fPIC"
32if {$::tcl_platform(os) eq "Darwin"} {
33  set gcc_shared -dynamiclib
34}
35
36# The error messages tested by this file are operating system dependent
37# (because they are returned by sqlite3OsDlError()). For now, they only
38# work with UNIX (and probably only certain kinds of UNIX).
39#
40# When a shared-object cannot be opened because it does not exist, the
41# format of the message returned is:
42#
43#      [format $dlerror_nosuchfile <shared-object-name>]
44#
45# When a shared-object cannot be opened because it consists of the 4
46# characters "blah" only, we expect the error message to be:
47#
48#      [format $dlerror_notadll <shared-object-name>]
49#
50# When a symbol cannot be found within an open shared-object, the error
51# message should be:
52#
53#      [format $dlerror_nosymbol <shared-object-name> <symbol-name>]
54#
55# The exact error messages are not important. The important bit is
56# that SQLite is correctly copying the message from xDlError().
57#
58set dlerror_nosuchfile \
59    {%s: cannot open shared object file: No such file or directory}
60set dlerror_notadll    {%s: file too short}
61set dlerror_nosymbol   {%s: undefined symbol: %s}
62
63if {$::tcl_platform(os) eq "Darwin"} {
64  set dlerror_nosuchfile {dlopen(%s, 10): image not found}
65  set dlerror_notadll    {dlopen(%1$s, 10): no suitable image found.*}
66  set dlerror_nosymbol   {dlsym(XXX, %2$s): symbol not found}
67}
68
69# Make sure the test extension actually exists.  If it does not
70# exist, try to create it.  If unable to create it, then skip this
71# test file.
72#
73if {![file exists $testextension]} {
74  set srcdir [file dir $testdir]/src
75  set testextsrc $srcdir/test_loadext.c
76
77  set cmdline [concat exec gcc $gcc_shared]
78  lappend cmdline -Wall -I$srcdir -I. -g $testextsrc -o $testextension
79
80  if {[catch $cmdline msg]} {
81    puts "Skipping loadext tests: Test extension not built..."
82    puts $msg
83    finish_test
84    return
85  }
86}
87
88# Test that loading the extension produces the expected results - adding
89# the half() function to the specified database handle.
90#
91do_test loadext-1.1 {
92  catchsql {
93    SELECT half(1.0);
94  }
95} {1 {no such function: half}}
96do_test loadext-1.2 {
97  db enable_load_extension 1
98  sqlite3_load_extension db $testextension testloadext_init
99  catchsql {
100    SELECT half(1.0);
101  }
102} {0 0.5}
103
104# Test that a second database connection (db2) can load the extension also.
105#
106do_test loadext-1.3 {
107  sqlite3 db2 test.db
108  sqlite3_enable_load_extension db2 1
109  catchsql {
110    SELECT half(1.0);
111  } db2
112} {1 {no such function: half}}
113do_test loadext-1.4 {
114  sqlite3_load_extension db2 $testextension testloadext_init
115  catchsql {
116    SELECT half(1.0);
117  } db2
118} {0 0.5}
119
120# Close the first database connection. Then check that the second database
121# can still use the half() function without a problem.
122#
123do_test loadext-1.5 {
124  db close
125  catchsql {
126    SELECT half(1.0);
127  } db2
128} {0 0.5}
129
130db2 close
131sqlite3 db test.db
132sqlite3_enable_load_extension db 1
133
134# Try to load an extension for which the file does not exist.
135#
136do_test loadext-2.1 {
137  file delete -force ${testextension}xx
138  set rc [catch {
139    sqlite3_load_extension db "${testextension}xx"
140  } msg]
141  list $rc $msg
142} [list 1 [format $dlerror_nosuchfile ${testextension}xx]]
143
144# Try to load an extension for which the file is not a shared object
145#
146do_test loadext-2.2 {
147  set fd [open "${testextension}xx" w]
148  puts $fd blah
149  close $fd
150  set rc [catch {
151    sqlite3_load_extension db "${testextension}xx"
152  } msg]
153  set expected_error_pattern [format $dlerror_notadll ${testextension}xx]
154  list $rc [string match $expected_error_pattern $msg]
155} [list 1 1]
156
157# Try to load an extension for which the file is present but the
158# entry point is not.
159#
160do_test loadext-2.3 {
161  set rc [catch {
162    sqlite3_load_extension db $testextension icecream
163  } msg]
164  if {$::tcl_platform(os) eq "Darwin"} {
165    regsub {0x[1234567890abcdefABCDEF]*} $msg XXX msg
166  }
167  list $rc $msg
168} [list 1 [format $dlerror_nosymbol $testextension icecream]]
169
170# Try to load an extension for which the entry point fails (returns non-zero)
171#
172do_test loadext-2.4 {
173  set rc [catch {
174    sqlite3_load_extension db $testextension testbrokenext_init
175  } msg]
176  list $rc $msg
177} {1 {error during initialization: broken!}}
178
179############################################################################
180# Tests for the load_extension() SQL function
181#
182
183db close
184sqlite3 db test.db
185sqlite3_enable_load_extension db 1
186do_test loadext-3.1 {
187  catchsql {
188    SELECT half(5);
189  }
190} {1 {no such function: half}}
191do_test loadext-3.2 {
192  set res [catchsql {
193    SELECT load_extension($::testextension)
194  }]
195  if {$::tcl_platform(os) eq "Darwin"} {
196    regsub {0x[1234567890abcdefABCDEF]*} $res XXX res
197  }
198  set res
199} [list 1 [format $dlerror_nosymbol $testextension sqlite3_extension_init]]
200do_test loadext-3.3 {
201  catchsql {
202    SELECT load_extension($::testextension,'testloadext_init')
203  }
204} {0 {{}}}
205do_test loadext-3.4 {
206  catchsql {
207    SELECT half(5);
208  }
209} {0 2.5}
210do_test loadext-3.5 {
211  db eval {
212    SELECT sqlite3_status('MEMORY_USED') AS mused
213  } break
214  puts -nonewline " (memory_used=$mused) "
215  expr {$mused>0}
216} {1}
217do_test loadext-3.6 {
218  catchsql {
219    SELECT sqlite3_status('MEMORY_USED_X') AS mused
220  }
221} {1 {unknown status property: MEMORY_USED_X}}
222do_test loadext-3.7 {
223  catchsql {
224    SELECT sqlite3_status(4.53) AS mused
225  }
226} {1 {unknown status type}}
227do_test loadext-3.8 {
228  catchsql {
229    SELECT sqlite3_status(23) AS mused
230  }
231} {1 {sqlite3_status(23,...) returns 21}}
232
233# Ticket #1863
234# Make sure the extension loading mechanism will not work unless it
235# is explicitly enabled.
236#
237db close
238sqlite3 db test.db
239do_test loadext-4.1 {
240  catchsql {
241    SELECT load_extension($::testextension,'testloadext_init')
242  }
243} {1 {not authorized}}
244do_test loadext-4.2 {
245  sqlite3_enable_load_extension db 1
246  catchsql {
247    SELECT load_extension($::testextension,'testloadext_init')
248  }
249} {0 {{}}}
250
251do_test loadext-4.3 {
252  sqlite3_enable_load_extension db 0
253  catchsql {
254    SELECT load_extension($::testextension,'testloadext_init')
255  }
256} {1 {not authorized}}
257
258source $testdir/malloc_common.tcl
259
260
261# Malloc failure in sqlite3_auto_extension and sqlite3_load_extension
262#
263do_malloc_test loadext-5 -tclprep {
264  sqlite3_reset_auto_extension
265} -tclbody {
266  if {[autoinstall_test_functions]==7} {error "out of memory"}
267}
268do_malloc_test loadext-6 -tclbody {
269  db enable_load_extension 1
270  sqlite3_load_extension db $::testextension testloadext_init
271}
272autoinstall_test_functions
273
274finish_test
275