Module: SQLite3::FFI
- Defined in:
- lib/sqlite3/ffi/c_api.rb,
lib/sqlite3/ffi/utils.rb,
lib/sqlite3/ffi/version.rb,
lib/sqlite3/ffi/core_ext.rb,
lib/sqlite3/ffi/exception.rb,
lib/sqlite3/ffi/functions.rb,
lib/sqlite3/ffi/aggregator.rb
Defined Under Namespace
Modules: CApi, CoreExt Classes: AggregatorInstance, AggregatorWrapper
Constant Summary collapse
- RB_ERRINFO =
:sqlite3_ffi_rb_errinfo- OBJECT_REGISTRY =
ObjectSpace::WeakMap.new
- VERSION =
"0.1.1"- COMPARATOR =
::FFI::Function.new(:int, [:pointer, :int, :pointer, :int, :pointer]) do |ctx, a_len, a, b_len, b| comparator = unwrap(ctx) a_str = a.read_bytes(a_len).force_encoding(Encoding::UTF_8) b_str = b.read_bytes(b_len).force_encoding(Encoding::UTF_8) if Encoding.default_internal a_str = a_str.encode(Encoding.default_internal) b_str = b_str.encode(Encoding.default_internal) end comparator.compare(a_str, b_str) end
- TRACE =
::FFI::Function.new(:int, [:uint, :pointer, :pointer, :pointer]) do |_, ctx, _, x| unwrap(ctx).call(x.read_string) 0 end
- AUTH =
::FFI::Function.new(:int, [:pointer, :int, :string, :string, :string, :string]) do |ctx, op_id, s1, s2, s3, s4| result = unwrap(ctx).call(op_id, s1, s2, s3, s4) if result.is_a?(Integer) result elsif result == true CApi::SQLITE_OK elsif result == false CApi::SQLITE_DENY else CApi::SQLITE_IGNORE end end
- HASH_CALLBACK =
::FFI::Function.new(:int, [:pointer, :int, :pointer, :pointer]) do |ctx, count, data, columns| callback_ary = unwrap(ctx) new_hash = {} data.read_array_of_pointer(count).zip(columns.read_array_of_pointer(count)) do |value, column| new_hash[column.read_string] = value.null? ? nil : value.read_string end callback_ary << new_hash 0 end
- REGULAR_CALLBACK =
::FFI::Function.new(:int, [:pointer, :int, :pointer, :pointer]) do |ctx, count, data, columns| callback_ary = unwrap(ctx) new_ary = [] data.read_array_of_pointer(count).each do |value| new_ary << (value.null? ? nil : value.read_string) end callback_ary << new_ary 0 end
- STATEMENT_TIMEOUT =
::FFI::Function.new(:int, [:pointer]) do |ctx| ctx = unwrap(ctx) current_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) if ctx.instance_variable_get(:@stmt_deadline).nil? ctx.instance_variable_set(:@stmt_deadline, current_time) 0 elsif current_time >= ctx.instance_variable_get(:@stmt_deadline) 1 else 0 end end
- BUSY_HANDLER =
::FFI::Function.new(:int, [:pointer, :int]) do |ctx, count| handler = unwrap(ctx).instance_variable_get(:@busy_handler) result = handler.(count) result == false ? 0 : 1 end
- FUNC =
::FFI::Function.new(:void, [:pointer, :int, :pointer]) do |ctx, argc, argv| callable = unwrap(CApi.sqlite3_user_data(ctx)) params = argv.read_array_of_pointer(argc).map { |v| FFI.sqlite3val2rb(v) } result = callable.(*params) FFI.set_sqlite3_func_result(ctx, result) end
- AGGREGATOR_STEP =
::FFI::Function.new(:void, [:pointer, :int, :pointer]) do |ctx, argc, argv| begin inst = aggregate_instance(ctx) handler_instance = inst.handler_instance params = argv.read_array_of_pointer(argc).map { |v| FFI.sqlite3val2rb(v) } handler_instance.step(*params) rescue => e FFI.rb_errinfo = e end end
- AGGREGATOR_FINAL =
::FFI::Function.new(:void, [:pointer]) do |ctx| begin inst = aggregate_instance(ctx) handler_instance = inst.handler_instance result = handler_instance.finalize FFI.set_sqlite3_func_result(ctx, result) aggregate_instance_destroy(ctx) rescue => e FFI.rb_errinfo = e end end
Class Method Summary collapse
- .aggregate_instance(ctx) ⇒ Object
- .aggregate_instance_destroy(ctx) ⇒ Object
- .check(db, status) ⇒ Object
- .check_msg(db, status, msg) ⇒ Object
- .check_prepare(db, status, sql) ⇒ Object
-
.interned_utf8_cstr(str) ⇒ Object
TODO intern.
- .raise_(db, status) ⇒ Object
- .raise_msg(db, status, msg) ⇒ Object
- .raise_with_sql(db, status, sql) ⇒ Object
- .rb_errinfo ⇒ Object
- .rb_errinfo=(e) ⇒ Object
- .set_sqlite3_func_result(ctx, result) ⇒ Object
- .sqlite3val2rb(val) ⇒ Object
- .status2klass(status) ⇒ Object
- .string_value(obj) ⇒ Object
- .unwrap(ptr) ⇒ Object
- .wrap(obj) ⇒ Object
Class Method Details
.aggregate_instance(ctx) ⇒ Object
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/sqlite3/ffi/aggregator.rb', line 16 def self.aggregate_instance(ctx) aw = FFI.unwrap(CApi.sqlite3_user_data(ctx)) handler_klass = aw.handler_klass inst_ptr = CApi.sqlite3_aggregate_context(ctx, 8) if inst_ptr.null? fatal "SQLite is out-of-merory" end if inst_ptr.read_pointer.null? instances = aw.instances inst = AggregatorInstance.new inst.handler_instance = handler_klass.new instances << inst inst_ptr.write_pointer(FFI.wrap(inst)) else inst = FFI.unwrap(inst_ptr.read_pointer) end if inst.nil? fatal "SQLite called us back on an already destroyed aggregate instance" end inst end |
.aggregate_instance_destroy(ctx) ⇒ Object
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/sqlite3/ffi/aggregator.rb', line 43 def self.aggregate_instance_destroy(ctx) aw = FFI.unwrap(CApi.sqlite3_user_data(ctx)) instances = aw.instances inst_ptr = CApi.sqlite3_aggregate_context(ctx, 0) if inst_ptr.null? || inst_ptr.read_pointer.null? return end inst = FFI.unwrap(inst_ptr.read_pointer) if inst.nil? fatal "attempt to destroy aggregate instance twice" end inst.handler_instance = nil if instances.delete(inst).nil? fatal "must be in instances at that point" end inst_ptr.write_pointer(::FFI::Pointer.new(0)) end |
.check(db, status) ⇒ Object
3 4 5 |
# File 'lib/sqlite3/ffi/exception.rb', line 3 def self.check(db, status) raise_(db, status) end |
.check_msg(db, status, msg) ⇒ Object
7 8 9 |
# File 'lib/sqlite3/ffi/exception.rb', line 7 def self.check_msg(db, status, msg) raise_msg(db, status, msg) end |
.check_prepare(db, status, sql) ⇒ Object
11 12 13 |
# File 'lib/sqlite3/ffi/exception.rb', line 11 def self.check_prepare(db, status, sql) raise_with_sql(db, status, sql) end |
.interned_utf8_cstr(str) ⇒ Object
TODO intern
42 43 44 |
# File 'lib/sqlite3/ffi/utils.rb', line 42 def self.interned_utf8_cstr(str) str.freeze end |
.raise_(db, status) ⇒ Object
76 77 78 79 80 81 82 83 84 |
# File 'lib/sqlite3/ffi/exception.rb', line 76 def self.raise_(db, status) klass = status2klass(status) return if klass.nil? exception = klass.new(CApi.sqlite3_errmsg(db)) exception.instance_variable_set(:@code, status) raise exception end |
.raise_msg(db, status, msg) ⇒ Object
86 87 88 89 90 91 92 93 94 95 |
# File 'lib/sqlite3/ffi/exception.rb', line 86 def self.raise_msg(db, status, msg) klass = status2klass(status) return if klass.nil? exception = klass.new(msg.read_pointer.read_string) exception.instance_variable_set(:@code, status) CApi.sqlite3_free(msg.read_pointer) raise exception end |
.raise_with_sql(db, status, sql) ⇒ Object
97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/sqlite3/ffi/exception.rb', line 97 def self.raise_with_sql(db, status, sql) klass = status2klass(status) return if klass.nil? exception = klass.new(CApi.sqlite3_errmsg(db)) exception.instance_variable_set(:@code, status) if sql exception.instance_variable_set(:@sql, sql) exception.instance_variable_set(:@sql_offset, CApi.sqlite3_error_offset(db)) end raise exception end |
.rb_errinfo ⇒ Object
64 65 66 |
# File 'lib/sqlite3/ffi/utils.rb', line 64 def self.rb_errinfo Thread.current[RB_ERRINFO] end |
.rb_errinfo=(e) ⇒ Object
68 69 70 |
# File 'lib/sqlite3/ffi/utils.rb', line 68 def self.rb_errinfo=(e) Thread.current[RB_ERRINFO] = e end |
.set_sqlite3_func_result(ctx, result) ⇒ Object
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/sqlite3/ffi/utils.rb', line 22 def self.set_sqlite3_func_result(ctx, result) case result when NilClass CApi.sqlite3_result_null(ctx) when Integer CApi.sqlite3_result_int64(ctx, result) when Float CApi.sqlite3_result_double(ctx, result) when String if result.is_a?(Blob) || result.encoding == Encoding::BINARY CApi.sqlite3_result_blob(ctx, result, result.bytesize, CApi::SQLITE_TRANSIENT) else CApi.sqlite3_result_text(ctx, result, result.bytesize, CApi::SQLITE_TRANSIENT) end else raise RuntimeError, "can't return #{result.class.name}" end end |
.sqlite3val2rb(val) ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# File 'lib/sqlite3/ffi/utils.rb', line 3 def self.sqlite3val2rb(val) case CApi.sqlite3_value_type(val) when CApi::SQLITE_INTEGER CApi.sqlite3_value_int64(val) when CApi::SQLITE_FLOAT CApi.sqlite3_value_double(val) when CApi::SQLITE_TEXT len = CApi.sqlite3_value_bytes(val) CApi.sqlite3_value_text(val).read_bytes(len).force_encoding(Encoding::UTF_8).freeze when CApi::SQLITE_BLOB len = CApi.sqlite3_value_bytes(val) CApi.sqlite3_value_text(val).read_bytes(len).freeze when CApi::SQLITE_NULL nil else raise RuntimeError, "bad type" end end |
.status2klass(status) ⇒ Object
.string_value(obj) ⇒ Object
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/sqlite3/ffi/utils.rb', line 46 def self.string_value(obj) unless obj.respond_to?(:to_str) val = case obj when nil "nil" when true, false obj.to_s else obj.class.name end raise TypeError, "no implicit conversion of #{val} into String" end obj.to_str end |
.unwrap(ptr) ⇒ Object
79 80 81 |
# File 'lib/sqlite3/ffi/utils.rb', line 79 def self.unwrap(ptr) OBJECT_REGISTRY[ptr.to_i] || (raise "object not found") end |
.wrap(obj) ⇒ Object
74 75 76 77 |
# File 'lib/sqlite3/ffi/utils.rb', line 74 def self.wrap(obj) OBJECT_REGISTRY[obj.object_id] = obj ::FFI::Pointer.new(obj.object_id) end |