sqlite3-ffi 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/lib/sqlite3/database.rb +20 -30
- data/lib/sqlite3/ffi/aggregator.rb +5 -18
- data/lib/sqlite3/ffi/backup.rb +10 -3
- data/lib/sqlite3/ffi/database.rb +99 -67
- data/lib/sqlite3/ffi/exception.rb +59 -0
- data/lib/sqlite3/ffi/functions.rb +76 -43
- data/lib/sqlite3/ffi/statement.rb +29 -19
- data/lib/sqlite3/ffi/utils.rb +25 -9
- data/lib/sqlite3/ffi/version.rb +1 -1
- data/lib/sqlite3/resultset.rb +6 -2
- data/lib/sqlite3/statement.rb +19 -3
- data/lib/sqlite3/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 88fbf37c663c6a41c1f491c411813c2dec850f22a00bb04fb970876c0b00cf94
|
|
4
|
+
data.tar.gz: 04327ad75659e1ca926446b21f72ecc974aec0dea1fe4c1176adab29e1d0c63e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '0584a32cb0eff8e49cd76ffaa48756951f83cbaa897360289ab2c92e7a96adcb8ae3bda2b2558145a3fb788f3358e2e4834fed00134a2635e8a49b89c6ecaf81'
|
|
7
|
+
data.tar.gz: 0d9fcea2a566d141dc167b1ba760dbbc313e0a9ff9865cb761d1e9e803c5119807ce2055d5976618a818a9561ff8c97bfa7d792ccec9ed49df679f8db7c0a94b
|
data/CHANGELOG.md
CHANGED
data/lib/sqlite3/database.rb
CHANGED
|
@@ -579,6 +579,25 @@ module SQLite3
|
|
|
579
579
|
self
|
|
580
580
|
end
|
|
581
581
|
|
|
582
|
+
# The "handler" that #define_aggregator hands to define_aggregator2, which
|
|
583
|
+
# only needs something that responds to +new+ (a fresh aggregator for each
|
|
584
|
+
# aggregate invocation) and +arity+.
|
|
585
|
+
class AggregatorFactory # :nodoc:
|
|
586
|
+
def initialize(template)
|
|
587
|
+
@template = template
|
|
588
|
+
end
|
|
589
|
+
|
|
590
|
+
def new
|
|
591
|
+
@template.clone
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
# this is what sqlite3_obj_method_arity did before
|
|
595
|
+
def arity
|
|
596
|
+
@template.method(:step).arity
|
|
597
|
+
end
|
|
598
|
+
end
|
|
599
|
+
private_constant :AggregatorFactory
|
|
600
|
+
|
|
582
601
|
# Define an aggregate function named +name+ using a object template
|
|
583
602
|
# object +aggregator+. +aggregator+ must respond to +step+ and +finalize+.
|
|
584
603
|
# +step+ will be called with row information and +finalize+ must return the
|
|
@@ -592,36 +611,7 @@ module SQLite3
|
|
|
592
611
|
def define_aggregator(name, aggregator)
|
|
593
612
|
# Previously, this has been implemented in C. Now this is just yet
|
|
594
613
|
# another compatibility shim
|
|
595
|
-
|
|
596
|
-
@template = aggregator
|
|
597
|
-
@name = name
|
|
598
|
-
|
|
599
|
-
def self.template
|
|
600
|
-
@template
|
|
601
|
-
end
|
|
602
|
-
|
|
603
|
-
def self.name
|
|
604
|
-
@name
|
|
605
|
-
end
|
|
606
|
-
|
|
607
|
-
def self.arity
|
|
608
|
-
# this is what sqlite3_obj_method_arity did before
|
|
609
|
-
@template.method(:step).arity
|
|
610
|
-
end
|
|
611
|
-
|
|
612
|
-
def initialize
|
|
613
|
-
@klass = self.class.template.clone
|
|
614
|
-
end
|
|
615
|
-
|
|
616
|
-
def step(*args)
|
|
617
|
-
@klass.step(*args)
|
|
618
|
-
end
|
|
619
|
-
|
|
620
|
-
def finalize
|
|
621
|
-
@klass.finalize
|
|
622
|
-
end
|
|
623
|
-
end
|
|
624
|
-
define_aggregator2(proxy, name)
|
|
614
|
+
define_aggregator2(AggregatorFactory.new(aggregator), name)
|
|
625
615
|
self
|
|
626
616
|
end
|
|
627
617
|
|
|
@@ -1,17 +1,8 @@
|
|
|
1
1
|
module SQLite3
|
|
2
2
|
module FFI
|
|
3
|
-
|
|
4
|
-
attr_reader :handler_klass, :instances
|
|
3
|
+
AggregatorWrapper = Struct.new(:handler_klass, :instances)
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
@handler_klass = handler_klass
|
|
8
|
-
@instances = []
|
|
9
|
-
end
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
class AggregatorInstance
|
|
13
|
-
attr_accessor :handler_instance
|
|
14
|
-
end
|
|
5
|
+
AggregatorInstance = Struct.new(:handler_instance)
|
|
15
6
|
|
|
16
7
|
def self.aggregate_instance(ctx)
|
|
17
8
|
aw = FFI.unwrap(CApi.sqlite3_user_data(ctx))
|
|
@@ -64,26 +55,22 @@ module SQLite3
|
|
|
64
55
|
end
|
|
65
56
|
|
|
66
57
|
AGGREGATOR_STEP = ::FFI::Function.new(:void, [:pointer, :int, :pointer]) do |ctx, argc, argv|
|
|
67
|
-
|
|
58
|
+
FFI.protect_callback do
|
|
68
59
|
inst = aggregate_instance(ctx)
|
|
69
60
|
handler_instance = inst.handler_instance
|
|
70
61
|
params = argv.read_array_of_pointer(argc).map { |v| FFI.sqlite3val2rb(v) }
|
|
71
62
|
handler_instance.step(*params)
|
|
72
|
-
rescue => e
|
|
73
|
-
FFI.rb_errinfo = e
|
|
74
63
|
end
|
|
75
64
|
end
|
|
76
65
|
|
|
77
66
|
AGGREGATOR_FINAL = ::FFI::Function.new(:void, [:pointer]) do |ctx|
|
|
78
|
-
|
|
67
|
+
FFI.protect_callback do
|
|
79
68
|
inst = aggregate_instance(ctx)
|
|
80
69
|
handler_instance = inst.handler_instance
|
|
81
70
|
result = handler_instance.finalize
|
|
82
71
|
FFI.set_sqlite3_func_result(ctx, result)
|
|
83
|
-
aggregate_instance_destroy(ctx)
|
|
84
|
-
rescue => e
|
|
85
|
-
FFI.rb_errinfo = e
|
|
86
72
|
end
|
|
73
|
+
aggregate_instance_destroy(ctx)
|
|
87
74
|
end
|
|
88
75
|
end
|
|
89
76
|
end
|
data/lib/sqlite3/ffi/backup.rb
CHANGED
|
@@ -8,12 +8,12 @@ module SQLite3
|
|
|
8
8
|
raise ArgumentError, "cannot backup to a closed database"
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
ddb = dstdb.instance_variable_get(:@db
|
|
11
|
+
ddb = dstdb.instance_variable_get(:@ctx).db
|
|
12
12
|
@p =
|
|
13
13
|
FFI::CApi.sqlite3_backup_init(
|
|
14
14
|
ddb,
|
|
15
15
|
FFI.string_value(dstname),
|
|
16
|
-
srcdb.instance_variable_get(:@db
|
|
16
|
+
srcdb.instance_variable_get(:@ctx).db,
|
|
17
17
|
FFI.string_value(srcname)
|
|
18
18
|
)
|
|
19
19
|
|
|
@@ -24,8 +24,15 @@ module SQLite3
|
|
|
24
24
|
|
|
25
25
|
def step(n_page)
|
|
26
26
|
require_open_backup
|
|
27
|
+
pages = n_page.to_i
|
|
27
28
|
|
|
28
|
-
FFI::
|
|
29
|
+
frame = FFI::CallbackFrame.new
|
|
30
|
+
FFI.push_callback_frame(frame)
|
|
31
|
+
status = FFI::CApi.sqlite3_backup_step(@p, pages)
|
|
32
|
+
if (callback_state = FFI.pop_callback_frame(frame))
|
|
33
|
+
raise callback_state
|
|
34
|
+
end
|
|
35
|
+
status
|
|
29
36
|
end
|
|
30
37
|
|
|
31
38
|
def finish
|
data/lib/sqlite3/ffi/database.rb
CHANGED
|
@@ -6,21 +6,21 @@ module SQLite3
|
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
def closed?
|
|
9
|
-
@db.nil?
|
|
9
|
+
@ctx&.db.nil?
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def total_changes
|
|
13
13
|
require_open_db
|
|
14
14
|
|
|
15
|
-
FFI::CApi.sqlite3_total_changes(@db)
|
|
15
|
+
FFI::CApi.sqlite3_total_changes(@ctx.db)
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def trace(blk = nil, &block)
|
|
19
19
|
require_open_db
|
|
20
20
|
|
|
21
21
|
blk ||= block
|
|
22
|
-
@
|
|
23
|
-
FFI::CApi.sqlite3_trace_v2(@db, FFI::CApi::SQLITE_TRACE_STMT, blk.nil? ? nil : FFI::TRACE, FFI.wrap(
|
|
22
|
+
@ctx.trace_handler = blk
|
|
23
|
+
FFI::CApi.sqlite3_trace_v2(@ctx.db, FFI::CApi::SQLITE_TRACE_STMT, blk.nil? ? nil : FFI::TRACE, FFI.wrap(@ctx))
|
|
24
24
|
self
|
|
25
25
|
end
|
|
26
26
|
|
|
@@ -28,31 +28,32 @@ module SQLite3
|
|
|
28
28
|
require_open_db
|
|
29
29
|
|
|
30
30
|
blk ||= block
|
|
31
|
-
@busy_handler = blk
|
|
32
|
-
status = FFI::CApi.sqlite3_busy_handler(@db, FFI::BUSY_HANDLER, FFI.wrap(
|
|
33
|
-
FFI.check(@db, status)
|
|
31
|
+
@ctx.busy_handler = blk
|
|
32
|
+
status = FFI::CApi.sqlite3_busy_handler(@ctx.db, FFI::BUSY_HANDLER, FFI.wrap(@ctx))
|
|
33
|
+
FFI.check(@ctx.db, status)
|
|
34
34
|
self
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def statement_timeout=(milliseconds)
|
|
38
|
-
@stmt_timeout = milliseconds.to_i
|
|
38
|
+
@ctx.stmt_timeout = milliseconds.to_i
|
|
39
39
|
n = milliseconds.to_i == 0 ? -1 : 1000
|
|
40
|
-
FFI::CApi.sqlite3_progress_handler(@db, n, FFI::STATEMENT_TIMEOUT, FFI.wrap(
|
|
40
|
+
FFI::CApi.sqlite3_progress_handler(@ctx.db, n, FFI::STATEMENT_TIMEOUT, FFI.wrap(@ctx))
|
|
41
41
|
self
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def last_insert_row_id
|
|
45
45
|
require_open_db
|
|
46
46
|
|
|
47
|
-
FFI::CApi.sqlite3_last_insert_rowid(@db)
|
|
47
|
+
FFI::CApi.sqlite3_last_insert_rowid(@ctx.db)
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def define_function_with_flags(name, flags, &block)
|
|
51
51
|
require_open_db
|
|
52
52
|
|
|
53
|
-
status = FFI::CApi.sqlite3_create_function(@db, FFI.string_value(name), block.arity, flags, FFI.wrap(block), FFI::FUNC, nil, nil)
|
|
54
|
-
FFI.check(@db, status)
|
|
53
|
+
status = FFI::CApi.sqlite3_create_function(@ctx.db, FFI.string_value(name), block.arity, flags, FFI.wrap(block), FFI::FUNC, nil, nil)
|
|
54
|
+
FFI.check(@ctx.db, status)
|
|
55
55
|
@functions << block
|
|
56
|
+
@ctx.functions = @functions
|
|
56
57
|
self
|
|
57
58
|
end
|
|
58
59
|
|
|
@@ -63,20 +64,20 @@ module SQLite3
|
|
|
63
64
|
def interrupt
|
|
64
65
|
require_open_db
|
|
65
66
|
|
|
66
|
-
FFI::CApi.sqlite3_interrupt(@db)
|
|
67
|
+
FFI::CApi.sqlite3_interrupt(@ctx.db)
|
|
67
68
|
self
|
|
68
69
|
end
|
|
69
70
|
|
|
70
71
|
def errmsg
|
|
71
72
|
require_open_db
|
|
72
73
|
|
|
73
|
-
FFI::CApi.sqlite3_errmsg(@db)
|
|
74
|
+
FFI::CApi.sqlite3_errmsg(@ctx.db)
|
|
74
75
|
end
|
|
75
76
|
|
|
76
77
|
def errcode
|
|
77
78
|
require_open_db
|
|
78
79
|
|
|
79
|
-
FFI::CApi.sqlite3_errcode(@db)
|
|
80
|
+
FFI::CApi.sqlite3_errcode(@ctx.db)
|
|
80
81
|
end
|
|
81
82
|
|
|
82
83
|
def complete?(sql)
|
|
@@ -86,35 +87,52 @@ module SQLite3
|
|
|
86
87
|
def changes
|
|
87
88
|
require_open_db
|
|
88
89
|
|
|
89
|
-
FFI::CApi.sqlite3_changes(@db)
|
|
90
|
+
FFI::CApi.sqlite3_changes(@ctx.db)
|
|
90
91
|
end
|
|
91
92
|
|
|
92
93
|
def authorizer=(authorizer)
|
|
93
94
|
require_open_db
|
|
94
95
|
|
|
95
|
-
status = FFI::CApi.sqlite3_set_authorizer(@db, authorizer.nil? ? nil : FFI::AUTH, FFI.wrap(
|
|
96
|
-
FFI.check(@db, status)
|
|
97
|
-
@authorizer = authorizer
|
|
96
|
+
status = FFI::CApi.sqlite3_set_authorizer(@ctx.db, authorizer.nil? ? nil : FFI::AUTH, FFI.wrap(@ctx))
|
|
97
|
+
FFI.check(@ctx.db, status)
|
|
98
|
+
@ctx.authorizer = authorizer
|
|
98
99
|
end
|
|
99
100
|
|
|
100
101
|
def busy_timeout=(timeout)
|
|
101
102
|
require_open_db
|
|
102
103
|
|
|
103
|
-
FFI.check(@db, FFI::CApi.sqlite3_busy_timeout(@db, timeout))
|
|
104
|
+
FFI.check(@ctx.db, FFI::CApi.sqlite3_busy_timeout(@ctx.db, timeout))
|
|
104
105
|
end
|
|
105
106
|
|
|
106
107
|
def extended_result_codes=(enable)
|
|
107
108
|
require_open_db
|
|
108
109
|
|
|
109
|
-
FFI::CApi.sqlite3_extended_result_codes(@db, enable ? 1 : 0)
|
|
110
|
+
FFI::CApi.sqlite3_extended_result_codes(@ctx.db, enable ? 1 : 0)
|
|
110
111
|
self
|
|
111
112
|
end
|
|
112
113
|
|
|
113
114
|
def collation(name, comparator)
|
|
114
115
|
require_open_db
|
|
115
116
|
|
|
116
|
-
|
|
117
|
-
|
|
117
|
+
name = FFI.string_value(name)
|
|
118
|
+
key = name.dup
|
|
119
|
+
key.each_byte.with_index do |v, i|
|
|
120
|
+
if v >= 65 && v <= 90 # 'A' and 'Z'
|
|
121
|
+
key.setbyte(i, v + 32) # 'a' - 'A'
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
@_collations ||= {}
|
|
126
|
+
registry = @_collations
|
|
127
|
+
|
|
128
|
+
status = FFI::CApi.sqlite3_create_collation(@ctx.db, name, FFI::CApi::SQLITE_UTF8, FFI.wrap(comparator), comparator.nil? ? nil : FFI::COMPARATOR)
|
|
129
|
+
FFI.check(@ctx.db, status)
|
|
130
|
+
|
|
131
|
+
registry[key] = comparator
|
|
132
|
+
comparators = []
|
|
133
|
+
comparators += registry.values
|
|
134
|
+
@ctx.collations = comparators
|
|
135
|
+
|
|
118
136
|
@collations[name] = comparator
|
|
119
137
|
self
|
|
120
138
|
end
|
|
@@ -130,7 +148,7 @@ module SQLite3
|
|
|
130
148
|
else
|
|
131
149
|
onoffparam = onoff.to_i
|
|
132
150
|
end
|
|
133
|
-
FFI.check(@db, FFI::CApi.sqlite3_enable_load_extension(@db, onoffparam))
|
|
151
|
+
FFI.check(@ctx.db, FFI::CApi.sqlite3_enable_load_extension(@ctx.db, onoffparam))
|
|
134
152
|
self
|
|
135
153
|
end
|
|
136
154
|
end
|
|
@@ -138,26 +156,33 @@ module SQLite3
|
|
|
138
156
|
def transaction_active?
|
|
139
157
|
require_open_db
|
|
140
158
|
|
|
141
|
-
FFI::CApi.sqlite3_get_autocommit(@db).zero?
|
|
159
|
+
FFI::CApi.sqlite3_get_autocommit(@ctx.db).zero?
|
|
142
160
|
end
|
|
143
161
|
|
|
144
162
|
def exec_batch(sql, results_as_hash)
|
|
145
163
|
require_open_db
|
|
146
164
|
|
|
147
|
-
|
|
165
|
+
sql_cstr = FFI.string_value(sql)
|
|
166
|
+
@ctx.stmt_deadline = nil
|
|
148
167
|
|
|
149
168
|
callback_ary = []
|
|
150
169
|
err_msg = ::FFI::MemoryPointer.new(:pointer)
|
|
151
170
|
callback = results_as_hash == true ? FFI::HASH_CALLBACK : FFI::REGULAR_CALLBACK
|
|
152
|
-
|
|
153
|
-
FFI.
|
|
171
|
+
frame = FFI::CallbackFrame.new
|
|
172
|
+
FFI.push_callback_frame(frame)
|
|
173
|
+
status = FFI::CApi.sqlite3_exec(@ctx.db, sql_cstr, callback, FFI.wrap(callback_ary), err_msg)
|
|
174
|
+
if (state = FFI.pop_callback_frame(frame))
|
|
175
|
+
FFI::CApi.sqlite3_free(err_msg.read_pointer)
|
|
176
|
+
raise state
|
|
177
|
+
end
|
|
178
|
+
FFI.check_msg(@ctx.db, status, err_msg)
|
|
154
179
|
callback_ary
|
|
155
180
|
end
|
|
156
181
|
|
|
157
182
|
def db_filename(db_name)
|
|
158
183
|
require_open_db
|
|
159
184
|
|
|
160
|
-
fname = FFI::CApi.sqlite3_db_filename(@db, db_name)
|
|
185
|
+
fname = FFI::CApi.sqlite3_db_filename(@ctx.db, db_name)
|
|
161
186
|
fname.null? ? nil : fname.read_string.force_encoding(Encoding::UTF_8)
|
|
162
187
|
end
|
|
163
188
|
|
|
@@ -168,7 +193,7 @@ module SQLite3
|
|
|
168
193
|
z_schema = db_name.nil? ? nil : FFI.string_value_cstr(db_name)
|
|
169
194
|
|
|
170
195
|
size = ::FFI::MemoryPointer.new(:int64)
|
|
171
|
-
data = FFI::CApi.sqlite3_serialize(@db, z_schema, size, 0)
|
|
196
|
+
data = FFI::CApi.sqlite3_serialize(@ctx.db, z_schema, size, 0)
|
|
172
197
|
size = size.read_int64
|
|
173
198
|
|
|
174
199
|
if data.null? && size < 0
|
|
@@ -200,9 +225,9 @@ module SQLite3
|
|
|
200
225
|
p_data.put_bytes(0, data)
|
|
201
226
|
p_data.put_bytes(sz_db, "\x00"*(sz_buf - sz_db))
|
|
202
227
|
|
|
203
|
-
status = FFI::CApi.sqlite3_deserialize(@db, z_schema, p_data, sz_db, sz_db, FFI::CApi::SQLITE_DESERIALIZE_FREEONCLOSE | FFI::CApi::SQLITE_DESERIALIZE_RESIZEABLE)
|
|
228
|
+
status = FFI::CApi.sqlite3_deserialize(@ctx.db, z_schema, p_data, sz_db, sz_db, FFI::CApi::SQLITE_DESERIALIZE_FREEONCLOSE | FFI::CApi::SQLITE_DESERIALIZE_RESIZEABLE)
|
|
204
229
|
|
|
205
|
-
FFI.check(@db, status)
|
|
230
|
+
FFI.check(@ctx.db, status)
|
|
206
231
|
self
|
|
207
232
|
end
|
|
208
233
|
end
|
|
@@ -210,13 +235,13 @@ module SQLite3
|
|
|
210
235
|
private
|
|
211
236
|
|
|
212
237
|
def require_open_db
|
|
213
|
-
if @db.nil?
|
|
238
|
+
if @ctx&.db.nil?
|
|
214
239
|
raise SQLite3::Exception, "cannot use a closed database"
|
|
215
240
|
end
|
|
216
241
|
end
|
|
217
242
|
|
|
218
243
|
def require_closed_db
|
|
219
|
-
if !@db.nil?
|
|
244
|
+
if !@ctx&.db.nil?
|
|
220
245
|
raise SQLite3::Exception, "cannot open a database that is already open"
|
|
221
246
|
end
|
|
222
247
|
end
|
|
@@ -224,38 +249,38 @@ module SQLite3
|
|
|
224
249
|
def discard_db
|
|
225
250
|
sfile = ::FFI::MemoryPointer.new(:pointer)
|
|
226
251
|
|
|
227
|
-
FFI::CApi.sqlite3_db_release_memory(@db)
|
|
252
|
+
FFI::CApi.sqlite3_db_release_memory(@ctx.db)
|
|
228
253
|
|
|
229
254
|
if FFI::CApi::HAVE_SQLITE3_DB_NAME
|
|
230
255
|
j_db = 0
|
|
231
|
-
while !(db_name = FFI::CApi.sqlite3_db_name(@db, j_db)).null?
|
|
232
|
-
status = FFI::CApi.sqlite3_file_control(@db, db_name, FFI::CApi::SQLITE_FCNTL_FILE_POINTER, sfile)
|
|
256
|
+
while !(db_name = FFI::CApi.sqlite3_db_name(@ctx.db, j_db)).null?
|
|
257
|
+
status = FFI::CApi.sqlite3_file_control(@ctx.db, db_name, FFI::CApi::SQLITE_FCNTL_FILE_POINTER, sfile)
|
|
233
258
|
if status == 0
|
|
234
259
|
# TODO
|
|
235
260
|
end
|
|
236
261
|
j_db += 1
|
|
237
262
|
end
|
|
238
263
|
else
|
|
239
|
-
status = FFI::CApi.sqlite3_file_control(@db, nil, FFI::CApi::SQLITE_FCNTL_FILE_POINTER, sfile)
|
|
264
|
+
status = FFI::CApi.sqlite3_file_control(@ctx.db, nil, FFI::CApi::SQLITE_FCNTL_FILE_POINTER, sfile)
|
|
240
265
|
if status == 0
|
|
241
266
|
# TODO
|
|
242
267
|
end
|
|
243
268
|
end
|
|
244
269
|
|
|
245
|
-
status = FFI::CApi.sqlite3_file_control(@db, nil, FFI::CApi::SQLITE_FCNTL_JOURNAL_POINTER, sfile)
|
|
270
|
+
status = FFI::CApi.sqlite3_file_control(@ctx.db, nil, FFI::CApi::SQLITE_FCNTL_JOURNAL_POINTER, sfile)
|
|
246
271
|
if status == 0
|
|
247
272
|
# TODO
|
|
248
273
|
end
|
|
249
274
|
|
|
250
|
-
@db = nil
|
|
251
|
-
@discarded = true
|
|
275
|
+
@ctx.db = nil
|
|
276
|
+
@ctx.discarded = true
|
|
252
277
|
end
|
|
253
278
|
|
|
254
279
|
def close_or_discard_db
|
|
255
|
-
unless @db.nil?
|
|
256
|
-
if @readonly || @owner == Process.pid
|
|
257
|
-
@db.free
|
|
258
|
-
@db = nil
|
|
280
|
+
unless @ctx&.db.nil?
|
|
281
|
+
if @ctx.readonly || @ctx.owner == Process.pid
|
|
282
|
+
@ctx.db.free
|
|
283
|
+
@ctx.db = nil
|
|
259
284
|
else
|
|
260
285
|
discard_db
|
|
261
286
|
end
|
|
@@ -282,23 +307,23 @@ module SQLite3
|
|
|
282
307
|
|
|
283
308
|
def open_v2(file, flags, zvfs)
|
|
284
309
|
require_closed_db
|
|
285
|
-
@
|
|
310
|
+
@ctx = FFI::Context.new
|
|
311
|
+
@ctx.owner = Process.pid
|
|
286
312
|
db = ::FFI::MemoryPointer.new(:pointer)
|
|
287
313
|
status = FFI::CApi.sqlite3_open_v2(FFI.string_value_cstr(file), db, flags, zvfs.nil? ? nil : FFI.string_value_cstr(zvfs))
|
|
288
|
-
@db = db.read_pointer
|
|
289
|
-
|
|
290
|
-
FFI.check(@db, status)
|
|
314
|
+
@ctx.db = ::FFI::AutoPointer.new(db.read_pointer, FFI::CApi.method(:sqlite3_close_v2))
|
|
315
|
+
FFI.check(@ctx.db, status)
|
|
291
316
|
if (flags & FFI::CApi::SQLITE_OPEN_READONLY) != 0
|
|
292
|
-
@readonly = true
|
|
317
|
+
@ctx.readonly = true
|
|
293
318
|
end
|
|
294
319
|
self
|
|
295
320
|
end
|
|
296
321
|
|
|
297
322
|
def disable_quirk_mode
|
|
298
|
-
return false if @db.nil?
|
|
323
|
+
return false if @ctx&.db.nil?
|
|
299
324
|
|
|
300
|
-
FFI::CApi.sqlite3_db_config(@db, FFI::CApi::SQLITE_DBCONFIG_DQS_DDL, :int, 0, :pointer, nil)
|
|
301
|
-
FFI::CApi.sqlite3_db_config(@db, FFI::CApi::SQLITE_DBCONFIG_DQS_DML, :int, 0, :pointer, nil)
|
|
325
|
+
FFI::CApi.sqlite3_db_config(@ctx.db, FFI::CApi::SQLITE_DBCONFIG_DQS_DDL, :int, 0, :pointer, nil)
|
|
326
|
+
FFI::CApi.sqlite3_db_config(@ctx.db, FFI::CApi::SQLITE_DBCONFIG_DQS_DML, :int, 0, :pointer, nil)
|
|
302
327
|
true
|
|
303
328
|
end
|
|
304
329
|
|
|
@@ -312,26 +337,27 @@ module SQLite3
|
|
|
312
337
|
require_open_db
|
|
313
338
|
|
|
314
339
|
err_msg = ::FFI::MemoryPointer.new(:pointer)
|
|
315
|
-
status = FFI::CApi.sqlite3_load_extension(@db, FFI.string_value(file), nil, err_msg)
|
|
316
|
-
FFI.check_msg(@db, status, err_msg)
|
|
340
|
+
status = FFI::CApi.sqlite3_load_extension(@ctx.db, FFI.string_value(file), nil, err_msg)
|
|
341
|
+
FFI.check_msg(@ctx.db, status, err_msg)
|
|
317
342
|
self
|
|
318
343
|
end
|
|
319
344
|
end
|
|
320
345
|
|
|
321
346
|
def open16(file)
|
|
322
347
|
require_closed_db
|
|
323
|
-
@
|
|
348
|
+
@ctx = FFI::Context.new
|
|
349
|
+
@ctx.owner = Process.pid
|
|
324
350
|
db = ::FFI::MemoryPointer.new(:pointer)
|
|
325
351
|
status = FFI::CApi.sqlite3_open16(utf16_string_value_ptr(file), db)
|
|
326
|
-
@db = ::FFI::AutoPointer.new(db.read_pointer, FFI::CApi.method(:sqlite3_close_v2))
|
|
352
|
+
@ctx.db = ::FFI::AutoPointer.new(db.read_pointer, FFI::CApi.method(:sqlite3_close_v2))
|
|
327
353
|
if status != FFI::CApi::SQLITE_OK
|
|
328
354
|
# https://github.com/ffi/ffi/issues/1121
|
|
329
|
-
# msg = FFI::CApi.sqlite3_mprintf("%s", :string, FFI::CApi.sqlite3_errmsg(@db))
|
|
355
|
+
# msg = FFI::CApi.sqlite3_mprintf("%s", :string, FFI::CApi.sqlite3_errmsg(@ctx.db))
|
|
330
356
|
# msg = ::FFI::MemoryPointer.new(:pointer).write_pointer(msg)
|
|
331
|
-
msg = FFI::CApi.sqlite3_errmsg(@db)
|
|
332
|
-
@db.free
|
|
333
|
-
@db = nil
|
|
334
|
-
FFI.check_msg(@db, status, msg)
|
|
357
|
+
msg = FFI::CApi.sqlite3_errmsg(@ctx.db)
|
|
358
|
+
@ctx.db.free
|
|
359
|
+
@ctx.db = nil
|
|
360
|
+
FFI.check_msg(@ctx.db, status, msg)
|
|
335
361
|
end
|
|
336
362
|
status
|
|
337
363
|
end
|
|
@@ -349,12 +375,18 @@ module SQLite3
|
|
|
349
375
|
raise ArgumentError, "Aggregator arity=#{arity} out of range -1..127"
|
|
350
376
|
end
|
|
351
377
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
378
|
+
@_aggregators ||= []
|
|
379
|
+
aggregators = @_aggregators
|
|
380
|
+
|
|
381
|
+
aw = FFI::AggregatorWrapper.new
|
|
382
|
+
aw.handler_klass = aggregator
|
|
383
|
+
aw.instances = []
|
|
384
|
+
|
|
385
|
+
status = FFI::CApi.sqlite3_create_function(@ctx.db, FFI.string_value(ruby_name), arity, FFI::CApi::SQLITE_UTF8, FFI.wrap(aw), nil, FFI::AGGREGATOR_STEP, FFI::AGGREGATOR_FINAL)
|
|
386
|
+
FFI.check(@ctx.db, status)
|
|
355
387
|
|
|
356
|
-
|
|
357
|
-
@aggregators
|
|
388
|
+
aggregators << aw
|
|
389
|
+
@ctx.aggregators = aggregators
|
|
358
390
|
|
|
359
391
|
self
|
|
360
392
|
end
|
|
@@ -107,5 +107,64 @@ module SQLite3
|
|
|
107
107
|
|
|
108
108
|
raise exception
|
|
109
109
|
end
|
|
110
|
+
|
|
111
|
+
CallbackFrame = Struct.new(:state, :prev, :holder)
|
|
112
|
+
|
|
113
|
+
CallbackFrames = Struct.new(:top)
|
|
114
|
+
|
|
115
|
+
def self.current_callback_frames_holder
|
|
116
|
+
holder = Thread.current[:__sqlite3_callback_frames__]
|
|
117
|
+
holder.is_a?(CallbackFrames) ? holder : nil
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def self.current_callback_frames
|
|
121
|
+
holder = current_callback_frames_holder
|
|
122
|
+
holder.nil? ? nil : holder
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def self.push_callback_frame(frame)
|
|
126
|
+
holder = current_callback_frames_holder
|
|
127
|
+
|
|
128
|
+
if holder.nil?
|
|
129
|
+
holder = CallbackFrames.new
|
|
130
|
+
Thread.current[:__sqlite3_callback_frames__] = holder
|
|
131
|
+
end
|
|
132
|
+
frames = holder
|
|
133
|
+
|
|
134
|
+
frame.state = nil
|
|
135
|
+
frame.prev = frames.top
|
|
136
|
+
frame.holder = holder
|
|
137
|
+
frames.top = frame
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def self.pop_callback_frame(frame)
|
|
141
|
+
frame.holder.top = frame.prev
|
|
142
|
+
frame.state
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def self.protect_callback
|
|
146
|
+
frames = current_callback_frames
|
|
147
|
+
frame = frames ? frames.top : nil
|
|
148
|
+
result = nil
|
|
149
|
+
state = nil
|
|
150
|
+
|
|
151
|
+
if frame && frame.state
|
|
152
|
+
failed = true
|
|
153
|
+
return [nil, failed]
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
begin
|
|
157
|
+
result = yield
|
|
158
|
+
rescue => e
|
|
159
|
+
state = e
|
|
160
|
+
end
|
|
161
|
+
failed = !state.nil?
|
|
162
|
+
|
|
163
|
+
if frame
|
|
164
|
+
frame.state = state
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
[result, failed]
|
|
168
|
+
end
|
|
110
169
|
end
|
|
111
170
|
end
|
|
@@ -1,63 +1,85 @@
|
|
|
1
1
|
module SQLite3
|
|
2
2
|
module FFI
|
|
3
3
|
COMPARATOR = ::FFI::Function.new(:int, [:pointer, :int, :pointer, :int, :pointer]) do |ctx, a_len, a, b_len, b|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
result, failed =
|
|
5
|
+
protect_callback do
|
|
6
|
+
comparator = unwrap(ctx)
|
|
7
|
+
a_str = a.read_bytes(a_len).force_encoding(Encoding::UTF_8)
|
|
8
|
+
b_str = b.read_bytes(b_len).force_encoding(Encoding::UTF_8)
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
if Encoding.default_internal
|
|
11
|
+
a_str = a_str.encode(Encoding.default_internal)
|
|
12
|
+
b_str = b_str.encode(Encoding.default_internal)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
comparator.compare(a_str, b_str)
|
|
16
|
+
end
|
|
12
17
|
|
|
13
|
-
|
|
18
|
+
failed ? 0 : result
|
|
14
19
|
end
|
|
15
20
|
|
|
16
21
|
TRACE = ::FFI::Function.new(:int, [:uint, :pointer, :pointer, :pointer]) do |_, ctx, _, x|
|
|
17
|
-
|
|
22
|
+
protect_callback do
|
|
23
|
+
unwrap(ctx).trace_handler.call(x.read_string)
|
|
24
|
+
end
|
|
18
25
|
0
|
|
19
26
|
end
|
|
20
27
|
|
|
21
28
|
AUTH = ::FFI::Function.new(:int, [:pointer, :int, :string, :string, :string, :string]) do |ctx, op_id, s1, s2, s3, s4|
|
|
22
|
-
result
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
result, failed =
|
|
30
|
+
protect_callback do
|
|
31
|
+
result = unwrap(ctx).authorizer.call(op_id, s1, s2, s3, s4)
|
|
32
|
+
if result.is_a?(Integer)
|
|
33
|
+
result
|
|
34
|
+
elsif result == true
|
|
35
|
+
CApi::SQLITE_OK
|
|
36
|
+
elsif result == false
|
|
37
|
+
CApi::SQLITE_DENY
|
|
38
|
+
else
|
|
39
|
+
CApi::SQLITE_IGNORE
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
failed ? CApi::SQLITE_DENY : result
|
|
32
44
|
end
|
|
33
45
|
|
|
34
46
|
HASH_CALLBACK = ::FFI::Function.new(:int, [:pointer, :int, :pointer, :pointer]) do |ctx, count, data, columns|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
47
|
+
_, failed =
|
|
48
|
+
protect_callback do
|
|
49
|
+
callback_ary = unwrap(ctx)
|
|
50
|
+
new_hash = {}
|
|
51
|
+
data.read_array_of_pointer(count).zip(columns.read_array_of_pointer(count)) do |value, column|
|
|
52
|
+
new_hash[column.read_string] = value.null? ? nil : value.read_string
|
|
53
|
+
end
|
|
54
|
+
callback_ary << new_hash
|
|
55
|
+
nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
failed ? 1 : 0
|
|
42
59
|
end
|
|
43
60
|
|
|
44
61
|
REGULAR_CALLBACK = ::FFI::Function.new(:int, [:pointer, :int, :pointer, :pointer]) do |ctx, count, data, columns|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
62
|
+
_, failed =
|
|
63
|
+
protect_callback do
|
|
64
|
+
callback_ary = unwrap(ctx)
|
|
65
|
+
new_ary = []
|
|
66
|
+
data.read_array_of_pointer(count).each do |value|
|
|
67
|
+
new_ary << (value.null? ? nil : value.read_string)
|
|
68
|
+
end
|
|
69
|
+
callback_ary << new_ary
|
|
70
|
+
nil
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
failed ? 1 : 0
|
|
52
74
|
end
|
|
53
75
|
|
|
54
76
|
STATEMENT_TIMEOUT = ::FFI::Function.new(:int, [:pointer]) do |ctx|
|
|
55
77
|
ctx = unwrap(ctx)
|
|
56
78
|
current_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
57
|
-
if ctx.
|
|
58
|
-
ctx.
|
|
79
|
+
if ctx.stmt_deadline.nil?
|
|
80
|
+
ctx.stmt_deadline = current_time + ctx.stmt_timeout.to_i / 1000.0
|
|
59
81
|
0
|
|
60
|
-
elsif current_time >= ctx.
|
|
82
|
+
elsif current_time >= ctx.stmt_deadline
|
|
61
83
|
1
|
|
62
84
|
else
|
|
63
85
|
0
|
|
@@ -65,16 +87,27 @@ module SQLite3
|
|
|
65
87
|
end
|
|
66
88
|
|
|
67
89
|
BUSY_HANDLER = ::FFI::Function.new(:int, [:pointer, :int]) do |ctx, count|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
90
|
+
result, failed =
|
|
91
|
+
protect_callback do
|
|
92
|
+
handler = unwrap(ctx).busy_handler
|
|
93
|
+
handler.(count)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
if failed || result == false
|
|
97
|
+
0
|
|
98
|
+
else
|
|
99
|
+
1
|
|
100
|
+
end
|
|
71
101
|
end
|
|
72
102
|
|
|
73
103
|
FUNC = ::FFI::Function.new(:void, [:pointer, :int, :pointer]) do |ctx, argc, argv|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
104
|
+
protect_callback do
|
|
105
|
+
callable = unwrap(CApi.sqlite3_user_data(ctx))
|
|
106
|
+
params = argv.read_array_of_pointer(argc).map { |v| FFI.sqlite3val2rb(v) }
|
|
107
|
+
result = callable.(*params)
|
|
108
|
+
FFI.set_sqlite3_func_result(ctx, result)
|
|
109
|
+
nil
|
|
110
|
+
end
|
|
78
111
|
end
|
|
79
112
|
end
|
|
80
113
|
end
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
module SQLite3
|
|
2
2
|
class Statement
|
|
3
3
|
def prepare(db, sql)
|
|
4
|
-
|
|
4
|
+
@db_ctx = db.instance_variable_get(:@ctx)
|
|
5
|
+
@db_ctx.stmt_deadline = nil
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
sql_cstr = FFI.string_value(sql)
|
|
8
|
+
sql_len = FFI.rstring_lenint(sql)
|
|
9
|
+
|
|
10
|
+
db = @db_ctx.db
|
|
9
11
|
stmt = ::FFI::MemoryPointer.new(:pointer)
|
|
10
12
|
tail = ::FFI::MemoryPointer.new(:pointer)
|
|
11
|
-
|
|
12
|
-
FFI.
|
|
13
|
-
|
|
13
|
+
frame = FFI::CallbackFrame.new
|
|
14
|
+
FFI.push_callback_frame(frame)
|
|
15
|
+
status = FFI::CApi.sqlite3_prepare_v2(db, sql_cstr, sql_len, stmt, tail)
|
|
16
|
+
# sqlite3_finalize is no-op on NULL pointer
|
|
14
17
|
@stmt = ::FFI::AutoPointer.new(stmt.read_pointer, FFI::CApi.method(:sqlite3_finalize))
|
|
18
|
+
if (callback_state = FFI.pop_callback_frame(frame))
|
|
19
|
+
raise callback_state
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
FFI.check_prepare(db, status, sql)
|
|
15
23
|
|
|
16
24
|
tail.read_pointer.read_string.force_encoding(Encoding::UTF_8)
|
|
17
25
|
end
|
|
@@ -36,14 +44,19 @@ module SQLite3
|
|
|
36
44
|
stmt = @stmt
|
|
37
45
|
|
|
38
46
|
if FFI::CApi.sqlite3_stmt_busy(stmt) == 0
|
|
39
|
-
@
|
|
47
|
+
@db_ctx.stmt_deadline = nil
|
|
40
48
|
end
|
|
41
49
|
|
|
50
|
+
frame = FFI::CallbackFrame.new
|
|
51
|
+
FFI.push_callback_frame(frame)
|
|
42
52
|
value = FFI::CApi.sqlite3_step(stmt)
|
|
43
|
-
if FFI.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
if (callback_state = FFI.pop_callback_frame(frame))
|
|
54
|
+
raise callback_state
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
if value == FFI::CApi::SQLITE_DONE
|
|
58
|
+
@done = true
|
|
59
|
+
return nil
|
|
47
60
|
end
|
|
48
61
|
|
|
49
62
|
length = FFI::CApi.sqlite3_column_count(stmt)
|
|
@@ -75,9 +88,6 @@ module SQLite3
|
|
|
75
88
|
end
|
|
76
89
|
list << val
|
|
77
90
|
end
|
|
78
|
-
when FFI::CApi::SQLITE_DONE
|
|
79
|
-
@done = true
|
|
80
|
-
return nil
|
|
81
91
|
else
|
|
82
92
|
FFI::CApi.sqlite3_reset(stmt)
|
|
83
93
|
@done = false
|
|
@@ -108,14 +118,14 @@ module SQLite3
|
|
|
108
118
|
case value
|
|
109
119
|
when String
|
|
110
120
|
if value.is_a?(Blob) || value.encoding == Encoding::BINARY
|
|
111
|
-
status = FFI::CApi.sqlite3_bind_blob(@stmt, index, value, value
|
|
121
|
+
status = FFI::CApi.sqlite3_bind_blob(@stmt, index, value, FFI.rstring_lenint(value), FFI::CApi::SQLITE_TRANSIENT)
|
|
112
122
|
elsif value.encoding == Encoding::UTF_16LE || value.encoding == Encoding::UTF_16BE
|
|
113
|
-
status = FFI::CApi.sqlite3_bind_text16(@stmt, index, value, value
|
|
123
|
+
status = FFI::CApi.sqlite3_bind_text16(@stmt, index, value, FFI.rstring_lenint(value), FFI::CApi::SQLITE_TRANSIENT)
|
|
114
124
|
else
|
|
115
125
|
if value.encoding != Encoding::UTF_8 && value.encoding != Encoding::US_ASCII
|
|
116
126
|
value = value.encode(Encoding::UTF_8)
|
|
117
127
|
end
|
|
118
|
-
status = FFI::CApi.sqlite3_bind_text(@stmt, index, +value, value
|
|
128
|
+
status = FFI::CApi.sqlite3_bind_text(@stmt, index, +value, FFI.rstring_lenint(value), FFI::CApi::SQLITE_TRANSIENT)
|
|
119
129
|
end
|
|
120
130
|
when Float
|
|
121
131
|
status = FFI::CApi.sqlite3_bind_double(@stmt, index, value)
|
|
@@ -268,7 +278,7 @@ module SQLite3
|
|
|
268
278
|
end
|
|
269
279
|
|
|
270
280
|
def require_live_db
|
|
271
|
-
if @
|
|
281
|
+
if @db_ctx.discarded
|
|
272
282
|
raise SQLite3::Exception, "cannot use a statement associated with a discarded database"
|
|
273
283
|
end
|
|
274
284
|
end
|
data/lib/sqlite3/ffi/utils.rb
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
module SQLite3
|
|
2
2
|
module FFI
|
|
3
|
+
Context =
|
|
4
|
+
Struct.new(
|
|
5
|
+
:db,
|
|
6
|
+
:busy_handler,
|
|
7
|
+
:functions,
|
|
8
|
+
:collations,
|
|
9
|
+
:aggregators,
|
|
10
|
+
:trace_handler,
|
|
11
|
+
:authorizer,
|
|
12
|
+
:stmt_timeout,
|
|
13
|
+
:stmt_deadline,
|
|
14
|
+
:owner,
|
|
15
|
+
:readonly,
|
|
16
|
+
:discarded
|
|
17
|
+
)
|
|
18
|
+
|
|
3
19
|
def self.sqlite3val2rb(val)
|
|
4
20
|
case CApi.sqlite3_value_type(val)
|
|
5
21
|
when CApi::SQLITE_INTEGER
|
|
@@ -29,9 +45,9 @@ module SQLite3
|
|
|
29
45
|
CApi.sqlite3_result_double(ctx, result)
|
|
30
46
|
when String
|
|
31
47
|
if result.is_a?(Blob) || result.encoding == Encoding::BINARY
|
|
32
|
-
CApi.sqlite3_result_blob(ctx, result, result
|
|
48
|
+
CApi.sqlite3_result_blob(ctx, result, rstring_lenint(result), CApi::SQLITE_TRANSIENT)
|
|
33
49
|
else
|
|
34
|
-
CApi.sqlite3_result_text(ctx, result, result
|
|
50
|
+
CApi.sqlite3_result_text(ctx, result, rstring_lenint(result), CApi::SQLITE_TRANSIENT)
|
|
35
51
|
end
|
|
36
52
|
else
|
|
37
53
|
raise RuntimeError, "can't return #{result.class.name}"
|
|
@@ -70,14 +86,14 @@ module SQLite3
|
|
|
70
86
|
obj
|
|
71
87
|
end
|
|
72
88
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def self.rb_errinfo
|
|
76
|
-
Thread.current[RB_ERRINFO]
|
|
77
|
-
end
|
|
89
|
+
INT_MAX = 2**31 - 1
|
|
78
90
|
|
|
79
|
-
def self.
|
|
80
|
-
|
|
91
|
+
def self.rstring_lenint(obj)
|
|
92
|
+
n = obj.bytesize
|
|
93
|
+
if n > INT_MAX
|
|
94
|
+
raise RangeError, "integer %p too big to convert to int" % [n]
|
|
95
|
+
end
|
|
96
|
+
n
|
|
81
97
|
end
|
|
82
98
|
|
|
83
99
|
OBJECT_REGISTRY = ObjectSpace::WeakMap.new
|
data/lib/sqlite3/ffi/version.rb
CHANGED
data/lib/sqlite3/resultset.rb
CHANGED
|
@@ -16,8 +16,12 @@ module SQLite3
|
|
|
16
16
|
@stmt = stmt
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
#
|
|
20
|
-
#
|
|
19
|
+
# Resets the cursor to the first row, so that you can iterate over the
|
|
20
|
+
# result set again after it reaches the end.
|
|
21
|
+
#
|
|
22
|
+
# This method binds each argument to the statement with
|
|
23
|
+
# Statement#bind_params. The values that you bound before stay bound
|
|
24
|
+
# unless you bind new values to the same parameters.
|
|
21
25
|
def reset(*bind_params)
|
|
22
26
|
@stmt.reset!
|
|
23
27
|
@stmt.bind_params(*bind_params)
|
data/lib/sqlite3/statement.rb
CHANGED
|
@@ -65,6 +65,10 @@ module SQLite3
|
|
|
65
65
|
# stmt = db.prepare( "select * from table where a=? and b=?" )
|
|
66
66
|
# stmt.bind_params( 15, "hello" )
|
|
67
67
|
#
|
|
68
|
+
# The values that this method binds stay bound when you execute or reset
|
|
69
|
+
# the statement. To change a value, bind a new value to the same
|
|
70
|
+
# parameter. To clear all values, call #clear_bindings!.
|
|
71
|
+
#
|
|
68
72
|
# See also #execute, #bind_param, Statement#bind_param, and
|
|
69
73
|
# Statement#bind_params.
|
|
70
74
|
def bind_params(*bind_vars)
|
|
@@ -83,7 +87,17 @@ module SQLite3
|
|
|
83
87
|
# statement's virtual machine. If a block was given, the new ResultSet will
|
|
84
88
|
# be yielded to it; otherwise, the ResultSet will be returned.
|
|
85
89
|
#
|
|
86
|
-
#
|
|
90
|
+
# This method binds each argument to the statement with #bind_params. The
|
|
91
|
+
# values stay bound after this method returns. If a later call to #execute
|
|
92
|
+
# does not give a value for a parameter, the statement uses the value from
|
|
93
|
+
# the earlier call. To set all parameters to NULL before you execute the
|
|
94
|
+
# statement again, call #clear_bindings!:
|
|
95
|
+
#
|
|
96
|
+
# stmt = db.prepare( "select :a, :b" )
|
|
97
|
+
# stmt.execute( a: 1, b: 2 ).to_a # => [[1, 2]]
|
|
98
|
+
# stmt.execute( a: 3 ).to_a # => [[3, 2]]
|
|
99
|
+
# stmt.clear_bindings!
|
|
100
|
+
# stmt.execute( a: 3 ).to_a # => [[3, nil]]
|
|
87
101
|
#
|
|
88
102
|
# Example:
|
|
89
103
|
#
|
|
@@ -92,7 +106,7 @@ module SQLite3
|
|
|
92
106
|
# ...
|
|
93
107
|
# end
|
|
94
108
|
#
|
|
95
|
-
# See also #bind_params, #execute!.
|
|
109
|
+
# See also #bind_params, #execute!, #clear_bindings!.
|
|
96
110
|
def execute(*bind_vars)
|
|
97
111
|
reset! if active? || done?
|
|
98
112
|
|
|
@@ -109,7 +123,9 @@ module SQLite3
|
|
|
109
123
|
# rows returned by executing the statement. Otherwise, each row will be
|
|
110
124
|
# yielded to the block.
|
|
111
125
|
#
|
|
112
|
-
#
|
|
126
|
+
# This method binds each argument to the statement with #bind_params. The
|
|
127
|
+
# values stay bound after this method returns. For more information, see
|
|
128
|
+
# #execute.
|
|
113
129
|
#
|
|
114
130
|
# Example:
|
|
115
131
|
#
|
data/lib/sqlite3/version.rb
CHANGED