sqlite3-ffi 0.1.0

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.
@@ -0,0 +1,88 @@
1
+ require "sqlite3/constants"
2
+
3
+ module SQLite3
4
+ class Exception < ::StandardError
5
+ # A convenience for accessing the error code for this exception.
6
+ attr_reader :code
7
+
8
+ # If the error is associated with a SQL query, this is the query
9
+ attr_reader :sql
10
+
11
+ # If the error is associated with a particular offset in a SQL query, this is the non-negative
12
+ # offset. If the offset is not available, this will be -1.
13
+ attr_reader :sql_offset
14
+
15
+ def message
16
+ [super, sql_error].compact.join(":\n")
17
+ end
18
+
19
+ private def sql_error
20
+ return nil unless @sql
21
+ return @sql.chomp unless @sql_offset >= 0
22
+
23
+ offset = @sql_offset
24
+ sql.lines.flat_map do |line|
25
+ if offset >= 0 && line.length > offset
26
+ blanks = " " * offset
27
+ offset = -1
28
+ [line.chomp, blanks + "^"]
29
+ else
30
+ offset -= line.length if offset
31
+ line.chomp
32
+ end
33
+ end.join("\n")
34
+ end
35
+ end
36
+
37
+ class SQLException < Exception; end
38
+
39
+ class InternalException < Exception; end
40
+
41
+ class PermissionException < Exception; end
42
+
43
+ class AbortException < Exception; end
44
+
45
+ class BusyException < Exception; end
46
+
47
+ class LockedException < Exception; end
48
+
49
+ class MemoryException < Exception; end
50
+
51
+ class ReadOnlyException < Exception; end
52
+
53
+ class InterruptException < Exception; end
54
+
55
+ class IOException < Exception; end
56
+
57
+ class CorruptException < Exception; end
58
+
59
+ class NotFoundException < Exception; end
60
+
61
+ class FullException < Exception; end
62
+
63
+ class CantOpenException < Exception; end
64
+
65
+ class ProtocolException < Exception; end
66
+
67
+ class EmptyException < Exception; end
68
+
69
+ class SchemaChangedException < Exception; end
70
+
71
+ class TooBigException < Exception; end
72
+
73
+ class ConstraintException < Exception; end
74
+
75
+ class MismatchException < Exception; end
76
+
77
+ class MisuseException < Exception; end
78
+
79
+ class UnsupportedException < Exception; end
80
+
81
+ class AuthorizationException < Exception; end
82
+
83
+ class FormatException < Exception; end
84
+
85
+ class RangeException < Exception; end
86
+
87
+ class NotADatabaseException < Exception; end
88
+ end
@@ -0,0 +1,89 @@
1
+ module SQLite3
2
+ module FFI
3
+ class AggregatorWrapper
4
+ attr_reader :handler_klass, :instances
5
+
6
+ def initialize(handler_klass)
7
+ @handler_klass = handler_klass
8
+ @instances = []
9
+ end
10
+ end
11
+
12
+ class AggregatorInstance
13
+ attr_accessor :handler_instance
14
+ end
15
+
16
+ def self.aggregate_instance(ctx)
17
+ aw = FFI.unwrap(CApi.sqlite3_user_data(ctx))
18
+ handler_klass = aw.handler_klass
19
+ inst_ptr = CApi.sqlite3_aggregate_context(ctx, 8)
20
+
21
+ if inst_ptr.null?
22
+ fatal "SQLite is out-of-merory"
23
+ end
24
+
25
+ if inst_ptr.read_pointer.null?
26
+ instances = aw.instances
27
+
28
+ inst = AggregatorInstance.new
29
+ inst.handler_instance = handler_klass.new
30
+ instances << inst
31
+ inst_ptr.write_pointer(FFI.wrap(inst))
32
+ else
33
+ inst = FFI.unwrap(inst_ptr.read_pointer)
34
+ end
35
+
36
+ if inst.nil?
37
+ fatal "SQLite called us back on an already destroyed aggregate instance"
38
+ end
39
+
40
+ inst
41
+ end
42
+
43
+ def self.aggregate_instance_destroy(ctx)
44
+ aw = FFI.unwrap(CApi.sqlite3_user_data(ctx))
45
+ instances = aw.instances
46
+ inst_ptr = CApi.sqlite3_aggregate_context(ctx, 0)
47
+
48
+ if inst_ptr.null? || inst_ptr.read_pointer.null?
49
+ return
50
+ end
51
+
52
+ inst = FFI.unwrap(inst_ptr.read_pointer)
53
+
54
+ if inst.nil?
55
+ fatal "attempt to destroy aggregate instance twice"
56
+ end
57
+
58
+ inst.handler_instance = nil
59
+ if instances.delete(inst).nil?
60
+ fatal "must be in instances at that point"
61
+ end
62
+
63
+ inst_ptr.write_pointer(::FFI::Pointer.new(0))
64
+ end
65
+
66
+ AGGREGATOR_STEP = ::FFI::Function.new(:void, [:pointer, :int, :pointer]) do |ctx, argc, argv|
67
+ begin
68
+ inst = aggregate_instance(ctx)
69
+ handler_instance = inst.handler_instance
70
+ params = argv.read_array_of_pointer(argc).map { |v| FFI.sqlite3val2rb(v) }
71
+ handler_instance.step(*params)
72
+ rescue => e
73
+ FFI.rb_errinfo = e
74
+ end
75
+ end
76
+
77
+ AGGREGATOR_FINAL = ::FFI::Function.new(:void, [:pointer]) do |ctx|
78
+ begin
79
+ inst = aggregate_instance(ctx)
80
+ handler_instance = inst.handler_instance
81
+ result = handler_instance.finalize
82
+ FFI.set_sqlite3_func_result(ctx, result)
83
+ aggregate_instance_destroy(ctx)
84
+ rescue => e
85
+ FFI.rb_errinfo = e
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,59 @@
1
+ module SQLite3
2
+ class Backup
3
+ def initialize(dstdb, dstname, srcdb, srcname)
4
+ if srcdb.closed?
5
+ raise ArgumentError, "cannot backup from a closed database"
6
+ end
7
+ if dstdb.closed?
8
+ raise ArgumentError, "cannot backup to a closed database"
9
+ end
10
+
11
+ ddb = dstdb.instance_variable_get(:@db)
12
+ @p =
13
+ FFI::CApi.sqlite3_backup_init(
14
+ ddb,
15
+ FFI.string_value(dstname),
16
+ srcdb.instance_variable_get(:@db),
17
+ FFI.string_value(srcname)
18
+ )
19
+
20
+ if @p.null?
21
+ FFI.check(ddb, FFI::CApi.sqlite3_errcode(ddb))
22
+ end
23
+ end
24
+
25
+ def step(n_page)
26
+ require_open_backup
27
+
28
+ FFI::CApi.sqlite3_backup_step(@p, n_page)
29
+ end
30
+
31
+ def finish
32
+ require_open_backup
33
+
34
+ FFI::CApi.sqlite3_backup_finish(@p)
35
+ @p = nil
36
+ nil
37
+ end
38
+
39
+ def remaining
40
+ require_open_backup
41
+
42
+ FFI::CApi.sqlite3_backup_remaining(@p)
43
+ end
44
+
45
+ def pagecount
46
+ require_open_backup
47
+
48
+ FFI::CApi.sqlite3_backup_pagecount(@p)
49
+ end
50
+
51
+ private
52
+
53
+ def require_open_backup
54
+ if @p.nil? || @p.null?
55
+ raise SQLite3::Exception, "cannot use a closed backup"
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,214 @@
1
+ module SQLite3
2
+ module FFI
3
+ module CApi
4
+ extend ::FFI::Library
5
+
6
+ # TODO vendor sqlite3
7
+ ffi_lib "sqlite3"
8
+
9
+ SQLITE_OK = 0
10
+ SQLITE_ERROR = 1
11
+ SQLITE_INTERNAL = 2
12
+ SQLITE_PERM = 3
13
+ SQLITE_ABORT = 4
14
+ SQLITE_BUSY = 5
15
+ SQLITE_LOCKED = 6
16
+ SQLITE_NOMEM = 7
17
+ SQLITE_READONLY = 8
18
+ SQLITE_INTERRUPT = 9
19
+ SQLITE_IOERR = 10
20
+ SQLITE_CORRUPT = 11
21
+ SQLITE_NOTFOUND = 12
22
+ SQLITE_FULL = 13
23
+ SQLITE_CANTOPEN = 14
24
+ SQLITE_PROTOCOL = 15
25
+ SQLITE_EMPTY = 16
26
+ SQLITE_SCHEMA = 17
27
+ SQLITE_TOOBIG = 18
28
+ SQLITE_CONSTRAINT = 19
29
+ SQLITE_MISMATCH = 20
30
+ SQLITE_MISUSE = 21
31
+ SQLITE_NOLFS = 22
32
+ SQLITE_AUTH = 23
33
+ SQLITE_FORMAT = 24
34
+ SQLITE_RANGE = 25
35
+ SQLITE_NOTADB = 26
36
+ SQLITE_NOTICE = 27
37
+ SQLITE_WARNING = 28
38
+ SQLITE_ROW = 100
39
+ SQLITE_DONE = 101
40
+
41
+ SQLITE_INTEGER = 1
42
+ SQLITE_FLOAT = 2
43
+ SQLITE_TEXT = 3
44
+ SQLITE_BLOB = 4
45
+ SQLITE_NULL = 5
46
+
47
+ SQLITE_UTF8 = 1
48
+ SQLITE_UTF16LE = 2
49
+ SQLITE_UTF16BE = 3
50
+ SQLITE_UTF16 = 4
51
+ SQLITE_ANY = 5
52
+ SQLITE_UTF16_ALIGNED = 8
53
+
54
+ SQLITE_DENY = 1
55
+ SQLITE_IGNORE = 2
56
+
57
+ SQLITE_STATIC = ::FFI::Pointer.new(0)
58
+ SQLITE_TRANSIENT = ::FFI::Pointer.new(-1)
59
+
60
+ SQLITE_DBCONFIG_MAINDBNAME = 1000
61
+ SQLITE_DBCONFIG_LOOKASIDE = 1001
62
+ SQLITE_DBCONFIG_ENABLE_FKEY = 1002
63
+ SQLITE_DBCONFIG_ENABLE_TRIGGER = 1003
64
+ SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER = 1004
65
+ SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION = 1005
66
+ SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE = 1006
67
+ SQLITE_DBCONFIG_ENABLE_QPSG = 1007
68
+ SQLITE_DBCONFIG_TRIGGER_EQP = 1008
69
+ SQLITE_DBCONFIG_RESET_DATABASE = 1009
70
+ SQLITE_DBCONFIG_DEFENSIVE = 1010
71
+ SQLITE_DBCONFIG_WRITABLE_SCHEMA = 1011
72
+ SQLITE_DBCONFIG_LEGACY_ALTER_TABLE = 1012
73
+ SQLITE_DBCONFIG_DQS_DML = 1013
74
+ SQLITE_DBCONFIG_DQS_DDL = 1014
75
+ SQLITE_DBCONFIG_ENABLE_VIEW = 1015
76
+ SQLITE_DBCONFIG_LEGACY_FILE_FORMAT = 1016
77
+ SQLITE_DBCONFIG_TRUSTED_SCHEMA = 1017
78
+ SQLITE_DBCONFIG_STMT_SCANSTATUS = 1018
79
+ SQLITE_DBCONFIG_REVERSE_SCANORDER = 1019
80
+ SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE = 1020
81
+ SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE = 1021
82
+ SQLITE_DBCONFIG_ENABLE_COMMENTS = 1022
83
+ SQLITE_DBCONFIG_MAX = 1022
84
+
85
+ SQLITE_OPEN_READONLY = 0x00000001
86
+ SQLITE_OPEN_READWRITE = 0x00000002
87
+ SQLITE_OPEN_CREATE = 0x00000004
88
+ SQLITE_OPEN_DELETEONCLOSE = 0x00000008
89
+ SQLITE_OPEN_EXCLUSIVE = 0x00000010
90
+ SQLITE_OPEN_AUTOPROXY = 0x00000020
91
+ SQLITE_OPEN_URI = 0x00000040
92
+ SQLITE_OPEN_MEMORY = 0x00000080
93
+ SQLITE_OPEN_MAIN_DB = 0x00000100
94
+ SQLITE_OPEN_TEMP_DB = 0x00000200
95
+ SQLITE_OPEN_TRANSIENT_DB = 0x00000400
96
+ SQLITE_OPEN_MAIN_JOURNAL = 0x00000800
97
+ SQLITE_OPEN_TEMP_JOURNAL = 0x00001000
98
+ SQLITE_OPEN_SUBJOURNAL = 0x00002000
99
+ SQLITE_OPEN_SUPER_JOURNAL = 0x00004000
100
+ SQLITE_OPEN_NOMUTEX = 0x00008000
101
+ SQLITE_OPEN_FULLMUTEX = 0x00010000
102
+ SQLITE_OPEN_SHAREDCACHE = 0x00020000
103
+ SQLITE_OPEN_PRIVATECACHE = 0x00040000
104
+ SQLITE_OPEN_WAL = 0x00080000
105
+ SQLITE_OPEN_NOFOLLOW = 0x01000000
106
+ SQLITE_OPEN_EXRESCODE = 0x02000000
107
+ SQLITE_OPEN_MASTER_JOURNAL = 0x00004000
108
+
109
+ SQLITE_STMTSTATUS_FULLSCAN_STEP = 1
110
+ SQLITE_STMTSTATUS_SORT = 2
111
+ SQLITE_STMTSTATUS_AUTOINDEX = 3
112
+ SQLITE_STMTSTATUS_VM_STEP = 4
113
+ SQLITE_STMTSTATUS_REPREPARE = 5
114
+ SQLITE_STMTSTATUS_RUN = 6
115
+ SQLITE_STMTSTATUS_FILTER_MISS = 7
116
+ SQLITE_STMTSTATUS_FILTER_HIT = 8
117
+ SQLITE_STMTSTATUS_MEMUSED = 99
118
+
119
+ SQLITE_TRACE_STMT = 0x01
120
+ SQLITE_TRACE_PROFILE = 0x02
121
+ SQLITE_TRACE_ROW = 0x04
122
+ SQLITE_TRACE_CLOSE = 0x08
123
+
124
+ attach_function :sqlite3_aggregate_context, [:pointer, :int], :pointer
125
+ attach_function :sqlite3_backup_finish, [:pointer], :int
126
+ attach_function :sqlite3_backup_init, [:pointer, :string, :pointer, :string], :pointer
127
+ attach_function :sqlite3_backup_pagecount, [:pointer], :int
128
+ attach_function :sqlite3_backup_remaining, [:pointer], :int
129
+ attach_function :sqlite3_backup_step, [:pointer, :int], :int
130
+ attach_function :sqlite3_bind_blob, [:pointer, :int, :pointer, :int, :pointer], :int
131
+ attach_function :sqlite3_bind_double, [:pointer, :int, :double], :int
132
+ attach_function :sqlite3_bind_int64, [:pointer, :int, :int64], :int
133
+ attach_function :sqlite3_bind_null, [:pointer, :int], :int
134
+ attach_function :sqlite3_bind_parameter_count, [:pointer], :int
135
+ attach_function :sqlite3_bind_parameter_index, [:pointer, :string], :int
136
+ attach_function :sqlite3_bind_text, [:pointer, :int, :string, :int, :pointer], :int
137
+ attach_function :sqlite3_bind_text16, [:pointer, :int, :pointer, :int, :pointer], :int
138
+ attach_function :sqlite3_busy_handler, [:pointer, :pointer, :pointer], :int
139
+ attach_function :sqlite3_busy_timeout, [:pointer, :int], :int
140
+ attach_function :sqlite3_changes, [:pointer], :int
141
+ attach_function :sqlite3_clear_bindings, [:pointer], :int
142
+ attach_function :sqlite3_close_v2, [:pointer], :int
143
+ attach_function :sqlite3_column_blob, [:pointer, :int], :pointer
144
+ attach_function :sqlite3_column_bytes, [:pointer, :int], :int
145
+ attach_function :sqlite3_column_count, [:pointer], :int
146
+ attach_function :sqlite3_column_decltype, [:pointer, :int], :string
147
+ attach_function :sqlite3_column_double, [:pointer, :int], :double
148
+ attach_function :sqlite3_column_int64, [:pointer, :int], :int64
149
+ attach_function :sqlite3_column_name, [:pointer, :int], :string
150
+ attach_function :sqlite3_column_text, [:pointer, :int], :pointer
151
+ attach_function :sqlite3_column_type, [:pointer, :int], :int
152
+ attach_function :sqlite3_complete, [:string], :int
153
+ attach_function :sqlite3_create_collation, [:pointer, :string, :int, :pointer, :pointer], :int
154
+ attach_function :sqlite3_create_function, [:pointer, :string, :int, :int, :pointer, :pointer, :pointer, :pointer], :int
155
+ attach_function :sqlite3_db_config, [:pointer, :int, :varargs], :int
156
+ attach_function :sqlite3_db_handle, [:pointer], :pointer
157
+ attach_function :sqlite3_db_filename, [:pointer, :string], :pointer
158
+ attach_function :sqlite3_db_release_memory, [:pointer], :int
159
+ attach_function :sqlite3_errcode, [:pointer], :int
160
+ attach_function :sqlite3_errmsg, [:pointer], :string
161
+ attach_function :sqlite3_error_offset, [:pointer], :int
162
+ attach_function :sqlite3_exec, [:pointer, :string, :pointer, :pointer, :pointer], :int
163
+ attach_function :sqlite3_expanded_sql, [:pointer], :string
164
+ attach_function :sqlite3_extended_result_codes, [:pointer, :int], :int
165
+ attach_function :sqlite3_file_control, [:pointer, :string, :int, :pointer], :int
166
+ attach_function :sqlite3_finalize, [:pointer], :int
167
+ attach_function :sqlite3_free, [:pointer], :void
168
+ attach_function :sqlite3_get_autocommit, [:pointer], :int
169
+ attach_function :sqlite3_interrupt, [:pointer], :void
170
+ attach_function :sqlite3_last_insert_rowid, [:pointer], :int64
171
+ attach_function :sqlite3_libversion, [], :string
172
+ attach_function :sqlite3_libversion_number, [], :int
173
+ attach_function :sqlite3_open16, [:pointer, :pointer], :int
174
+ attach_function :sqlite3_open_v2, [:string, :pointer, :int, :pointer], :int
175
+ attach_function :sqlite3_prepare_v2, [:pointer, :string, :int, :pointer, :pointer], :int
176
+ attach_function :sqlite3_progress_handler, [:pointer, :int, :pointer, :pointer], :void
177
+ attach_function :sqlite3_reset, [:pointer], :int
178
+ attach_function :sqlite3_result_blob, [:pointer, :pointer, :int, :pointer], :void
179
+ attach_function :sqlite3_result_double, [:pointer, :double], :void
180
+ attach_function :sqlite3_result_int64, [:pointer, :int64], :void
181
+ attach_function :sqlite3_result_null, [:pointer], :void
182
+ attach_function :sqlite3_result_text, [:pointer, :string, :int, :pointer], :void
183
+ attach_function :sqlite3_set_authorizer, [:pointer, :pointer, :pointer], :int
184
+ attach_function :sqlite3_sql, [:pointer], :string
185
+ attach_function :sqlite3_status64, [:int, :pointer, :pointer, :int], :int
186
+ attach_function :sqlite3_step, [:pointer], :int
187
+ attach_function :sqlite3_stmt_status, [:pointer, :int, :int], :int
188
+ attach_function :sqlite3_threadsafe, [], :int
189
+ attach_function :sqlite3_total_changes, [:pointer], :int
190
+ attach_function :sqlite3_trace_v2, [:pointer, :uint, :pointer, :pointer], :int
191
+ attach_function :sqlite3_user_data, [:pointer], :pointer
192
+ attach_function :sqlite3_value_blob, [:pointer], :pointer
193
+ attach_function :sqlite3_value_bytes, [:pointer], :int
194
+ attach_function :sqlite3_value_double, [:pointer], :double
195
+ attach_function :sqlite3_value_int64, [:pointer], :int64
196
+ attach_function :sqlite3_value_text, [:pointer], :pointer
197
+ attach_function :sqlite3_value_type, [:pointer], :int
198
+
199
+ HAVE_SQLITE3_ENABLE_LOAD_EXTENSION = begin
200
+ attach_function :sqlite3_enable_load_extension, [:pointer, :int], :int
201
+ true
202
+ rescue ::FFI::NotFoundError
203
+ false
204
+ end
205
+
206
+ HAVE_SQLITE3_LOAD_EXTENSION = begin
207
+ attach_function :sqlite3_load_extension, [:pointer, :string, :string, :pointer], :int
208
+ true
209
+ rescue ::FFI::NotFoundError
210
+ false
211
+ end
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,14 @@
1
+ module SQLite3
2
+ module FFI
3
+ module CoreExt
4
+ # must override for Active Record
5
+ def gem(name, ...)
6
+ # TODO check version
7
+ return if name == "sqlite3"
8
+ super
9
+ end
10
+ end
11
+
12
+ Kernel.prepend CoreExt
13
+ end
14
+ end