extralite 1.27 → 2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,333 +0,0 @@
1
- #include <stdio.h>
2
- #include "extralite.h"
3
-
4
- /*
5
- * Document-class: Extralite::PreparedStatement
6
- *
7
- * This class represents a prepared statement.
8
- */
9
-
10
- VALUE cPreparedStatement;
11
-
12
- static size_t PreparedStatement_size(const void *ptr) {
13
- return sizeof(PreparedStatement_t);
14
- }
15
-
16
- static void PreparedStatement_mark(void *ptr) {
17
- PreparedStatement_t *stmt = ptr;
18
- rb_gc_mark(stmt->db);
19
- rb_gc_mark(stmt->sql);
20
- }
21
-
22
- static void PreparedStatement_free(void *ptr) {
23
- PreparedStatement_t *stmt = ptr;
24
- if (stmt->stmt) sqlite3_finalize(stmt->stmt);
25
- free(ptr);
26
- }
27
-
28
- static const rb_data_type_t PreparedStatement_type = {
29
- "PreparedStatement",
30
- {PreparedStatement_mark, PreparedStatement_free, PreparedStatement_size,},
31
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
32
- };
33
-
34
- static VALUE PreparedStatement_allocate(VALUE klass) {
35
- PreparedStatement_t *stmt = ALLOC(PreparedStatement_t);
36
- stmt->db = Qnil;
37
- stmt->sqlite3_db = NULL;
38
- stmt->stmt = NULL;
39
- return TypedData_Wrap_Struct(klass, &PreparedStatement_type, stmt);
40
- }
41
-
42
- #define GetPreparedStatement(obj, stmt) \
43
- TypedData_Get_Struct((obj), PreparedStatement_t, &PreparedStatement_type, (stmt))
44
-
45
- /* call-seq: initialize(db, sql)
46
- *
47
- * Initializes a new SQLite prepared statement with the given path.
48
- */
49
- VALUE PreparedStatement_initialize(VALUE self, VALUE db, VALUE sql) {
50
- PreparedStatement_t *stmt;
51
- GetPreparedStatement(self, stmt);
52
-
53
- sql = rb_funcall(sql, ID_strip, 0);
54
- if (!RSTRING_LEN(sql))
55
- rb_raise(cError, "Cannot prepare an empty SQL query");
56
-
57
- stmt->db = db;
58
- stmt->db_struct = Database_struct(db);
59
- stmt->sqlite3_db = Database_sqlite3_db(db);
60
- stmt->sql = sql;
61
-
62
- prepare_single_stmt(stmt->sqlite3_db, &stmt->stmt, sql);
63
-
64
- return Qnil;
65
- }
66
-
67
- static inline VALUE PreparedStatement_perform_query(int argc, VALUE *argv, VALUE self, VALUE (*call)(query_ctx *)) {
68
- PreparedStatement_t *stmt;
69
- GetPreparedStatement(self, stmt);
70
-
71
- if (!stmt->stmt)
72
- rb_raise(cError, "Prepared statement is closed");
73
-
74
- if (stmt->db_struct->trace_block != Qnil) rb_funcall(stmt->db_struct->trace_block, ID_call, 1, stmt->sql);
75
-
76
- sqlite3_reset(stmt->stmt);
77
- sqlite3_clear_bindings(stmt->stmt);
78
- bind_all_parameters(stmt->stmt, argc, argv);
79
- query_ctx ctx = { self, stmt->sqlite3_db, stmt->stmt };
80
- return call(&ctx);
81
- }
82
-
83
- /* call-seq:
84
- * query(sql, *parameters, &block) -> [...]
85
- * query_hash(sql, *parameters, &block) -> [...]
86
- *
87
- * Runs a query returning rows as hashes (with symbol keys). If a block is
88
- * given, it will be called for each row. Otherwise, an array containing all
89
- * rows is returned.
90
- *
91
- * Query parameters to be bound to placeholders in the query can be specified as
92
- * a list of values or as a hash mapping parameter names to values. When
93
- * parameters are given as a least, the query should specify parameters using
94
- * `?`:
95
- *
96
- * db.query('select * from foo where x = ?', 42)
97
- *
98
- * Named placeholders are specified using `:`. The placeholder values are
99
- * specified using a hash, where keys are either strings are symbols. String
100
- * keys can include or omit the `:` prefix. The following are equivalent:
101
- *
102
- * db.query('select * from foo where x = :bar', bar: 42)
103
- * db.query('select * from foo where x = :bar', 'bar' => 42)
104
- * db.query('select * from foo where x = :bar', ':bar' => 42)
105
- */
106
- VALUE PreparedStatement_query_hash(int argc, VALUE *argv, VALUE self) {
107
- return PreparedStatement_perform_query(argc, argv, self, safe_query_hash);
108
- }
109
-
110
- /* call-seq:
111
- * stmt.query_ary(sql, *parameters, &block) -> [...]
112
- *
113
- * Runs a query returning rows as arrays. If a block is given, it will be called
114
- * for each row. Otherwise, an array containing all rows is returned.
115
- *
116
- * Query parameters to be bound to placeholders in the query can be specified as
117
- * a list of values or as a hash mapping parameter names to values. When
118
- * parameters are given as a least, the query should specify parameters using
119
- * `?`:
120
- *
121
- * db.query_ary('select * from foo where x = ?', 42)
122
- *
123
- * Named placeholders are specified using `:`. The placeholder values are
124
- * specified using a hash, where keys are either strings are symbols. String
125
- * keys can include or omit the `:` prefix. The following are equivalent:
126
- *
127
- * db.query_ary('select * from foo where x = :bar', bar: 42)
128
- * db.query_ary('select * from foo where x = :bar', 'bar' => 42)
129
- * db.query_ary('select * from foo where x = :bar', ':bar' => 42)
130
- */
131
- VALUE PreparedStatement_query_ary(int argc, VALUE *argv, VALUE self) {
132
- return PreparedStatement_perform_query(argc, argv, self, safe_query_ary);
133
- }
134
-
135
- /* call-seq:
136
- * stmt.query_single_row(sql, *parameters) -> {...}
137
- *
138
- * Runs a query returning a single row as a hash.
139
- *
140
- * Query parameters to be bound to placeholders in the query can be specified as
141
- * a list of values or as a hash mapping parameter names to values. When
142
- * parameters are given as a least, the query should specify parameters using
143
- * `?`:
144
- *
145
- * db.query_single_row('select * from foo where x = ?', 42)
146
- *
147
- * Named placeholders are specified using `:`. The placeholder values are
148
- * specified using a hash, where keys are either strings are symbols. String
149
- * keys can include or omit the `:` prefix. The following are equivalent:
150
- *
151
- * db.query_single_row('select * from foo where x = :bar', bar: 42)
152
- * db.query_single_row('select * from foo where x = :bar', 'bar' => 42)
153
- * db.query_single_row('select * from foo where x = :bar', ':bar' => 42)
154
- */
155
- VALUE PreparedStatement_query_single_row(int argc, VALUE *argv, VALUE self) {
156
- return PreparedStatement_perform_query(argc, argv, self, safe_query_single_row);
157
- }
158
-
159
- /* call-seq:
160
- * stmt.query_single_column(sql, *parameters, &block) -> [...]
161
- *
162
- * Runs a query returning single column values. If a block is given, it will be called
163
- * for each value. Otherwise, an array containing all values is returned.
164
- *
165
- * Query parameters to be bound to placeholders in the query can be specified as
166
- * a list of values or as a hash mapping parameter names to values. When
167
- * parameters are given as a least, the query should specify parameters using
168
- * `?`:
169
- *
170
- * db.query_single_column('select x from foo where x = ?', 42)
171
- *
172
- * Named placeholders are specified using `:`. The placeholder values are
173
- * specified using a hash, where keys are either strings are symbols. String
174
- * keys can include or omit the `:` prefix. The following are equivalent:
175
- *
176
- * db.query_single_column('select x from foo where x = :bar', bar: 42)
177
- * db.query_single_column('select x from foo where x = :bar', 'bar' => 42)
178
- * db.query_single_column('select x from foo where x = :bar', ':bar' => 42)
179
- */
180
- VALUE PreparedStatement_query_single_column(int argc, VALUE *argv, VALUE self) {
181
- return PreparedStatement_perform_query(argc, argv, self, safe_query_single_column);
182
- }
183
-
184
- /* call-seq:
185
- * stmt.query_single_value(sql, *parameters) -> value
186
- *
187
- * Runs a query returning a single value from the first row.
188
- *
189
- * Query parameters to be bound to placeholders in the query can be specified as
190
- * a list of values or as a hash mapping parameter names to values. When
191
- * parameters are given as a least, the query should specify parameters using
192
- * `?`:
193
- *
194
- * db.query_single_value('select x from foo where x = ?', 42)
195
- *
196
- * Named placeholders are specified using `:`. The placeholder values are
197
- * specified using a hash, where keys are either strings are symbols. String
198
- * keys can include or omit the `:` prefix. The following are equivalent:
199
- *
200
- * db.query_single_value('select x from foo where x = :bar', bar: 42)
201
- * db.query_single_value('select x from foo where x = :bar', 'bar' => 42)
202
- * db.query_single_value('select x from foo where x = :bar', ':bar' => 42)
203
- */
204
- VALUE PreparedStatement_query_single_value(int argc, VALUE *argv, VALUE self) {
205
- return PreparedStatement_perform_query(argc, argv, self, safe_query_single_value);
206
- }
207
-
208
- /* call-seq:
209
- * stmt.execute_multi(params_array) -> changes
210
- *
211
- * Executes the prepared statment for each list of parameters in params_array.
212
- * Returns the number of changes effected. This method is designed for inserting
213
- * multiple records.
214
- *
215
- * stmt = db.prepare('insert into foo values (?, ?, ?)')
216
- * records = [
217
- * [1, 2, 3],
218
- * [4, 5, 6]
219
- * ]
220
- * stmt.execute_multi_query(records)
221
- *
222
- */
223
- VALUE PreparedStatement_execute_multi(VALUE self, VALUE params_array) {
224
- PreparedStatement_t *stmt;
225
- GetPreparedStatement(self, stmt);
226
-
227
- if (!stmt->stmt)
228
- rb_raise(cError, "Prepared statement is closed");
229
-
230
- query_ctx ctx = { self, stmt->sqlite3_db, stmt->stmt, params_array };
231
- return safe_execute_multi(&ctx);
232
- }
233
-
234
- /* call-seq:
235
- * stmt.database -> database
236
- * stmt.db -> database
237
- *
238
- * Returns the database associated with the prepared statement.
239
- */
240
- VALUE PreparedStatement_database(VALUE self) {
241
- PreparedStatement_t *stmt;
242
- GetPreparedStatement(self, stmt);
243
- return stmt->db;
244
- }
245
-
246
- /* call-seq:
247
- * stmt.sql -> sql
248
- *
249
- * Returns the SQL query used for the prepared statement.
250
- */
251
- VALUE PreparedStatement_sql(VALUE self) {
252
- PreparedStatement_t *stmt;
253
- GetPreparedStatement(self, stmt);
254
- return stmt->sql;
255
- }
256
-
257
- /* call-seq:
258
- * stmt.columns -> columns
259
- *
260
- * Returns the column names for the prepared statement without running it.
261
- */
262
- VALUE PreparedStatement_columns(VALUE self) {
263
- return PreparedStatement_perform_query(0, NULL, self, safe_query_columns);
264
- }
265
-
266
- /* call-seq:
267
- * stmt.close -> stmt
268
- *
269
- * Closes the prepared statement. Running a closed prepared statement will raise
270
- * an error.
271
- */
272
- VALUE PreparedStatement_close(VALUE self) {
273
- PreparedStatement_t *stmt;
274
- GetPreparedStatement(self, stmt);
275
- if (stmt->stmt) {
276
- sqlite3_finalize(stmt->stmt);
277
- stmt->stmt = NULL;
278
- }
279
- return self;
280
- }
281
-
282
- /* call-seq:
283
- * stmt.closed? -> closed
284
- *
285
- * Returns true if the prepared statement is closed.
286
- */
287
- VALUE PreparedStatement_closed_p(VALUE self) {
288
- PreparedStatement_t *stmt;
289
- GetPreparedStatement(self, stmt);
290
-
291
- return stmt->stmt ? Qfalse : Qtrue;
292
- }
293
-
294
- /* call-seq:
295
- * stmt.status(op[, reset]) -> value
296
- *
297
- * Returns the current status value for the given op. To reset the value, pass
298
- * true as reset.
299
- */
300
- VALUE PreparedStatement_status(int argc, VALUE* argv, VALUE self) {
301
- VALUE op, reset;
302
-
303
- rb_scan_args(argc, argv, "11", &op, &reset);
304
-
305
- PreparedStatement_t *stmt;
306
- GetPreparedStatement(self, stmt);
307
-
308
- int value = sqlite3_stmt_status(stmt->stmt, NUM2INT(op), RTEST(reset) ? 1 : 0);
309
- return INT2NUM(value);
310
- }
311
-
312
- void Init_ExtralitePreparedStatement(void) {
313
- VALUE mExtralite = rb_define_module("Extralite");
314
-
315
- cPreparedStatement = rb_define_class_under(mExtralite, "PreparedStatement", rb_cObject);
316
- rb_define_alloc_func(cPreparedStatement, PreparedStatement_allocate);
317
-
318
- rb_define_method(cPreparedStatement, "close", PreparedStatement_close, 0);
319
- rb_define_method(cPreparedStatement, "closed?", PreparedStatement_closed_p, 0);
320
- rb_define_method(cPreparedStatement, "columns", PreparedStatement_columns, 0);
321
- rb_define_method(cPreparedStatement, "database", PreparedStatement_database, 0);
322
- rb_define_method(cPreparedStatement, "db", PreparedStatement_database, 0);
323
- rb_define_method(cPreparedStatement, "execute_multi", PreparedStatement_execute_multi, 1);
324
- rb_define_method(cPreparedStatement, "initialize", PreparedStatement_initialize, 2);
325
- rb_define_method(cPreparedStatement, "query", PreparedStatement_query_hash, -1);
326
- rb_define_method(cPreparedStatement, "query_hash", PreparedStatement_query_hash, -1);
327
- rb_define_method(cPreparedStatement, "query_ary", PreparedStatement_query_ary, -1);
328
- rb_define_method(cPreparedStatement, "query_single_row", PreparedStatement_query_single_row, -1);
329
- rb_define_method(cPreparedStatement, "query_single_column", PreparedStatement_query_single_column, -1);
330
- rb_define_method(cPreparedStatement, "query_single_value", PreparedStatement_query_single_value, -1);
331
- rb_define_method(cPreparedStatement, "sql", PreparedStatement_sql, 0);
332
- rb_define_method(cPreparedStatement, "status", PreparedStatement_status, -1);
333
- }
@@ -1,225 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'helper'
4
-
5
- class PreparedStatementTest < MiniTest::Test
6
- def setup
7
- @db = Extralite::Database.new(':memory:')
8
- @db.query('create table if not exists t (x,y,z)')
9
- @db.query('delete from t')
10
- @db.query('insert into t values (1, 2, 3)')
11
- @db.query('insert into t values (4, 5, 6)')
12
-
13
- @stmt = @db.prepare('select * from t where x = ?')
14
- end
15
-
16
- # def test_foo
17
- # stmt = @db.prepare('select 1')
18
- # assert_equal 1, stmt.query_single_value
19
- # end
20
-
21
- def test_prepared_statement_props
22
- assert_kind_of Extralite::PreparedStatement, @stmt
23
- assert_equal @db, @stmt.database
24
- assert_equal @db, @stmt.db
25
- assert_equal 'select * from t where x = ?', @stmt.sql
26
- end
27
-
28
- def test_prepared_statement_query
29
- assert_equal [{ x: 1, y: 2, z: 3 }], @stmt.query(1)
30
-
31
- buf = []
32
- @stmt.query(1) { |r| buf << r }
33
- assert_equal [{ x: 1, y: 2, z: 3 }], buf
34
- end
35
-
36
- def test_prepared_statement_with_invalid_sql
37
- assert_raises(Extralite::SQLError) { @db.prepare('blah') }
38
- end
39
-
40
- def test_prepared_statement_with_multiple_queries
41
- error = begin; @db.prepare('select 1; select 2'); rescue => e; error = e; end
42
- assert_equal Extralite::Error, error.class
43
- end
44
-
45
- def test_prepared_statement_query_hash
46
- r = @stmt.query_hash(4)
47
- assert_equal [{x: 4, y: 5, z: 6}], r
48
-
49
- r = @stmt.query_hash(5)
50
- assert_equal [], r
51
- end
52
-
53
- def test_prepared_statement_query_ary
54
- r = @stmt.query_ary(1)
55
- assert_equal [[1, 2, 3]], r
56
-
57
- r = @stmt.query_ary(2)
58
- assert_equal [], r
59
- end
60
-
61
- def test_prepared_statement_query_single_row
62
- r = @stmt.query_single_row(4)
63
- assert_equal({ x: 4, y: 5, z: 6 }, r)
64
-
65
- r = @stmt.query_single_row(5)
66
- assert_nil r
67
- end
68
-
69
- def test_prepared_statement_query_single_column
70
- stmt =
71
- r = @db.prepare('select y from t').query_single_column
72
- assert_equal [2, 5], r
73
-
74
- r = @db.prepare('select y from t where x = 2').query_single_column
75
- assert_equal [], r
76
- end
77
-
78
- def test_prepared_statement_query_single_value
79
- r = @db.prepare('select z from t order by Z desc limit 1').query_single_value
80
- assert_equal 6, r
81
-
82
- r = @db.prepare('select z from t where x = 2').query_single_value
83
- assert_nil r
84
- end
85
-
86
- def test_prepared_statement_multiple_statements
87
- assert_raises(Extralite::Error) {
88
- @db.prepare("insert into t values ('a', 'b', 'c'); insert into t values ('d', 'e', 'f');")
89
- }
90
- end
91
-
92
- def test_prepared_statement_multiple_statements_with_bad_sql
93
- error = nil
94
- begin
95
- stmt =@db.prepare("insert into t values foo; insert into t values ('d', 'e', 'f');")
96
- stmt.query
97
- rescue => error
98
- end
99
-
100
- assert_kind_of Extralite::SQLError, error
101
- assert_equal 'near "foo": syntax error', error.message
102
- end
103
-
104
- def test_prepared_statement_repeated_execution_missing_param
105
- r = @stmt.query_hash(4)
106
- assert_equal [{x: 4, y: 5, z: 6}], r
107
-
108
- r = @stmt.query_hash
109
- assert_equal [], r
110
- end
111
-
112
- def test_prepared_statement_empty_sql
113
- assert_raises(Extralite::Error) { @db.prepare(' ') }
114
-
115
- r = @db.prepare('select 1 as foo; ').query
116
- assert_equal [{ foo: 1 }], r
117
- end
118
-
119
- def test_prepared_statement_parameter_binding_simple
120
- r = @db.prepare('select x, y, z from t where x = ?').query(1)
121
- assert_equal [{ x: 1, y: 2, z: 3 }], r
122
-
123
- r = @db.prepare('select x, y, z from t where z = ?').query(6)
124
- assert_equal [{ x: 4, y: 5, z: 6 }], r
125
- end
126
-
127
- def test_prepared_statement_parameter_binding_with_index
128
- r = @db.prepare('select x, y, z from t where x = ?2').query(0, 1)
129
- assert_equal [{ x: 1, y: 2, z: 3 }], r
130
-
131
- r = @db.prepare('select x, y, z from t where z = ?3').query(3, 4, 6)
132
- assert_equal [{ x: 4, y: 5, z: 6 }], r
133
- end
134
-
135
- def test_prepared_statement_parameter_binding_with_name
136
- r = @db.prepare('select x, y, z from t where x = :x').query(x: 1, y: 2)
137
- assert_equal [{ x: 1, y: 2, z: 3 }], r
138
-
139
- r = @db.prepare('select x, y, z from t where z = :zzz').query('zzz' => 6)
140
- assert_equal [{ x: 4, y: 5, z: 6 }], r
141
-
142
- r = @db.prepare('select x, y, z from t where z = :bazzz').query(':bazzz' => 6)
143
- assert_equal [{ x: 4, y: 5, z: 6 }], r
144
- end
145
-
146
- def test_prepared_statement_parameter_binding_with_index_key
147
- r = @db.prepare('select x, y, z from t where z = ?').query(1 => 3)
148
- assert_equal [{ x: 1, y: 2, z: 3 }], r
149
-
150
- r = @db.prepare('select x, y, z from t where x = ?2').query(1 => 42, 2 => 4)
151
- assert_equal [{ x: 4, y: 5, z: 6 }], r
152
- end
153
-
154
- def test_prepared_statement_value_casting
155
- r = @db.prepare("select 'abc'").query_single_value
156
- assert_equal 'abc', r
157
-
158
- r = @db.prepare('select 123').query_single_value
159
- assert_equal 123, r
160
-
161
- r = @db.prepare('select 12.34').query_single_value
162
- assert_equal 12.34, r
163
-
164
- r = @db.prepare('select zeroblob(4)').query_single_value
165
- assert_equal "\x00\x00\x00\x00", r
166
-
167
- r = @db.prepare('select null').query_single_value
168
- assert_nil r
169
- end
170
-
171
- def test_prepared_statement_columns
172
- r = @db.prepare("select 'abc' as a, 'def' as b").columns
173
- assert_equal [:a, :b], r
174
- end
175
-
176
- def test_prepared_statement_close
177
- p = @db.prepare("select 'abc'")
178
-
179
- assert_equal false, p.closed?
180
-
181
- p.close
182
- assert_equal true, p.closed?
183
-
184
- p.close
185
- assert_equal true, p.closed?
186
-
187
- assert_raises { p.query_single_value }
188
- end
189
-
190
- def test_prepared_statement_execute_multi
191
- @db.query('create table foo (a, b, c)')
192
- assert_equal [], @db.query('select * from foo')
193
-
194
- records = [
195
- [1, '2', 3],
196
- ['4', 5, 6]
197
- ]
198
-
199
- p = @db.prepare('insert into foo values (?, ?, ?)')
200
- changes = p.execute_multi(records)
201
-
202
- assert_equal 2, changes
203
- assert_equal [
204
- { a: 1, b: '2', c: 3 },
205
- { a: '4', b: 5, c: 6 }
206
- ], @db.query('select * from foo')
207
- end
208
-
209
- def test_prepared_statement_status
210
- assert_equal 0, @stmt.status(Extralite::SQLITE_STMTSTATUS_RUN)
211
- @stmt.query
212
- assert_equal 1, @stmt.status(Extralite::SQLITE_STMTSTATUS_RUN)
213
- @stmt.query
214
- assert_equal 2, @stmt.status(Extralite::SQLITE_STMTSTATUS_RUN)
215
- @stmt.query
216
- assert_equal 3, @stmt.status(Extralite::SQLITE_STMTSTATUS_RUN, true)
217
- assert_equal 0, @stmt.status(Extralite::SQLITE_STMTSTATUS_RUN)
218
- end
219
-
220
- def test_query_after_db_close
221
- assert_equal [{ x: 4, y: 5, z: 6}], @stmt.query(4)
222
- @db.close
223
- assert_equal [{ x: 4, y: 5, z: 6}], @stmt.query(4)
224
- end
225
- end