sqlite-ruby 2.2.1 → 2.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/doc/faq/faq.html +50 -43
- data/lib/sqlite/parsed_statement.rb +11 -1
- data/lib/sqlite/version.rb +1 -1
- data/test/tc_parsed_statement.rb +21 -0
- metadata +2 -13
- data/test/lib/sqlite.rb +0 -34
- data/test/lib/sqlite/database.rb +0 -682
- data/test/lib/sqlite/parsed_statement.rb +0 -233
- data/test/lib/sqlite/pragmas.rb +0 -236
- data/test/lib/sqlite/resultset.rb +0 -168
- data/test/lib/sqlite/statement.rb +0 -177
- data/test/lib/sqlite/translator.rb +0 -135
- data/test/lib/sqlite/version.rb +0 -45
- data/test/lib/sqlite_api.so +0 -0
data/doc/faq/faq.html
CHANGED
@@ -62,38 +62,40 @@
|
|
62
62
|
<ul>
|
63
63
|
<li>How do I do a database query?
|
64
64
|
<ul>
|
65
|
-
<li><a href='#
|
66
|
-
<li><a href='#
|
67
|
-
<li><a href='#
|
68
|
-
<li><a href='#
|
69
|
-
<li><a href='#
|
70
|
-
<li><a href='#
|
65
|
+
<li><a href='#538683626'>I just want an array of the rows…</a></li>
|
66
|
+
<li><a href='#538683586'>I’d like to use a block to iterate through the rows…</a></li>
|
67
|
+
<li><a href='#538683546'>I need to get the column names as well as the rows…</a></li>
|
68
|
+
<li><a href='#538683506'>I need the result set object itself…</a></li>
|
69
|
+
<li><a href='#538683466'>I just want the first row of the result set…</a></li>
|
70
|
+
<li><a href='#538683426'>I just want the first value of the first row of the result set…</a></li>
|
71
71
|
</ul>
|
72
72
|
</li>
|
73
|
-
<li><a href='#
|
74
|
-
<li><a href='#
|
75
|
-
<li><a href='#
|
76
|
-
<li><a href='#
|
77
|
-
<li><a href='#
|
78
|
-
<li><a href='#
|
79
|
-
<li><a href='#
|
80
|
-
<li><a href='#
|
73
|
+
<li><a href='#538683356'>How do I prepare a statement for repeated execution?</a></li>
|
74
|
+
<li><a href='#538683316'>How do I use placeholders in an <span class="caps">SQL</span> statement?</a></li>
|
75
|
+
<li><a href='#538683276'>How do I discover metadata about a query?</a></li>
|
76
|
+
<li><a href='#538683236'>I’d like the rows to be indexible by column name.</a></li>
|
77
|
+
<li><a href='#538683196'>I’d like the values from a query to be the correct types, instead of String.</a></li>
|
78
|
+
<li><a href='#538683156'>How do I do a <span class="caps">DDL </span>(insert, update, delete) statement?</a></li>
|
79
|
+
<li><a href='#538683116'>How do I execute multiple statements in a single string?</a></li>
|
80
|
+
<li><a href='#538683076'>How do I begin/end a transaction?</a></li>
|
81
81
|
</ul>
|
82
82
|
</div>
|
83
|
-
<a name='
|
83
|
+
<a name='538683626'></a>
|
84
84
|
<div class='faq-title'>How do I do a database query? I just want an array of the rows…</div>
|
85
85
|
<div class='faq-answer'><p>Use the <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#execute</a> method. If you don’t give it a block, it will return an array of all the rows:</p>
|
86
86
|
|
87
|
+
|
87
88
|
<pre>
|
88
89
|
require 'sqlite'
|
89
90
|
|
90
91
|
db = SQLite::<a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database</a>.new( "test.db" )
|
91
92
|
rows = db.execute( "select * from test" )
|
92
93
|
</pre></div>
|
93
|
-
<a name='
|
94
|
+
<a name='538683586'></a>
|
94
95
|
<div class='faq-title'>How do I do a database query? I’d like to use a block to iterate through the rows…</div>
|
95
96
|
<div class='faq-answer'><p>Use the <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#execute</a> method. If you give it a block, each row of the result will be yielded to the block:</p>
|
96
97
|
|
98
|
+
|
97
99
|
<pre>
|
98
100
|
require 'sqlite'
|
99
101
|
|
@@ -102,10 +104,11 @@
|
|
102
104
|
...
|
103
105
|
end
|
104
106
|
</pre></div>
|
105
|
-
<a name='
|
107
|
+
<a name='538683546'></a>
|
106
108
|
<div class='faq-title'>How do I do a database query? I need to get the column names as well as the rows…</div>
|
107
109
|
<div class='faq-answer'><p>Use the <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#execute2</a> method. This works just like <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#execute</a>; if you don’t give it a block, it returns an array of rows; otherwise, it will yield each row to the block. <em>However</em>, the first row returned is always an array of the column names from the query:</p>
|
108
110
|
|
111
|
+
|
109
112
|
<pre>
|
110
113
|
require 'sqlite'
|
111
114
|
|
@@ -123,7 +126,7 @@
|
|
123
126
|
end
|
124
127
|
end
|
125
128
|
</pre></div>
|
126
|
-
<a name='
|
129
|
+
<a name='538683506'></a>
|
127
130
|
<div class='faq-title'>How do I do a database query? I need the result set object itself…</div>
|
128
131
|
<div class='faq-answer'><p>Sometimes you don’t want all the rows at once, and yet you’d like to be able to iterate through the results. For instance, you may want to pass the results to some other function (or series of functions) and have them pull rows from the results on demand. This is more effecient for very large queries.</p>
|
129
132
|
|
@@ -131,6 +134,7 @@
|
|
131
134
|
|
132
135
|
<p>You can do both internal and external iteration with the result set this way:</p>
|
133
136
|
|
137
|
+
|
134
138
|
<pre>
|
135
139
|
require 'sqlite'
|
136
140
|
|
@@ -165,30 +169,30 @@
|
|
165
169
|
result.next
|
166
170
|
end
|
167
171
|
</pre>
|
168
|
-
|
169
172
|
<p>In general, <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#query</a> is not a very good choice for such operations…</p></div>
|
170
|
-
<a name='
|
173
|
+
<a name='538683466'></a>
|
171
174
|
<div class='faq-title'>How do I do a database query? I just want the first row of the result set…</div>
|
172
175
|
<div class='faq-answer'><p>Easy. Just call <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#get_first_row</a>:</p>
|
173
176
|
|
177
|
+
|
174
178
|
<pre>
|
175
179
|
row = db.get_first_row( "select * from table" )
|
176
180
|
</pre>
|
177
|
-
|
178
181
|
<p>This also supports bind variables, just like <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#execute</a> and friends.</p></div>
|
179
|
-
<a name='
|
182
|
+
<a name='538683426'></a>
|
180
183
|
<div class='faq-title'>How do I do a database query? I just want the first value of the first row of the result set…</div>
|
181
184
|
<div class='faq-answer'><p>Also easy. Just call <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#get_first_value</a>:</p>
|
182
185
|
|
186
|
+
|
183
187
|
<pre>
|
184
188
|
count = db.get_first_value( "select count(*) from table" )
|
185
189
|
</pre>
|
186
|
-
|
187
190
|
<p>This also supports bind variables, just like <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#execute</a> and friends.</p></div>
|
188
|
-
<a name='
|
191
|
+
<a name='538683356'></a>
|
189
192
|
<div class='faq-title'>How do I prepare a statement for repeated execution?</div>
|
190
193
|
<div class='faq-answer'><p>If the same statement is going to be executed repeatedly, you can speed things up a bit by <em>preparing</em> the statement. You do this via the <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#prepare</a> method. It returns a <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Statement.html'>Statement</a> object, and you can then invoke #execute on that to get the <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/ResultSet.html'>ResultSet</a>:</p>
|
191
194
|
|
195
|
+
|
192
196
|
<pre>
|
193
197
|
stmt = db.prepare( "select * from person" )
|
194
198
|
|
@@ -198,12 +202,10 @@
|
|
198
202
|
end
|
199
203
|
end
|
200
204
|
</pre>
|
201
|
-
|
202
205
|
<p>This is made more useful by the ability to bind variables to placeholders via the <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Statement.html'>Statement#bind_param</a> and <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Statement.html'>Statement#bind_params</a> methods. (See the next <span class="caps">FAQ</span> for details.)</p></div>
|
203
|
-
<a name='
|
206
|
+
<a name='538683316'></a>
|
204
207
|
<div class='faq-title'>How do I use placeholders in an <span class="caps">SQL</span> statement?</div>
|
205
208
|
<div class='faq-answer'><p>Placeholders in an <span class="caps">SQL</span> statement take any of the following formats:</p>
|
206
|
-
|
207
209
|
<ul>
|
208
210
|
<li><code>?</code></li>
|
209
211
|
<li><code>?<em>n</em></code></li>
|
@@ -215,6 +217,7 @@
|
|
215
217
|
|
216
218
|
<p>For example, here is a query using these placeholder formats:</p>
|
217
219
|
|
220
|
+
|
218
221
|
<pre>
|
219
222
|
select *
|
220
223
|
from table
|
@@ -223,31 +226,31 @@
|
|
223
226
|
and e = :spouse:
|
224
227
|
and f = :1
|
225
228
|
</pre>
|
226
|
-
|
227
229
|
<p>This defines 5 different placeholders: 1, 2, 3, “name”, and “spouse”.</p>
|
228
230
|
|
229
231
|
<p>You replace these placeholders by <em>binding</em> them to values. This can be accomplished in a variety of ways.</p>
|
230
232
|
|
231
233
|
<p>The <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#execute</a>, <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#execute2</a>, and <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#query</a> methods all accept additional arguments following the <span class="caps">SQL</span> statement. These arguments are assumed to be bind parameters, and they are bound (positionally) to their corresponding placeholders:</p>
|
232
234
|
|
235
|
+
|
233
236
|
<pre>
|
234
237
|
db.execute( "select * from table where a = ? and b = ?",
|
235
238
|
"hello",
|
236
239
|
"world" )
|
237
240
|
</pre>
|
238
|
-
|
239
241
|
<p>The above would replace the first question mark with ‘hello’ and the second with ‘world’. If the placeholders have an explicit index given, they will be replaced with the bind parameter at that index (1-based).</p>
|
240
242
|
|
241
243
|
<p>If a Hash is given as a bind parameter, then its key/value pairs are bound to the placeholders. This is how you bind by name:</p>
|
242
244
|
|
245
|
+
|
243
246
|
<pre>
|
244
247
|
db.execute( "select * from table where a = :name and b = :value",
|
245
248
|
"name" => "bob",
|
246
249
|
"value" => "priceless" )
|
247
250
|
</pre>
|
248
|
-
|
249
251
|
<p>You can also bind explicitly using the <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Statement.html'>Statement</a> object itself. Just do a <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#prepare</a> to get the <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Statement.html'>Statement</a>, and then use either <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Statement.html'>Statement#bind_param</a> or <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Statement.html'>Statement#bind_params</a>:</p>
|
250
252
|
|
253
|
+
|
251
254
|
<pre>
|
252
255
|
stmt = db.prepare( "select * from table where a = :name and b = ?" )
|
253
256
|
|
@@ -258,20 +261,21 @@
|
|
258
261
|
|
259
262
|
stmt.bind_params( "value", "name" => "bob" )
|
260
263
|
</pre></div>
|
261
|
-
<a name='
|
264
|
+
<a name='538683276'></a>
|
262
265
|
<div class='faq-title'>How do I discover metadata about a query?</div>
|
263
266
|
<div class='faq-answer'><p>If you ever want to know the names or types of the columns in a result set, you can do it in several ways.</p>
|
264
267
|
|
265
268
|
<p>The first way is to ask the row object itself. Each row will have a property “fields” that returns an array of the column names. The row will also have a property “types” that returns an array of the column types:</p>
|
266
269
|
|
270
|
+
|
267
271
|
<pre>
|
268
272
|
rows = db.execute( "select * from table" )
|
269
273
|
p rows[0].fields
|
270
274
|
p rows[0].types
|
271
275
|
</pre>
|
272
|
-
|
273
276
|
<p>Obviously, this approach requires you to execute a statement that actually returns data. If you don’t know if the statement will return any rows, but you still need the metadata, you can use <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#query</a> and ask the <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/ResultSet.html'>ResultSet</a> object itself:</p>
|
274
277
|
|
278
|
+
|
275
279
|
<pre>
|
276
280
|
db.query( "select * from table" ) do |result|
|
277
281
|
p result.columns
|
@@ -279,20 +283,21 @@
|
|
279
283
|
...
|
280
284
|
end
|
281
285
|
</pre>
|
282
|
-
|
283
286
|
<p>Lastly, you can use <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#prepare</a> and ask the <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Statement.html'>Statement</a> object what the metadata are:</p>
|
284
287
|
|
288
|
+
|
285
289
|
<pre>
|
286
290
|
stmt = db.prepare( "select * from table" )
|
287
291
|
p stmt.columns
|
288
292
|
p stmt.types
|
289
293
|
</pre></div>
|
290
|
-
<a name='
|
294
|
+
<a name='538683236'></a>
|
291
295
|
<div class='faq-title'>I’d like the rows to be indexible by column name.</div>
|
292
296
|
<div class='faq-answer'><p>By default, each row from a query is returned as an Array of values. This means that you can only obtain values by their index. Sometimes, however, you would like to obtain values by their column name.</p>
|
293
297
|
|
294
298
|
<p>The first way to do this is to set the <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database</a> property “results_as_hash” to true. If you do this, then all rows will be returned as Hash objects, with the column names as the keys. (In this case, the “fields” property is unavailable on the row, although the “types” property remains.)</p>
|
295
299
|
|
300
|
+
|
296
301
|
<pre>
|
297
302
|
db.results_as_hash = true
|
298
303
|
db.execute( "select * from table" ) do |row|
|
@@ -300,9 +305,9 @@
|
|
300
305
|
p row['column2']
|
301
306
|
end
|
302
307
|
</pre>
|
303
|
-
|
304
308
|
<p>The other way is to use Ara Howard’s <a href="http://rubyforge.org/projects/arrayfields">ArrayFields</a> module. Just require “arrayfields”, and all of your rows will be indexable by column name, even though they are still arrays!</p>
|
305
309
|
|
310
|
+
|
306
311
|
<pre>
|
307
312
|
require 'arrayfields'
|
308
313
|
|
@@ -312,21 +317,22 @@
|
|
312
317
|
p row[1] == row['column2']
|
313
318
|
end
|
314
319
|
</pre></div>
|
315
|
-
<a name='
|
320
|
+
<a name='538683196'></a>
|
316
321
|
<div class='faq-title'>I’d like the values from a query to be the correct types, instead of String.</div>
|
317
322
|
<div class='faq-answer'><p>You can turn on “type translation” by setting <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#type_translation</a> to true:</p>
|
318
323
|
|
324
|
+
|
319
325
|
<pre>
|
320
326
|
db.type_translation = true
|
321
327
|
db.execute( "select * from table" ) do |row|
|
322
328
|
p row
|
323
329
|
end
|
324
330
|
</pre>
|
325
|
-
|
326
331
|
<p>By doing this, each return value for each row will be translated to its correct type, based on its declared column type.</p>
|
327
332
|
|
328
333
|
<p>You can even declare your own translation routines, if (for example) you are using an <span class="caps">SQL</span> type that is not handled by default:</p>
|
329
334
|
|
335
|
+
|
330
336
|
<pre>
|
331
337
|
# assume "objects" table has the following schema:
|
332
338
|
# create table objects (
|
@@ -347,19 +353,21 @@
|
|
347
353
|
obj = db.get_first_value( "select thing from objects where name='bob'" )
|
348
354
|
p obj == h
|
349
355
|
</pre></div>
|
350
|
-
<a name='
|
356
|
+
<a name='538683156'></a>
|
351
357
|
<div class='faq-title'>How do I do a <span class="caps">DDL </span>(insert, update, delete) statement?</div>
|
352
358
|
<div class='faq-answer'><p>You can actually do inserts, updates, and deletes in exactly the same way as selects, but in general the <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#execute</a> method will be most convenient:</p>
|
353
359
|
|
360
|
+
|
354
361
|
<pre>
|
355
362
|
db.execute( "insert into table values ( ?, ? )", *bind_vars )
|
356
363
|
</pre></div>
|
357
|
-
<a name='
|
364
|
+
<a name='538683116'></a>
|
358
365
|
<div class='faq-title'>How do I execute multiple statements in a single string?</div>
|
359
366
|
<div class='faq-answer'><p>The standard query methods (<a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#execute</a>, <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#execute2</a>, <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#query</a>, and <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Statement.html'>Statement#execute</a>) will only execute the first statement in the string that is given to them. Thus, if you have a string with multiple <span class="caps">SQL</span> statements, each separated by a string, you can’t use those methods to execute them all at once.</p>
|
360
367
|
|
361
368
|
<p>Instead, use <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#execute_batch</a>:</p>
|
362
369
|
|
370
|
+
|
363
371
|
<pre>
|
364
372
|
sql = <<SQL
|
365
373
|
create table the_table (
|
@@ -374,17 +382,16 @@
|
|
374
382
|
|
375
383
|
db.execute_batch( sql )
|
376
384
|
</pre>
|
377
|
-
|
378
385
|
<p>Unlike the other query methods, <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#execute_batch</a> accepts no block. It will also only ever return <ins>nil</ins>. Thus, it is really only suitable for batch processing of <span class="caps">DDL</span> statements.</p></div>
|
379
|
-
<a name='
|
386
|
+
<a name='538683076'></a>
|
380
387
|
<div class='faq-title'>How do I begin/end a transaction?</div>
|
381
388
|
<div class='faq-answer'><p>Use <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#transaction</a> to start a transaction. If you give it a block, the block will be automatically committed at the end of the block, unless an exception was raised, in which case the transaction will be rolled back. (Never explicitly call <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#commit</a> or <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#rollback</a> inside of a transaction block—you’ll get errors when the block terminates!)</p>
|
382
389
|
|
383
|
-
<pre> database.transaction do |db| db.execute( "insert into table values ( 'a', 'b', 'c' )" ) ... end </pre>
|
384
390
|
|
391
|
+
<pre> database.transaction do |db| db.execute( "insert into table values ( 'a', 'b', 'c' )" ) ... end </pre>
|
385
392
|
<p>Alternatively, if you don’t give a block to <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#transaction</a>, the transaction remains open until you explicitly call <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#commit</a> or <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#rollback</a>.</p>
|
386
393
|
|
387
|
-
<pre> db.transaction db.execute( "insert into table values ( 'a', 'b', 'c' )" ) db.commit </pre>
|
388
394
|
|
395
|
+
<pre> db.transaction db.execute( "insert into table values ( 'a', 'b', 'c' )" ) db.commit </pre>
|
389
396
|
<p>Note that SQLite does not allow nested transactions, so you’ll get errors if you try to open a new transaction while one is already active. Use <a href='http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html'>Database#transaction_active</a>? to determine whether a transaction is active or not.</p></div>
|
390
397
|
</body></html>
|
@@ -176,13 +176,23 @@ module SQLite
|
|
176
176
|
|
177
177
|
scanner = StringScanner.new( sql )
|
178
178
|
variable_index = 0
|
179
|
+
allow_break = true
|
179
180
|
|
180
181
|
until scanner.eos?
|
181
182
|
tokens << " " unless tokens.empty? if scanner.scan( /\s+/ )
|
182
183
|
break if scanner.eos?
|
183
184
|
|
184
185
|
if scanner.scan( /;/ )
|
185
|
-
break
|
186
|
+
break if allow_break
|
187
|
+
tokens << Token.new( ";" )
|
188
|
+
elsif scanner.scan( /\bbegin\s+transaction\b/i )
|
189
|
+
tokens << Token.new( "begin transaction" )
|
190
|
+
elsif scanner.scan( /\bbegin\b(?=\s*[^\s;]|$)/im )
|
191
|
+
tokens << Token.new( "begin" )
|
192
|
+
allow_break = false
|
193
|
+
elsif !allow_break && scanner.scan( /\bend\b/i )
|
194
|
+
tokens << Token.new( "end" )
|
195
|
+
allow_break = true
|
186
196
|
elsif scanner.scan( /---.*$/ ) || scanner.scan( %r{/\*.*?\*/}m )
|
187
197
|
# comments
|
188
198
|
next
|
data/lib/sqlite/version.rb
CHANGED
data/test/tc_parsed_statement.rb
CHANGED
@@ -157,4 +157,25 @@ class TC_ParsedStatement < Test::Unit::TestCase
|
|
157
157
|
assert( ( [ "name", "spouse", 1, 5, 12, 15 ] - stmt.placeholders ).empty? )
|
158
158
|
end
|
159
159
|
|
160
|
+
def test_begin_end
|
161
|
+
sql = %Q{begin\n delete blah;\n and blah;\nend; extra}
|
162
|
+
stmt = SQLite::ParsedStatement.new( sql )
|
163
|
+
assert_equal "begin delete blah; and blah; end", stmt.sql
|
164
|
+
assert_equal " extra", stmt.trailing
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_begin
|
168
|
+
sql = %Q{begin ; end}
|
169
|
+
stmt = SQLite::ParsedStatement.new( sql )
|
170
|
+
assert_equal "begin", stmt.sql
|
171
|
+
assert_equal " end", stmt.trailing
|
172
|
+
end
|
173
|
+
|
174
|
+
def test_begin_transaction
|
175
|
+
sql = %Q{begin transaction; blah}
|
176
|
+
stmt = SQLite::ParsedStatement.new( sql )
|
177
|
+
assert_equal "begin transaction", stmt.sql
|
178
|
+
assert_equal " blah", stmt.trailing
|
179
|
+
end
|
180
|
+
|
160
181
|
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.1
|
|
3
3
|
specification_version: 1
|
4
4
|
name: sqlite-ruby
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 2.2.
|
7
|
-
date: 2004-11-
|
6
|
+
version: 2.2.2
|
7
|
+
date: 2004-11-15
|
8
8
|
summary: SQLite/Ruby is a module to allow Ruby scripts to interface with a SQLite database.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -42,7 +42,6 @@ files:
|
|
42
42
|
- lib/sqlite/version.rb
|
43
43
|
- lib/sqlite/pragmas.rb
|
44
44
|
- test/db
|
45
|
-
- test/lib
|
46
45
|
- test/tc_parsed_statement.rb
|
47
46
|
- test/tc_api_core.rb
|
48
47
|
- test/tests.rb
|
@@ -52,16 +51,6 @@ files:
|
|
52
51
|
- test/tc_type_translation.rb
|
53
52
|
- test/tc_database.rb
|
54
53
|
- test/db/fixtures.sql
|
55
|
-
- test/lib/sqlite_api.so
|
56
|
-
- test/lib/sqlite.rb
|
57
|
-
- test/lib/sqlite
|
58
|
-
- test/lib/sqlite/statement.rb
|
59
|
-
- test/lib/sqlite/database.rb
|
60
|
-
- test/lib/sqlite/parsed_statement.rb
|
61
|
-
- test/lib/sqlite/translator.rb
|
62
|
-
- test/lib/sqlite/resultset.rb
|
63
|
-
- test/lib/sqlite/version.rb
|
64
|
-
- test/lib/sqlite/pragmas.rb
|
65
54
|
- README
|
66
55
|
test_files:
|
67
56
|
- test/tests.rb
|
data/test/lib/sqlite.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
#--
|
2
|
-
# =============================================================================
|
3
|
-
# Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
|
4
|
-
# All rights reserved.
|
5
|
-
#
|
6
|
-
# Redistribution and use in source and binary forms, with or without
|
7
|
-
# modification, are permitted provided that the following conditions are met:
|
8
|
-
#
|
9
|
-
# * Redistributions of source code must retain the above copyright notice,
|
10
|
-
# this list of conditions and the following disclaimer.
|
11
|
-
#
|
12
|
-
# * Redistributions in binary form must reproduce the above copyright
|
13
|
-
# notice, this list of conditions and the following disclaimer in the
|
14
|
-
# documentation and/or other materials provided with the distribution.
|
15
|
-
#
|
16
|
-
# * The names of its contributors may not be used to endorse or promote
|
17
|
-
# products derived from this software without specific prior written
|
18
|
-
# permission.
|
19
|
-
#
|
20
|
-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
21
|
-
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
22
|
-
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
23
|
-
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
24
|
-
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
25
|
-
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
26
|
-
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
27
|
-
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
28
|
-
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
29
|
-
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
30
|
-
# =============================================================================
|
31
|
-
#++
|
32
|
-
|
33
|
-
require 'sqlite/database'
|
34
|
-
require 'sqlite/version'
|
data/test/lib/sqlite/database.rb
DELETED
@@ -1,682 +0,0 @@
|
|
1
|
-
#--
|
2
|
-
# =============================================================================
|
3
|
-
# Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
|
4
|
-
# All rights reserved.
|
5
|
-
#
|
6
|
-
# Redistribution and use in source and binary forms, with or without
|
7
|
-
# modification, are permitted provided that the following conditions are met:
|
8
|
-
#
|
9
|
-
# * Redistributions of source code must retain the above copyright notice,
|
10
|
-
# this list of conditions and the following disclaimer.
|
11
|
-
#
|
12
|
-
# * Redistributions in binary form must reproduce the above copyright
|
13
|
-
# notice, this list of conditions and the following disclaimer in the
|
14
|
-
# documentation and/or other materials provided with the distribution.
|
15
|
-
#
|
16
|
-
# * The names of its contributors may not be used to endorse or promote
|
17
|
-
# products derived from this software without specific prior written
|
18
|
-
# permission.
|
19
|
-
#
|
20
|
-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
21
|
-
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
22
|
-
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
23
|
-
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
24
|
-
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
25
|
-
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
26
|
-
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
27
|
-
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
28
|
-
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
29
|
-
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
30
|
-
# =============================================================================
|
31
|
-
#++
|
32
|
-
|
33
|
-
require 'base64'
|
34
|
-
require 'sqlite_api'
|
35
|
-
require 'sqlite/pragmas'
|
36
|
-
require 'sqlite/statement'
|
37
|
-
require 'sqlite/translator'
|
38
|
-
|
39
|
-
module SQLite
|
40
|
-
|
41
|
-
# The Database class encapsulates a single connection to a SQLite database.
|
42
|
-
# Its usage is very straightforward:
|
43
|
-
#
|
44
|
-
# require 'sqlite'
|
45
|
-
#
|
46
|
-
# db = SQLite::Database.new( "data.db" )
|
47
|
-
#
|
48
|
-
# db.execute( "select * from table" ) do |row|
|
49
|
-
# p row
|
50
|
-
# end
|
51
|
-
#
|
52
|
-
# db.close
|
53
|
-
#
|
54
|
-
# It wraps the lower-level methods provides by the API module, include
|
55
|
-
# includes the Pragmas module for access to various pragma convenience
|
56
|
-
# methods.
|
57
|
-
#
|
58
|
-
# The Database class provides type translation services as well, by which
|
59
|
-
# the SQLite data types (which are all represented as strings) may be
|
60
|
-
# converted into their corresponding types (as defined in the schemas
|
61
|
-
# for their tables). This translation only occurs when querying data from
|
62
|
-
# the database--insertions and updates are all still typeless.
|
63
|
-
#
|
64
|
-
# Furthermore, the Database class has been designed to work well with the
|
65
|
-
# ArrayFields module from Ara Howard. If you require the ArrayFields
|
66
|
-
# module before performing a query, and if you have not enabled results as
|
67
|
-
# hashes, then the results will all be indexible by field name.
|
68
|
-
class Database
|
69
|
-
include SQLite::Pragmas
|
70
|
-
|
71
|
-
# Opens the database contained in the given file. This just calls #new,
|
72
|
-
# passing 0 as the mode parameter. This returns the new Database
|
73
|
-
# instance.
|
74
|
-
def self.open( file_name )
|
75
|
-
new( file_name, 0 )
|
76
|
-
end
|
77
|
-
|
78
|
-
# Quotes the given string, making it safe to use in an SQL statement.
|
79
|
-
# It replaces all instances of the single-quote character with two
|
80
|
-
# single-quote characters. The modified string is returned.
|
81
|
-
def self.quote( string )
|
82
|
-
string.gsub( /'/, "''" )
|
83
|
-
end
|
84
|
-
|
85
|
-
# Returns a string that represents the serialization of the given object.
|
86
|
-
# The string may safely be used in an SQL statement.
|
87
|
-
def self.encode( object )
|
88
|
-
Base64.encode64( Marshal.dump( object ) ).strip
|
89
|
-
end
|
90
|
-
|
91
|
-
# Unserializes the object contained in the given string. The string must be
|
92
|
-
# one that was returned by #encode.
|
93
|
-
def self.decode( string )
|
94
|
-
Marshal.load( Base64.decode64( string ) )
|
95
|
-
end
|
96
|
-
|
97
|
-
# Return +true+ if the string is a valid (ie, parsable) SQL statement, and
|
98
|
-
# +false+ otherwise.
|
99
|
-
def self.complete?( string )
|
100
|
-
SQLite::API.complete( string )
|
101
|
-
end
|
102
|
-
|
103
|
-
# The low-level opaque database handle that this object wraps.
|
104
|
-
attr_reader :handle
|
105
|
-
|
106
|
-
# A boolean that indicates whether rows in result sets should be returned
|
107
|
-
# as hashes or not. By default, rows are returned as arrays.
|
108
|
-
attr_accessor :results_as_hash
|
109
|
-
|
110
|
-
# Create a new Database object that opens the given file. The mode
|
111
|
-
# parameter has no meaning yet, and may be omitted. If the file does not
|
112
|
-
# exist, it will be created if possible.
|
113
|
-
#
|
114
|
-
# By default, the new database will return result rows as arrays
|
115
|
-
# (#results_as_hash) and has type translation disabled (#type_translation=).
|
116
|
-
def initialize( file_name, mode=0 )
|
117
|
-
@handle = SQLite::API.open( file_name, mode )
|
118
|
-
@closed = false
|
119
|
-
@results_as_hash = false
|
120
|
-
@type_translation = false
|
121
|
-
@translator = nil
|
122
|
-
end
|
123
|
-
|
124
|
-
# Return the type translator employed by this database instance. Each
|
125
|
-
# database instance has its own type translator; this allows for different
|
126
|
-
# type handlers to be installed in each instance without affecting other
|
127
|
-
# instances. Furthermore, the translators are instantiated lazily, so that
|
128
|
-
# if a database does not use type translation, it will not be burdened by
|
129
|
-
# the overhead of a useless type translator. (See the Translator class.)
|
130
|
-
def translator
|
131
|
-
@translator ||= Translator.new
|
132
|
-
end
|
133
|
-
|
134
|
-
# Returns +true+ if type translation is enabled for this database, or
|
135
|
-
# +false+ otherwise.
|
136
|
-
def type_translation
|
137
|
-
@type_translation
|
138
|
-
end
|
139
|
-
|
140
|
-
# Enable or disable type translation for this database.
|
141
|
-
def type_translation=( mode )
|
142
|
-
@type_translation = mode
|
143
|
-
end
|
144
|
-
|
145
|
-
# Closes this database. No checks are done to ensure that a database is not
|
146
|
-
# closed more than once, and closing a database more than once can be
|
147
|
-
# catastrophic.
|
148
|
-
def close
|
149
|
-
SQLite::API.close( @handle )
|
150
|
-
@closed = true
|
151
|
-
end
|
152
|
-
|
153
|
-
# Returns +true+ if this database instance has been closed (see #close).
|
154
|
-
def closed?
|
155
|
-
@closed
|
156
|
-
end
|
157
|
-
|
158
|
-
# Returns a Statement object representing the given SQL. This does not
|
159
|
-
# execute the statement; it merely prepares the statement for execution.
|
160
|
-
def prepare( sql )
|
161
|
-
Statement.new( self, sql )
|
162
|
-
end
|
163
|
-
|
164
|
-
# Executes the given SQL statement. If additional parameters are given,
|
165
|
-
# they are treated as bind variables, and are bound to the placeholders in
|
166
|
-
# the query.
|
167
|
-
#
|
168
|
-
# Each placeholder must match one of the following formats:
|
169
|
-
#
|
170
|
-
# * <tt>?</tt>
|
171
|
-
# * <tt>?nnn</tt>
|
172
|
-
# * <tt>:word</tt>
|
173
|
-
# * <tt>:word:</tt>
|
174
|
-
#
|
175
|
-
# where _nnn_ is an integer value indicating the index of the bind
|
176
|
-
# variable to be bound at that position, and _word_ is an alphanumeric
|
177
|
-
# identifier for that placeholder. For "<tt>?</tt>", an index is
|
178
|
-
# automatically assigned of one greater than the previous index used
|
179
|
-
# (or 1, if it is the first).
|
180
|
-
#
|
181
|
-
# Note that if any of the values passed to this are hashes, then the
|
182
|
-
# key/value pairs are each bound separately, with the key being used as
|
183
|
-
# the name of the placeholder to bind the value to.
|
184
|
-
#
|
185
|
-
# The block is optional. If given, it will be invoked for each row returned
|
186
|
-
# by the query. Otherwise, any results are accumulated into an array and
|
187
|
-
# returned wholesale.
|
188
|
-
#
|
189
|
-
# See also #execute2, #execute_batch and #query for additional ways of
|
190
|
-
# executing statements.
|
191
|
-
def execute( sql, *bind_vars )
|
192
|
-
stmt = prepare( sql )
|
193
|
-
stmt.bind_params( *bind_vars )
|
194
|
-
result = stmt.execute
|
195
|
-
begin
|
196
|
-
if block_given?
|
197
|
-
result.each { |row| yield row }
|
198
|
-
else
|
199
|
-
return result.inject( [] ) { |arr,row| arr << row; arr }
|
200
|
-
end
|
201
|
-
ensure
|
202
|
-
result.close
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
# Executes the given SQL statement, exactly as with #execute. However, the
|
207
|
-
# first row returned (either via the block, or in the returned array) is
|
208
|
-
# always the names of the columns. Subsequent rows correspond to the data
|
209
|
-
# from the result set.
|
210
|
-
#
|
211
|
-
# Thus, even if the query itself returns no rows, this method will always
|
212
|
-
# return at least one row--the names of the columns.
|
213
|
-
#
|
214
|
-
# See also #execute, #execute_batch and #query for additional ways of
|
215
|
-
# executing statements.
|
216
|
-
def execute2( sql, *bind_vars )
|
217
|
-
stmt = prepare( sql )
|
218
|
-
stmt.bind_params( *bind_vars )
|
219
|
-
result = stmt.execute
|
220
|
-
begin
|
221
|
-
if block_given?
|
222
|
-
yield result.columns
|
223
|
-
result.each { |row| yield row }
|
224
|
-
else
|
225
|
-
return result.inject( [ result.columns ] ) { |arr,row| arr << row; arr }
|
226
|
-
end
|
227
|
-
ensure
|
228
|
-
result.close
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
# Executes all SQL statements in the given string. By contrast, the other
|
233
|
-
# means of executing queries will only execute the first statement in the
|
234
|
-
# string, ignoring all subsequent statements. This will execute each one
|
235
|
-
# in turn. The same bind parameters, if given, will be applied to each
|
236
|
-
# statement.
|
237
|
-
#
|
238
|
-
# This always returns +nil+, making it unsuitable for queries that return
|
239
|
-
# rows.
|
240
|
-
def execute_batch( sql, *bind_vars )
|
241
|
-
loop do
|
242
|
-
stmt = prepare( sql )
|
243
|
-
stmt.bind_params *bind_vars
|
244
|
-
stmt.execute
|
245
|
-
sql = stmt.remainder
|
246
|
-
break if sql.length < 1
|
247
|
-
end
|
248
|
-
nil
|
249
|
-
end
|
250
|
-
|
251
|
-
# This does like #execute and #execute2 (binding variables and so forth),
|
252
|
-
# but instead of yielding each row from the result set, this will yield the
|
253
|
-
# ResultSet instance itself (q.v.). If no block is given, the ResultSet
|
254
|
-
# instance will be returned.
|
255
|
-
def query( sql, *bind_vars, &block ) # :yields: result_set
|
256
|
-
stmt = prepare( sql )
|
257
|
-
stmt.bind_params( *bind_vars )
|
258
|
-
stmt.execute( &block )
|
259
|
-
end
|
260
|
-
|
261
|
-
# A convenience method for obtaining the first row of a result set, and
|
262
|
-
# discarding all others. It is otherwise identical to #execute.
|
263
|
-
#
|
264
|
-
# See also #get_first_value.
|
265
|
-
def get_first_row( sql, *bind_vars )
|
266
|
-
execute( sql, *bind_vars ) { |row| return row }
|
267
|
-
nil
|
268
|
-
end
|
269
|
-
|
270
|
-
# A convenience method for obtaining the first value of the first row of a
|
271
|
-
# result set, and discarding all other values and rows. It is otherwise
|
272
|
-
# identical to #execute.
|
273
|
-
#
|
274
|
-
# See also #get_first_row.
|
275
|
-
def get_first_value( sql, *bind_vars )
|
276
|
-
execute( sql, *bind_vars ) { |row| return row[0] }
|
277
|
-
nil
|
278
|
-
end
|
279
|
-
|
280
|
-
# Obtains the unique row ID of the last row to be inserted by this Database
|
281
|
-
# instance.
|
282
|
-
def last_insert_row_id
|
283
|
-
SQLite::API.last_insert_row_id( @handle )
|
284
|
-
end
|
285
|
-
|
286
|
-
# Returns the number of changes made to this database instance by the last
|
287
|
-
# operation performed. Note that a "delete from table" without a where
|
288
|
-
# clause will not affect this value.
|
289
|
-
def changes
|
290
|
-
SQLite::API.changes( @handle )
|
291
|
-
end
|
292
|
-
|
293
|
-
# Interrupts the currently executing operation, causing it to abort.
|
294
|
-
def interrupt
|
295
|
-
SQLite::API.interrupt( @handle )
|
296
|
-
end
|
297
|
-
|
298
|
-
# Register a busy handler with this database instance. When a requested
|
299
|
-
# resource is busy, this handler will be invoked. If the handler returns
|
300
|
-
# +false+, the operation will be aborted; otherwise, the resource will
|
301
|
-
# be requested again.
|
302
|
-
#
|
303
|
-
# The handler will be invoked with the name of the resource that was
|
304
|
-
# busy, and the number of times it has been retried.
|
305
|
-
#
|
306
|
-
# See also #busy_timeout.
|
307
|
-
def busy_handler( &block ) # :yields: resource, retries
|
308
|
-
SQLite::API.busy_handler( @handle, block )
|
309
|
-
end
|
310
|
-
|
311
|
-
# Indicates that if a request for a resource terminates because that
|
312
|
-
# resource is busy, SQLite should wait for the indicated number of
|
313
|
-
# milliseconds before trying again. By default, SQLite does not retry
|
314
|
-
# busy resources. To restore the default behavior, send 0 as the
|
315
|
-
# +ms+ parameter.
|
316
|
-
#
|
317
|
-
# See also #busy_handler.
|
318
|
-
def busy_timeout( ms )
|
319
|
-
SQLite::API.busy_timeout( @handle, ms )
|
320
|
-
end
|
321
|
-
|
322
|
-
# Creates a new function for use in SQL statements. It will be added as
|
323
|
-
# +name+, with the given +arity+. (For variable arity functions, use
|
324
|
-
# -1 for the arity.) If +type+ is non-nil, it should either be an
|
325
|
-
# integer (indicating that the type of the function is always the
|
326
|
-
# type of the argument at that index), or one of the symbols
|
327
|
-
# <tt>:numeric</tt>, <tt>:text</tt>, <tt>:args</tt> (in which case
|
328
|
-
# the function is, respectively, numeric, textual, or the same type as
|
329
|
-
# its arguments).
|
330
|
-
#
|
331
|
-
# The block should accept at least one parameter--the FunctionProxy
|
332
|
-
# instance that wraps this function invocation--and any other
|
333
|
-
# arguments it needs (up to its arity).
|
334
|
-
#
|
335
|
-
# The block does not return a value directly. Instead, it will invoke
|
336
|
-
# the FunctionProxy#set_result method on the +func+ parameter and
|
337
|
-
# indicate the return value that way.
|
338
|
-
#
|
339
|
-
# Example:
|
340
|
-
#
|
341
|
-
# db.create_function( "maim", 1, :text ) do |func, value|
|
342
|
-
# if value.nil?
|
343
|
-
# func.set_value nil
|
344
|
-
# else
|
345
|
-
# func.set_value value.split(//).sort.join
|
346
|
-
# end
|
347
|
-
# end
|
348
|
-
#
|
349
|
-
# puts db.get_first_value( "select maim(name) from table" )
|
350
|
-
def create_function( name, arity, type=nil, &block ) # :yields: func, *args
|
351
|
-
case type
|
352
|
-
when :numeric
|
353
|
-
type = SQLite::API::NUMERIC
|
354
|
-
when :text
|
355
|
-
type = SQLite::API::TEXT
|
356
|
-
when :args
|
357
|
-
type = SQLite::API::ARGS
|
358
|
-
end
|
359
|
-
|
360
|
-
callback = proc do |func,*args|
|
361
|
-
begin
|
362
|
-
block.call( FunctionProxy.new( func ), *args )
|
363
|
-
rescue Exception => e
|
364
|
-
SQLite::API.set_result_error( func, "#{e.message} (#{e.class})" )
|
365
|
-
end
|
366
|
-
end
|
367
|
-
|
368
|
-
SQLite::API.create_function( @handle, name, arity, callback )
|
369
|
-
SQLite::API.function_type( @handle, name, type ) if type
|
370
|
-
|
371
|
-
self
|
372
|
-
end
|
373
|
-
|
374
|
-
# Creates a new aggregate function for use in SQL statements. Aggregate
|
375
|
-
# functions are functions that apply over every row in the result set,
|
376
|
-
# instead of over just a single row. (A very common aggregate function
|
377
|
-
# is the "count" function, for determining the number of rows that match
|
378
|
-
# a query.)
|
379
|
-
#
|
380
|
-
# The new function will be added as +name+, with the given +arity+. (For
|
381
|
-
# variable arity functions, use -1 for the arity.) If +type+ is non-nil,
|
382
|
-
# it should be a value as described in #create_function.
|
383
|
-
#
|
384
|
-
# The +step+ parameter must be a proc object that accepts as its first
|
385
|
-
# parameter a FunctionProxy instance (representing the function
|
386
|
-
# invocation), with any subsequent parameters (up to the function's arity).
|
387
|
-
# The +step+ callback will be invoked once for each row of the result set.
|
388
|
-
#
|
389
|
-
# The +finalize+ parameter must be a +proc+ object that accepts only a
|
390
|
-
# single parameter, the FunctionProxy instance representing the current
|
391
|
-
# function invocation. It should invoke FunctionProxy#set_result to
|
392
|
-
# store the result of the function.
|
393
|
-
#
|
394
|
-
# Example:
|
395
|
-
#
|
396
|
-
# step = proc do |func, value|
|
397
|
-
# func[ :total ] ||= 0
|
398
|
-
# func[ :total ] += ( value ? value.length : 0 )
|
399
|
-
# end
|
400
|
-
#
|
401
|
-
# finalize = proc do |func|
|
402
|
-
# func.set_result( func[ :total ] || 0 )
|
403
|
-
# end
|
404
|
-
#
|
405
|
-
# db.create_aggregate( "lengths", 1, step, finalize, :numeric )
|
406
|
-
#
|
407
|
-
# puts db.get_first_value( "select lengths(name) from table" )
|
408
|
-
#
|
409
|
-
# See also #create_aggregate_handler for a more object-oriented approach to
|
410
|
-
# aggregate functions.
|
411
|
-
def create_aggregate( name, arity, step, finalize, type=nil )
|
412
|
-
case type
|
413
|
-
when :numeric
|
414
|
-
type = SQLite::API::NUMERIC
|
415
|
-
when :text
|
416
|
-
type = SQLite::API::TEXT
|
417
|
-
when :args
|
418
|
-
type = SQLite::API::ARGS
|
419
|
-
end
|
420
|
-
|
421
|
-
step_callback = proc do |func,*args|
|
422
|
-
ctx = SQLite::API.aggregate_context( func )
|
423
|
-
unless ctx[:__error]
|
424
|
-
begin
|
425
|
-
step.call( FunctionProxy.new( func, ctx ), *args )
|
426
|
-
rescue Exception => e
|
427
|
-
ctx[:__error] = e
|
428
|
-
end
|
429
|
-
end
|
430
|
-
end
|
431
|
-
|
432
|
-
finalize_callback = proc do |func|
|
433
|
-
ctx = SQLite::API.aggregate_context( func )
|
434
|
-
unless ctx[:__error]
|
435
|
-
begin
|
436
|
-
finalize.call( FunctionProxy.new( func, ctx ) )
|
437
|
-
rescue Exception => e
|
438
|
-
SQLite::API.set_result_error( func, "#{e.message} (#{e.class})" )
|
439
|
-
end
|
440
|
-
else
|
441
|
-
e = ctx[:__error]
|
442
|
-
SQLite::API.set_result_error( func, "#{e.message} (#{e.class})" )
|
443
|
-
end
|
444
|
-
end
|
445
|
-
|
446
|
-
SQLite::API.create_aggregate( @handle, name, arity,
|
447
|
-
step_callback, finalize_callback )
|
448
|
-
|
449
|
-
SQLite::API.function_type( @handle, name, type ) if type
|
450
|
-
|
451
|
-
self
|
452
|
-
end
|
453
|
-
|
454
|
-
# This is another approach to creating an aggregate function (see
|
455
|
-
# #create_aggregate). Instead of explicitly specifying the name,
|
456
|
-
# callbacks, arity, and type, you specify a factory object
|
457
|
-
# (the "handler") that knows how to obtain all of that information. The
|
458
|
-
# handler should respond to the following messages:
|
459
|
-
#
|
460
|
-
# +function_type+:: corresponds to the +type+ parameter of
|
461
|
-
# #create_aggregate. This is an optional message, and if
|
462
|
-
# the handler does not respond to it, the function type
|
463
|
-
# will not be set for this function.
|
464
|
-
# +arity+:: corresponds to the +arity+ parameter of #create_aggregate. This
|
465
|
-
# message is optional, and if the handler does not respond to it,
|
466
|
-
# the function will have an arity of -1.
|
467
|
-
# +name+:: this is the name of the function. The handler _must_ implement
|
468
|
-
# this message.
|
469
|
-
# +new+:: this must be implemented by the handler. It should return a new
|
470
|
-
# instance of the object that will handle a specific invocation of
|
471
|
-
# the function.
|
472
|
-
#
|
473
|
-
# The handler instance (the object returned by the +new+ message, described
|
474
|
-
# above), must respond to the following messages:
|
475
|
-
#
|
476
|
-
# +step+:: this is the method that will be called for each step of the
|
477
|
-
# aggregate function's evaluation. It should implement the same
|
478
|
-
# signature as the +step+ callback for #create_aggregate.
|
479
|
-
# +finalize+:: this is the method that will be called to finalize the
|
480
|
-
# aggregate function's evaluation. It should implement the
|
481
|
-
# same signature as the +finalize+ callback for
|
482
|
-
# #create_aggregate.
|
483
|
-
#
|
484
|
-
# Example:
|
485
|
-
#
|
486
|
-
# class LengthsAggregateHandler
|
487
|
-
# def self.function_type; :numeric; end
|
488
|
-
# def self.arity; 1; end
|
489
|
-
#
|
490
|
-
# def initialize
|
491
|
-
# @total = 0
|
492
|
-
# end
|
493
|
-
#
|
494
|
-
# def step( ctx, name )
|
495
|
-
# @total += ( name ? name.length : 0 )
|
496
|
-
# end
|
497
|
-
#
|
498
|
-
# def finalize( ctx )
|
499
|
-
# ctx.set_result( @total )
|
500
|
-
# end
|
501
|
-
# end
|
502
|
-
#
|
503
|
-
# db.create_aggregate_handler( LengthsAggregateHandler )
|
504
|
-
# puts db.get_first_value( "select lengths(name) from A" )
|
505
|
-
def create_aggregate_handler( handler )
|
506
|
-
type = nil
|
507
|
-
arity = -1
|
508
|
-
|
509
|
-
type = handler.function_type if handler.respond_to?(:function_type)
|
510
|
-
arity = handler.arity if handler.respond_to?(:arity)
|
511
|
-
name = handler.name
|
512
|
-
|
513
|
-
case type
|
514
|
-
when :numeric
|
515
|
-
type = SQLite::API::NUMERIC
|
516
|
-
when :text
|
517
|
-
type = SQLite::API::TEXT
|
518
|
-
when :args
|
519
|
-
type = SQLite::API::ARGS
|
520
|
-
end
|
521
|
-
|
522
|
-
step = proc do |func,*args|
|
523
|
-
ctx = SQLite::API.aggregate_context( func )
|
524
|
-
unless ctx[ :__error ]
|
525
|
-
ctx[ :handler ] ||= handler.new
|
526
|
-
begin
|
527
|
-
ctx[ :handler ].step( FunctionProxy.new( func, ctx ), *args )
|
528
|
-
rescue Exception => e
|
529
|
-
ctx[ :__error ] = e
|
530
|
-
end
|
531
|
-
end
|
532
|
-
end
|
533
|
-
|
534
|
-
finalize = proc do |func|
|
535
|
-
ctx = SQLite::API.aggregate_context( func )
|
536
|
-
unless ctx[ :__error ]
|
537
|
-
ctx[ :handler ] ||= handler.new
|
538
|
-
begin
|
539
|
-
ctx[ :handler ].finalize( FunctionProxy.new( func, ctx ) )
|
540
|
-
rescue Exception => e
|
541
|
-
ctx[ :__error ] = e
|
542
|
-
end
|
543
|
-
end
|
544
|
-
|
545
|
-
if ctx[ :__error ]
|
546
|
-
e = ctx[ :__error ]
|
547
|
-
SQLite::API.set_result_error( func, "#{e.message} (#{e.class})" )
|
548
|
-
end
|
549
|
-
end
|
550
|
-
|
551
|
-
SQLite::API.create_aggregate( @handle, name, arity, step, finalize )
|
552
|
-
SQLite::API.function_type( @handle, name, type ) if type
|
553
|
-
|
554
|
-
self
|
555
|
-
end
|
556
|
-
|
557
|
-
# Begins a new transaction. Note that nested transactions are not allowed
|
558
|
-
# by SQLite, so attempting to nest a transaction will result in a runtime
|
559
|
-
# exception.
|
560
|
-
#
|
561
|
-
# If a block is given, the database instance is yielded to it, and the
|
562
|
-
# transaction is committed when the block terminates. If the block
|
563
|
-
# raises an exception, a rollback will be performed instead. Note that if
|
564
|
-
# a block is given, #commit and #rollback should never be called
|
565
|
-
# explicitly or you'll get an error when the block terminates.
|
566
|
-
#
|
567
|
-
# If a block is not given, it is the caller's responsibility to end the
|
568
|
-
# transaction explicitly, either by calling #commit, or by calling
|
569
|
-
# #rollback.
|
570
|
-
def transaction
|
571
|
-
execute "begin transaction"
|
572
|
-
@transaction_active = true
|
573
|
-
|
574
|
-
if block_given?
|
575
|
-
abort = false
|
576
|
-
begin
|
577
|
-
yield self
|
578
|
-
rescue Exception
|
579
|
-
abort = true
|
580
|
-
raise
|
581
|
-
ensure
|
582
|
-
abort and rollback or commit
|
583
|
-
end
|
584
|
-
end
|
585
|
-
|
586
|
-
true
|
587
|
-
end
|
588
|
-
|
589
|
-
# Commits the current transaction. If there is no current transaction,
|
590
|
-
# this will cause an error to be raised. This returns +true+, in order
|
591
|
-
# to allow it to be used in idioms like
|
592
|
-
# <tt>abort? and rollback or commit</tt>.
|
593
|
-
def commit
|
594
|
-
execute "commit transaction"
|
595
|
-
@transaction_active = false
|
596
|
-
true
|
597
|
-
end
|
598
|
-
|
599
|
-
# Rolls the current transaction back. If there is no current transaction,
|
600
|
-
# this will cause an error to be raised. This returns +true+, in order
|
601
|
-
# to allow it to be used in idioms like
|
602
|
-
# <tt>abort? and rollback or commit</tt>.
|
603
|
-
def rollback
|
604
|
-
execute "rollback transaction"
|
605
|
-
@transaction_active = false
|
606
|
-
true
|
607
|
-
end
|
608
|
-
|
609
|
-
# Returns +true+ if there is a transaction active, and +false+ otherwise.
|
610
|
-
def transaction_active?
|
611
|
-
@transaction_active
|
612
|
-
end
|
613
|
-
|
614
|
-
# A helper class for dealing with custom functions (see #create_function,
|
615
|
-
# #create_aggregate, and #create_aggregate_handler). It encapsulates the
|
616
|
-
# opaque function object that represents the current invocation. It also
|
617
|
-
# provides more convenient access to the API functions that operate on
|
618
|
-
# the function object.
|
619
|
-
#
|
620
|
-
# This class will almost _always_ be instantiated indirectly, by working
|
621
|
-
# with the create methods mentioned above.
|
622
|
-
class FunctionProxy
|
623
|
-
|
624
|
-
# Create a new FunctionProxy that encapsulates the given +func+ object.
|
625
|
-
# If context is non-nil, the functions context will be set to that. If
|
626
|
-
# it is non-nil, it must quack like a Hash. If it is nil, then none of
|
627
|
-
# the context functions will be available.
|
628
|
-
def initialize( func, context=nil )
|
629
|
-
@func = func
|
630
|
-
@context = context
|
631
|
-
end
|
632
|
-
|
633
|
-
# Set the result of the function to the given value. The function will
|
634
|
-
# then return this value.
|
635
|
-
def set_result( result )
|
636
|
-
SQLite::API.set_result( @func, result )
|
637
|
-
end
|
638
|
-
|
639
|
-
# Set the result of the function to the given error message, which must
|
640
|
-
# be a string. The function will then return that error.
|
641
|
-
def set_error( error )
|
642
|
-
SQLite::API.set_result_error( @func, error )
|
643
|
-
end
|
644
|
-
|
645
|
-
# (Only available to aggregate functions.) Returns the number of rows
|
646
|
-
# that the aggregate has processed so far. This will include the current
|
647
|
-
# row, and so will always return at least 1.
|
648
|
-
def count
|
649
|
-
ensure_aggregate!
|
650
|
-
SQLite::API.aggregate_count( @func )
|
651
|
-
end
|
652
|
-
|
653
|
-
# Returns the value with the given key from the context. This is only
|
654
|
-
# available to aggregate functions.
|
655
|
-
def []( key )
|
656
|
-
ensure_aggregate!
|
657
|
-
@context[ key ]
|
658
|
-
end
|
659
|
-
|
660
|
-
# Sets the value with the given key in the context. This is only
|
661
|
-
# available to aggregate functions.
|
662
|
-
def []=( key, value )
|
663
|
-
ensure_aggregate!
|
664
|
-
@context[ key ] = value
|
665
|
-
end
|
666
|
-
|
667
|
-
# A function for performing a sanity check, to ensure that the function
|
668
|
-
# being invoked is an aggregate function. This is implied by the
|
669
|
-
# existence of the context variable.
|
670
|
-
def ensure_aggregate!
|
671
|
-
unless @context
|
672
|
-
raise Exceptions::MisuseException, "function is not an aggregate"
|
673
|
-
end
|
674
|
-
end
|
675
|
-
private :ensure_aggregate!
|
676
|
-
|
677
|
-
end
|
678
|
-
|
679
|
-
end
|
680
|
-
|
681
|
-
end
|
682
|
-
|