libsql 0.1.0-x64-mingw-ucrt

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.
Files changed (100) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING.md +60 -0
  3. data/HISTORY.md +6 -0
  4. data/LICENSE +31 -0
  5. data/Manifest.txt +96 -0
  6. data/README.md +59 -0
  7. data/Rakefile +28 -0
  8. data/TODO.md +57 -0
  9. data/examples/a.rb +9 -0
  10. data/examples/blob.rb +106 -0
  11. data/examples/define_aggregate.rb +75 -0
  12. data/examples/define_function.rb +104 -0
  13. data/examples/fts5.rb +152 -0
  14. data/examples/gem-db.rb +94 -0
  15. data/examples/schema-info.rb +34 -0
  16. data/ext/libsql/c/extconf.rb +86 -0
  17. data/ext/libsql/c/gen_constants.rb +353 -0
  18. data/ext/libsql/c/libsql_blob.c +240 -0
  19. data/ext/libsql/c/libsql_constants.c +1518 -0
  20. data/ext/libsql/c/libsql_database.c +1188 -0
  21. data/ext/libsql/c/libsql_ext.c +383 -0
  22. data/ext/libsql/c/libsql_ext.h +149 -0
  23. data/ext/libsql/c/libsql_statement.c +649 -0
  24. data/ext/libsql/c/notes.txt +134 -0
  25. data/ext/libsql/c/sqlite3.c +247030 -0
  26. data/ext/libsql/c/sqlite3.h +13436 -0
  27. data/lib/libsql/3.1/libsql_ext.so +0 -0
  28. data/lib/libsql/3.2/libsql_ext.so +0 -0
  29. data/lib/libsql/aggregate.rb +73 -0
  30. data/lib/libsql/blob.rb +186 -0
  31. data/lib/libsql/boolean.rb +42 -0
  32. data/lib/libsql/busy_timeout.rb +47 -0
  33. data/lib/libsql/column.rb +99 -0
  34. data/lib/libsql/csv_table_importer.rb +75 -0
  35. data/lib/libsql/database.rb +933 -0
  36. data/lib/libsql/function.rb +61 -0
  37. data/lib/libsql/index.rb +43 -0
  38. data/lib/libsql/memory_database.rb +15 -0
  39. data/lib/libsql/paths.rb +80 -0
  40. data/lib/libsql/profile_tap.rb +131 -0
  41. data/lib/libsql/progress_handler.rb +21 -0
  42. data/lib/libsql/schema.rb +225 -0
  43. data/lib/libsql/sqlite3/constants.rb +95 -0
  44. data/lib/libsql/sqlite3/database/function.rb +48 -0
  45. data/lib/libsql/sqlite3/database/status.rb +68 -0
  46. data/lib/libsql/sqlite3/libsql_version.rb +32 -0
  47. data/lib/libsql/sqlite3/status.rb +60 -0
  48. data/lib/libsql/sqlite3/version.rb +55 -0
  49. data/lib/libsql/sqlite3.rb +7 -0
  50. data/lib/libsql/statement.rb +421 -0
  51. data/lib/libsql/table.rb +91 -0
  52. data/lib/libsql/taps/console.rb +27 -0
  53. data/lib/libsql/taps/io.rb +74 -0
  54. data/lib/libsql/taps.rb +2 -0
  55. data/lib/libsql/trace_tap.rb +35 -0
  56. data/lib/libsql/type_map.rb +63 -0
  57. data/lib/libsql/type_maps/default_map.rb +166 -0
  58. data/lib/libsql/type_maps/storage_map.rb +38 -0
  59. data/lib/libsql/type_maps/text_map.rb +21 -0
  60. data/lib/libsql/version.rb +8 -0
  61. data/lib/libsql/view.rb +26 -0
  62. data/lib/libsql-ruby.rb +1 -0
  63. data/lib/libsql.rb +51 -0
  64. data/spec/aggregate_spec.rb +158 -0
  65. data/spec/blob_spec.rb +78 -0
  66. data/spec/boolean_spec.rb +24 -0
  67. data/spec/busy_handler.rb +157 -0
  68. data/spec/data/iso-3166-country.txt +242 -0
  69. data/spec/data/iso-3166-schema.sql +22 -0
  70. data/spec/data/iso-3166-subcountry.txt +3995 -0
  71. data/spec/data/make-iso-db.sh +12 -0
  72. data/spec/database_spec.rb +505 -0
  73. data/spec/default_map_spec.rb +92 -0
  74. data/spec/function_spec.rb +78 -0
  75. data/spec/integeration_spec.rb +97 -0
  76. data/spec/iso_3166_database.rb +58 -0
  77. data/spec/json_spec.rb +24 -0
  78. data/spec/libsql_spec.rb +4 -0
  79. data/spec/paths_spec.rb +28 -0
  80. data/spec/progress_handler_spec.rb +91 -0
  81. data/spec/rtree_spec.rb +66 -0
  82. data/spec/schema_spec.rb +131 -0
  83. data/spec/spec_helper.rb +48 -0
  84. data/spec/sqlite3/constants_spec.rb +108 -0
  85. data/spec/sqlite3/database_status_spec.rb +36 -0
  86. data/spec/sqlite3/libsql_version_spec.rb +16 -0
  87. data/spec/sqlite3/status_spec.rb +22 -0
  88. data/spec/sqlite3/version_spec.rb +28 -0
  89. data/spec/sqlite3_spec.rb +53 -0
  90. data/spec/statement_spec.rb +168 -0
  91. data/spec/storage_map_spec.rb +38 -0
  92. data/spec/tap_spec.rb +57 -0
  93. data/spec/text_map_spec.rb +20 -0
  94. data/spec/type_map_spec.rb +14 -0
  95. data/spec/version_spec.rb +8 -0
  96. data/tasks/custom.rake +134 -0
  97. data/tasks/default.rake +257 -0
  98. data/tasks/extension.rake +29 -0
  99. data/tasks/this.rb +208 -0
  100. metadata +329 -0
@@ -0,0 +1,353 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pp'
4
+
5
+ # readin in the sqlite3.h file and parse out all the #define lines
6
+ sqlite3_h_fname = File.expand_path(File.join(File.dirname(__FILE__), "sqlite3.h"))
7
+
8
+ # special handling for those that are function result codes
9
+ result_codes = %w[
10
+ SQLITE_OK
11
+ SQLITE_ERROR
12
+ SQLITE_INTERNAL
13
+ SQLITE_PERM
14
+ SQLITE_ABORT
15
+ SQLITE_BUSY
16
+ SQLITE_LOCKED
17
+ SQLITE_NOMEM
18
+ SQLITE_READONLY
19
+ SQLITE_INTERRUPT
20
+ SQLITE_IOERR
21
+ SQLITE_CORRUPT
22
+ SQLITE_NOTFOUND
23
+ SQLITE_FULL
24
+ SQLITE_CANTOPEN
25
+ SQLITE_PROTOCOL
26
+ SQLITE_EMPTY
27
+ SQLITE_SCHEMA
28
+ SQLITE_TOOBIG
29
+ SQLITE_CONSTRAINT
30
+ SQLITE_MISMATCH
31
+ SQLITE_MISUSE
32
+ SQLITE_NOLFS
33
+ SQLITE_AUTH
34
+ SQLITE_FORMAT
35
+ SQLITE_RANGE
36
+ SQLITE_NOTADB
37
+ SQLITE_NOTICE
38
+ SQLITE_WARNING
39
+ SQLITE_ROW
40
+ SQLITE_DONE
41
+ SQLITE_ERROR_MISSING_COLLSEQ
42
+ SQLITE_ERROR_RETRY
43
+ SQLITE_ERROR_SNAPSHOT
44
+ SQLITE_IOERR_READ
45
+ SQLITE_IOERR_SHORT_READ
46
+ SQLITE_IOERR_WRITE
47
+ SQLITE_IOERR_FSYNC
48
+ SQLITE_IOERR_DIR_FSYNC
49
+ SQLITE_IOERR_TRUNCATE
50
+ SQLITE_IOERR_FSTAT
51
+ SQLITE_IOERR_UNLOCK
52
+ SQLITE_IOERR_RDLOCK
53
+ SQLITE_IOERR_DELETE
54
+ SQLITE_IOERR_BLOCKED
55
+ SQLITE_IOERR_NOMEM
56
+ SQLITE_IOERR_ACCESS
57
+ SQLITE_IOERR_CHECKRESERVEDLOCK
58
+ SQLITE_IOERR_LOCK
59
+ SQLITE_IOERR_CLOSE
60
+ SQLITE_IOERR_DIR_CLOSE
61
+ SQLITE_IOERR_SHMOPEN
62
+ SQLITE_IOERR_SHMSIZE
63
+ SQLITE_IOERR_SHMLOCK
64
+ SQLITE_IOERR_SHMMAP
65
+ SQLITE_IOERR_SEEK
66
+ SQLITE_IOERR_DELETE_NOENT
67
+ SQLITE_IOERR_MMAP
68
+ SQLITE_IOERR_GETTEMPPATH
69
+ SQLITE_IOERR_CONVPATH
70
+ SQLITE_IOERR_VNODE
71
+ SQLITE_IOERR_AUTH
72
+ SQLITE_IOERR_BEGIN_ATOMIC
73
+ SQLITE_IOERR_COMMIT_ATOMIC
74
+ SQLITE_IOERR_ROLLBACK_ATOMIC
75
+ SQLITE_IOERR_DATA
76
+ SQLITE_IOERR_CORRUPTFS
77
+ SQLITE_LOCKED_SHAREDCACHE
78
+ SQLITE_LOCKED_VTAB
79
+ SQLITE_BUSY_RECOVERY
80
+ SQLITE_BUSY_SNAPSHOT
81
+ SQLITE_BUSY_TIMEOUT
82
+ SQLITE_CANTOPEN_NOTEMPDIR
83
+ SQLITE_CANTOPEN_ISDIR
84
+ SQLITE_CANTOPEN_FULLPATH
85
+ SQLITE_CANTOPEN_CONVPATH
86
+ SQLITE_CANTOPEN_DIRTYWAL
87
+ SQLITE_CANTOPEN_SYMLINK
88
+ SQLITE_CORRUPT_VTAB
89
+ SQLITE_CORRUPT_SEQUENCE
90
+ SQLITE_CORRUPT_INDEX
91
+ SQLITE_READONLY_RECOVERY
92
+ SQLITE_READONLY_CANTLOCK
93
+ SQLITE_READONLY_ROLLBACK
94
+ SQLITE_READONLY_DBMOVED
95
+ SQLITE_READONLY_CANTINIT
96
+ SQLITE_READONLY_DIRECTORY
97
+ SQLITE_ABORT_ROLLBACK
98
+ SQLITE_CONSTRAINT_CHECK
99
+ SQLITE_CONSTRAINT_COMMITHOOK
100
+ SQLITE_CONSTRAINT_FOREIGNKEY
101
+ SQLITE_CONSTRAINT_FUNCTION
102
+ SQLITE_CONSTRAINT_NOTNULL
103
+ SQLITE_CONSTRAINT_PRIMARYKEY
104
+ SQLITE_CONSTRAINT_TRIGGER
105
+ SQLITE_CONSTRAINT_UNIQUE
106
+ SQLITE_CONSTRAINT_VTAB
107
+ SQLITE_CONSTRAINT_ROWID
108
+ SQLITE_CONSTRAINT_PINNED
109
+ SQLITE_NOTICE_RECOVER_WAL
110
+ SQLITE_NOTICE_RECOVER_ROLLBACK
111
+ SQLITE_WARNING_AUTOINDEX
112
+ SQLITE_AUTH_USER
113
+ SQLITE_OK_LOAD_PERMANENTLY
114
+ SQLITE_OK_SYMLINK
115
+ ]
116
+
117
+ deprecated_codes = %w[ SQLITE_GET_LOCKPROXYFILE SQLITE_SET_LOCKPROXYFILE SQLITE_LAST_ERRNO ]
118
+ version_codes = %w[ SQLITE_VERSION SQLITE_VERSION_NUMBER SQLITE_SOURCE_ID LIBSQL_VERSION ]
119
+ rtree_codes = %w[ NOT_WITHIN PARTLY_WITHIN FULLY_WITHIN ]
120
+
121
+ authorizer_codes = %w[
122
+ SQLITE_DENY
123
+ SQLITE_IGNORE
124
+ SQLITE_CREATE_INDEX
125
+ SQLITE_CREATE_TABLE
126
+ SQLITE_CREATE_TEMP_INDEX
127
+ SQLITE_CREATE_TEMP_TABLE
128
+ SQLITE_CREATE_TEMP_TRIGGER
129
+ SQLITE_CREATE_TEMP_VIEW
130
+ SQLITE_CREATE_TRIGGER
131
+ SQLITE_CREATE_VIEW
132
+ SQLITE_DELETE
133
+ SQLITE_DROP_INDEX
134
+ SQLITE_DROP_TABLE
135
+ SQLITE_DROP_TEMP_INDEX
136
+ SQLITE_DROP_TEMP_TABLE
137
+ SQLITE_DROP_TEMP_TRIGGER
138
+ SQLITE_DROP_TEMP_VIEW
139
+ SQLITE_DROP_TRIGGER
140
+ SQLITE_DROP_VIEW
141
+ SQLITE_INSERT
142
+ SQLITE_PRAGMA
143
+ SQLITE_READ
144
+ SQLITE_SELECT
145
+ SQLITE_TRANSACTION
146
+ SQLITE_UPDATE
147
+ SQLITE_ATTACH
148
+ SQLITE_DETACH
149
+ SQLITE_ALTER_TABLE
150
+ SQLITE_REINDEX
151
+ SQLITE_ANALYZE
152
+ SQLITE_CREATE_VTABLE
153
+ SQLITE_DROP_VTABLE
154
+ SQLITE_FUNCTION
155
+ SQLITE_SAVEPOINT
156
+ SQLITE_COPY
157
+ SQLITE_RECURSIVE
158
+ ]
159
+
160
+ text_encoding_codes = %w[
161
+ SQLITE_UTF8
162
+ SQLITE_UTF16LE
163
+ SQLITE_UTF16BE
164
+ SQLITE_UTF16
165
+ SQLITE_ANY
166
+ SQLITE_UTF16_ALIGNED
167
+ SQLITE_DETERMINISTIC
168
+ SQLITE_DIRECTONLY
169
+ SQLITE_SUBTYPE
170
+ SQLITE_INNOCUOUS
171
+ ]
172
+
173
+ data_type_codes = %w[
174
+ SQLITE_INTEGER
175
+ SQLITE_FLOAT
176
+ SQLITE_BLOB
177
+ SQLITE_NULL
178
+ SQLITE3_TEXT
179
+ ]
180
+
181
+ fts5_codes = %w[
182
+ FTS5_TOKENIZE_QUERY
183
+ FTS5_TOKENIZE_PREFIX
184
+ FTS5_TOKENIZE_DOCUMENT
185
+ FTS5_TOKENIZE_AUX
186
+ FTS5_TOKEN_COLOCATED
187
+ ]
188
+
189
+ ignore_codes = [
190
+ # vtab related
191
+ "SQLITE_ROLLBACK",
192
+ "SQLITE_FAIL",
193
+ "SQLITE_REPLACE",
194
+ "SQLITE_VTAB_CONSTRAINT_SUPPORT",
195
+ "SQLITE_VTAB_INNOCUOUS",
196
+ "SQLITE_VTAB_DIRECTONLY",
197
+
198
+ # sqlite destructor callback codes
199
+ "SQLITE_STATIC",
200
+ "SQLITE_TRANSIENT",
201
+ ]
202
+
203
+ # oddball name
204
+ module_name_mapping = {
205
+
206
+ "DBCONFIG" => "DBConfig",
207
+ "DBSTATUS" => "DBStatus",
208
+ "IOCAP" => "IOCap",
209
+ "SHM" => "SHM",
210
+ "SCANSTAT" => "ScanStat",
211
+ "STMTSTATUS" => "StatementStatus",
212
+ "CHANGESETAPPLY" => "ChangesetApply",
213
+ "CHANGESETSTART" => "ChangesetStart",
214
+ "TXN" => "Transaction",
215
+ }
216
+
217
+ defines = []
218
+ IO.readlines(sqlite3_h_fname).each do |l|
219
+ result = {
220
+ "c_define" => nil,
221
+ "c_value" => nil,
222
+ "docstring" => nil,
223
+
224
+ "is_error_code" => false,
225
+
226
+ "r_module" => nil,
227
+ "r_constant" => nil,
228
+ }
229
+
230
+ if l =~ /beginning-of-error-codes/ .. l =~ /end-of-error-codes/ then
231
+ result["is_error_code"] = true
232
+ end
233
+
234
+ l.strip!
235
+ md = l.match(/\A#define\s+(\w+)\s+([^\/]+)\s*(\/\*(.*)\*\/)?\Z/)
236
+ next unless md
237
+
238
+ # Name munging
239
+ c_define = md[1]
240
+
241
+ c_parts = c_define.gsub(/^SQLITE_/,'').split("_")
242
+ r_module = c_parts.shift
243
+ r_constant = c_parts.join("_")
244
+
245
+
246
+ # custom module naming so they are human readable
247
+ r_module = module_name_mapping.fetch(r_module) { |m| r_module.capitalize }
248
+
249
+ case c_define
250
+ when *version_codes
251
+ next
252
+
253
+ when *deprecated_codes
254
+ next
255
+
256
+ when *rtree_codes
257
+ r_module = "RTree"
258
+ r_constant = c_define
259
+
260
+ when *result_codes
261
+ r_module = "ResultCode"
262
+ r_constant = c_define.gsub(/^SQLITE_/,'')
263
+
264
+ when *authorizer_codes
265
+ r_module = "Authorizer"
266
+ r_constant = c_define.gsub(/^SQLITE_/,'')
267
+
268
+ when *text_encoding_codes
269
+ r_module = "TextEncoding"
270
+ r_constant = c_define.gsub(/^SQLITE_/,'')
271
+
272
+ when *data_type_codes
273
+ r_module = "DataType"
274
+ r_constant = c_define.gsub(/^SQLITE(3)?_/,'')
275
+
276
+ when *fts5_codes
277
+ r_module = "FTS5"
278
+ r_constant = c_define.gsub(/^FTS5_/,'')
279
+
280
+ when *ignore_codes
281
+ next
282
+
283
+ when /TESTCTRL/ # sqlite3 codes used in testing
284
+ next
285
+
286
+ when /^__/ # sqlite3 internal items
287
+ next
288
+ end
289
+
290
+ result["c_define"] = c_define
291
+ result["c_value"] = md[2].strip
292
+ if !md[4].nil? && (md[4].strip.length > 0) then
293
+ result["docstring"] = md[4].strip
294
+ end
295
+ result["r_module"] = r_module
296
+ result["r_constant"] = r_constant
297
+
298
+ defines << result
299
+ end
300
+
301
+ #
302
+ # rework defines into constants
303
+ #
304
+ CONSTANTS = defines.group_by{ |d| d["r_module"] }
305
+
306
+ fname = File.expand_path(File.join(File.dirname(__FILE__), "libsql_constants.c"))
307
+
308
+
309
+ File.open(fname, "w+") do |f|
310
+ f.puts "/* Generated by gen_constants.rb -- do not edit */"
311
+ f.puts
312
+ f.puts '#include "libsql_ext.h"'
313
+ f.puts '/**'
314
+ f.puts ' * Document-class: ::Libsql::SQLite3::Constants'
315
+ f.puts ' *'
316
+ f.puts ' * class holding constants in the sqlite extension'
317
+ f.puts ' */'
318
+ f.puts "void Init_libsql_ext_constants( )"
319
+ f.puts "{"
320
+ f.puts
321
+ f.puts ' VALUE ma = rb_define_module("Libsql");'
322
+ f.puts ' VALUE mas = rb_define_module_under(ma, "SQLite3");'
323
+ f.puts
324
+ f.puts " /*"
325
+ f.puts " * module encapsulating all the SQLite C extension constants "
326
+ f.puts " */"
327
+ f.puts ' VALUE mC = rb_define_module_under( mas, "Constants");'
328
+
329
+
330
+ CONSTANTS.keys.sort.each do |mod|
331
+ f.puts " /**"
332
+ f.puts " * module encapsulating the SQLite3 C extension constants for #{mod}"
333
+ f.puts " */"
334
+ f.puts " VALUE mC_#{mod} = rb_define_module_under(mC, \"#{mod}\");"
335
+ f.puts
336
+ end
337
+
338
+ CONSTANTS.keys.sort.each do |mod|
339
+ const_set = CONSTANTS[mod]
340
+ const_set.sort_by { |c| c["c_define"] }.each do |result|
341
+ sql_const = result["c_define"]
342
+ const_doc = " /* no meaningful autogenerated documentation -- constant is self explanatory ?*/"
343
+ if !result["docstring"].nil? then
344
+ const_doc = " /* #{result['c_value']} -- #{result['docstring']} */"
345
+ end
346
+ ruby_constant = result['r_constant']
347
+ f.puts const_doc
348
+ f.puts " rb_define_const(mC_#{mod}, \"#{ruby_constant}\", INT2FIX(#{sql_const}));"
349
+ f.puts
350
+ end
351
+ end
352
+ f.puts "}"
353
+ end
@@ -0,0 +1,240 @@
1
+ #include "libsql_ext.h"
2
+ /**
3
+ * Copyright (c) 2023 Jeremy Hinegardner
4
+ * All rights reserved. See LICENSE and/or COPYING for details.
5
+ *
6
+ * vim: shiftwidth=4
7
+ */
8
+
9
+ /* class Amalgliate::SQLite3::Blob */
10
+ VALUE cLS_Blob;
11
+
12
+ /**
13
+ * call-seq:
14
+ * Blob.new( database, table_name, column_name, row_id, flag ) -> Blob
15
+ *
16
+ * Create a new Blob object and associate it with the approriate, database,
17
+ * table, column and row. +flag+ indicates if the Blob is to be opened for
18
+ * writing "w" or reading "r".
19
+ *
20
+ */
21
+ VALUE libsql_ext_sqlite3_blob_initialize( VALUE self, VALUE db, VALUE db_name, VALUE table_name, VALUE column_name, VALUE rowid, VALUE flag)
22
+ {
23
+ libsql_ext_sqlite3_blob *libsql_ext_blob;
24
+ int rc;
25
+ libsql_ext_sqlite3 *libsql_ext_db;
26
+ char *zDb = StringValuePtr( db_name );
27
+ char *zTable = StringValuePtr( table_name );
28
+ char *zColumn = StringValuePtr( column_name );
29
+ sqlite3_int64 iRow = NUM2SQLINT64( rowid ) ;
30
+ VALUE flag_str = StringValue( flag );
31
+ int flags = 0;
32
+
33
+ /* extract the blob struct */
34
+ Data_Get_Struct(self, libsql_ext_sqlite3_blob, libsql_ext_blob);
35
+
36
+ /* extract the sqlite3 db struct */
37
+ Data_Get_Struct(db, libsql_ext_sqlite3, libsql_ext_db);
38
+
39
+ /* make sure that the flags are valid, only 'r' or 'w' are allowed */
40
+ if ( ( RSTRING_LEN( flag_str ) != 1) ||
41
+ ( ( 'r' != RSTRING_PTR( flag_str )[0] ) &&
42
+ ( 'w' != RSTRING_PTR( flag_str )[0] ))) {
43
+ rb_raise( eLS_Error, "Error opening Blob in db = %s, table = %s, column = %s, rowid = %lu. Invalid flag '%s'. Must be either 'w' or 'r'\n",
44
+ zDb, zTable, zColumn, (unsigned long)iRow, RSTRING_PTR( flag_str ));
45
+ }
46
+
47
+ /* switch to write mode */
48
+ if ( 'w' == RSTRING_PTR( flag_str )[0] ) {
49
+ flags = 1;
50
+ }
51
+
52
+ /* open the blob and associate the db to it */
53
+ rc = sqlite3_blob_open( libsql_ext_db->db, zDb, zTable, zColumn, iRow, flags, &( libsql_ext_blob->blob ) );
54
+ if ( SQLITE_OK != rc ) {
55
+ rb_raise( eLS_Error, "Error opening Blob in db = %s, table = %s, column = %s, rowid = %lu : [SQLITE_ERROR %d] %s\n", zDb, zTable, zColumn, (unsigned long)iRow, rc, sqlite3_errmsg( libsql_ext_db->db) );
56
+ }
57
+ libsql_ext_blob->length = sqlite3_blob_bytes( libsql_ext_blob->blob );
58
+ libsql_ext_blob->db = libsql_ext_db->db;
59
+
60
+ /* if a block is given then yield self and close the blob when done */
61
+ if ( rb_block_given_p() ) {
62
+ rb_yield( self );
63
+ libsql_ext_sqlite3_blob_close( self );
64
+ return Qnil;
65
+ } else {
66
+ return self;
67
+ }
68
+ }
69
+
70
+ /**
71
+ * call-seq:
72
+ * blob.close -> nil
73
+ *
74
+ * Closes the blob.
75
+ */
76
+ VALUE libsql_ext_sqlite3_blob_close( VALUE self )
77
+ {
78
+ libsql_ext_sqlite3_blob *libsql_ext_blob;
79
+ int rc;
80
+
81
+ Data_Get_Struct(self, libsql_ext_sqlite3_blob, libsql_ext_blob);
82
+ rc = sqlite3_blob_close( libsql_ext_blob->blob );
83
+ if ( SQLITE_OK != rc ) {
84
+ rb_raise(eLS_Error, "Error closing blob: [SQLITE_ERROR %d] %s\n",
85
+ rc, sqlite3_errmsg( libsql_ext_blob->db ));
86
+ }
87
+
88
+
89
+ return Qnil;
90
+ }
91
+
92
+
93
+ /**
94
+ * call-seq:
95
+ * blob.length -> length in bytes of the blob
96
+ *
97
+ * Returns the number of bytes in the blob.
98
+ */
99
+ VALUE libsql_ext_sqlite3_blob_length( VALUE self )
100
+ {
101
+ libsql_ext_sqlite3_blob *libsql_ext_blob;
102
+
103
+ Data_Get_Struct(self, libsql_ext_sqlite3_blob, libsql_ext_blob);
104
+
105
+ return INT2FIX( libsql_ext_blob->length );
106
+ }
107
+
108
+ /**
109
+ * call-seq:
110
+ * blob.read( int ) -> String containting int number of bytes or nil if eof.
111
+ *
112
+ * returns int number of bytes as a String from the database
113
+ */
114
+ VALUE libsql_ext_sqlite3_blob_read( VALUE self, VALUE length )
115
+ {
116
+ libsql_ext_sqlite3_blob *libsql_ext_blob;
117
+ int rc;
118
+ int n = NUM2INT( length );
119
+ void *buf = NULL;
120
+ VALUE result;
121
+
122
+ Data_Get_Struct(self, libsql_ext_sqlite3_blob, libsql_ext_blob);
123
+
124
+ /* we have to be exact on the number of bytes to read. n + current_offset
125
+ * cannot be larger than the blob's length
126
+ */
127
+ if ( (n + libsql_ext_blob->current_offset > libsql_ext_blob->length)) {
128
+ n = libsql_ext_blob->length - libsql_ext_blob->current_offset;
129
+ }
130
+
131
+ if ( libsql_ext_blob->current_offset == libsql_ext_blob->length ) {
132
+ return Qnil;
133
+ }
134
+
135
+ buf = (void *)malloc( n );
136
+ rc = sqlite3_blob_read( libsql_ext_blob->blob, buf, n, libsql_ext_blob->current_offset);
137
+
138
+ if ( rc != SQLITE_OK ) {
139
+ rb_raise(eLS_Error, "Error reading %d bytes blob at offset %d: [SQLITE_ERROR %d] %s\n",
140
+ n, libsql_ext_blob->current_offset, rc, sqlite3_errmsg( libsql_ext_blob->db ));
141
+ }
142
+
143
+ libsql_ext_blob->current_offset += n;
144
+
145
+ result = rb_str_new( (char*)buf, n );
146
+ free( buf );
147
+ return result;
148
+
149
+ }
150
+
151
+ /**
152
+ * call-seq:
153
+ * blob.write( buf ) -> int
154
+ *
155
+ * writes the contents of the string buffer to the blob and returns the number
156
+ * of bytes written.
157
+ *
158
+ */
159
+ VALUE libsql_ext_sqlite3_blob_write( VALUE self, VALUE buf )
160
+ {
161
+ libsql_ext_sqlite3_blob *libsql_ext_blob;
162
+ int rc;
163
+ VALUE str = StringValue( buf );
164
+ int n = (int)RSTRING_LEN( str );
165
+ char *chk_buf = NULL;
166
+
167
+ Data_Get_Struct(self, libsql_ext_sqlite3_blob, libsql_ext_blob);
168
+
169
+ rc = sqlite3_blob_write( libsql_ext_blob->blob, RSTRING_PTR(str), n, libsql_ext_blob->current_offset);
170
+
171
+ if ( rc != SQLITE_OK ) {
172
+ rb_raise(eLS_Error, "Error writing %d bytes blob at offset %d: [SQLITE_ERROR %d] %s\n",
173
+ n, libsql_ext_blob->current_offset, rc, sqlite3_errmsg( libsql_ext_blob->db ));
174
+ }
175
+
176
+ chk_buf = (char *) malloc( n + 1);
177
+ chk_buf[n] = '\0';
178
+ sqlite3_blob_read( libsql_ext_blob->blob, chk_buf, n, 0);
179
+
180
+ libsql_ext_blob->current_offset += n;
181
+
182
+ return INT2FIX( n );
183
+
184
+ }
185
+
186
+
187
+ /***********************************************************************
188
+ * Ruby life cycle methods
189
+ ***********************************************************************/
190
+
191
+ /*
192
+ * garbage collector free method for the libsql_ext_sqlite3_blob structure
193
+ */
194
+ void libsql_ext_sqlite3_blob_free(libsql_ext_sqlite3_blob* wrapper)
195
+ {
196
+ free(wrapper);
197
+ return;
198
+ }
199
+
200
+ /*
201
+ * allocate the libsql_ext_blob structure
202
+ */
203
+ VALUE libsql_ext_sqlite3_blob_alloc(VALUE klass)
204
+ {
205
+ libsql_ext_sqlite3_blob *wrapper = ALLOC( libsql_ext_sqlite3_blob );
206
+ VALUE obj ;
207
+
208
+ wrapper->current_offset = 0;
209
+ wrapper->db = NULL;
210
+ obj = Data_Wrap_Struct(klass, NULL, libsql_ext_sqlite3_blob_free, wrapper);
211
+ return obj;
212
+ }
213
+
214
+
215
+ /**
216
+ * Document-class: Libsql::SQLite3::Blob
217
+ *
218
+ * The Blob class enables incremental IO on blob items. If you do not need
219
+ * incremental IO on a binary object, then you do not need to use Blob.
220
+ */
221
+
222
+ void Init_libsql_ext_blob( )
223
+ {
224
+
225
+ VALUE ma = rb_define_module("Libsql");
226
+ VALUE mas = rb_define_module_under(ma, "SQLite3");
227
+
228
+ /*
229
+ * Encapsulate the SQLite3 Statement handle in a class
230
+ */
231
+ cLS_Blob = rb_define_class_under( mas, "Blob", rb_cObject );
232
+ rb_define_alloc_func(cLS_Blob, libsql_ext_sqlite3_blob_alloc);
233
+ rb_define_method(cLS_Blob, "initialize", libsql_ext_sqlite3_blob_initialize, 6);
234
+ rb_define_method(cLS_Blob, "close", libsql_ext_sqlite3_blob_close, 0);
235
+ rb_define_method(cLS_Blob, "read", libsql_ext_sqlite3_blob_read, 1);
236
+ rb_define_method(cLS_Blob, "write", libsql_ext_sqlite3_blob_write, 1);
237
+ rb_define_method(cLS_Blob, "length", libsql_ext_sqlite3_blob_length, 0);
238
+ }
239
+
240
+