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,48 @@
1
+ module ::Libsql::SQLite3
2
+ class Database
3
+ ##
4
+ # A wrapper around a proc for use as an SQLite Ddatabase fuction
5
+ #
6
+ # f = Function.new( 'md5', lambda { |x| Digest::MD5.hexdigest( x.to_s ) } )
7
+ #
8
+ class Function
9
+
10
+ # the name of the function, and how it will be called in SQL
11
+ attr_reader :name
12
+
13
+ # The unique signature of this function. This is used to determin if the
14
+ # function is already registered or not
15
+ #
16
+ def self.signature( name, arity )
17
+ "#{name}/#{arity}"
18
+ end
19
+
20
+ # Initialize with the name and the Proc
21
+ #
22
+ def initialize( name, _proc )
23
+ @name = name
24
+ @function = _proc
25
+ end
26
+
27
+ # The unique signature of this function
28
+ #
29
+ def signature
30
+ @signature ||= Function.signature( name, arity )
31
+ end
32
+ alias :to_s :signature
33
+
34
+ # The arity of SQL function, -1 means it is takes a variable number of
35
+ # arguments.
36
+ #
37
+ def arity
38
+ @function.arity
39
+ end
40
+
41
+ # Invoke the proc
42
+ #
43
+ def call( *args )
44
+ @function.call( *args )
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,68 @@
1
+ require 'libsql/sqlite3/constants'
2
+ module ::Libsql::SQLite3
3
+ class Database
4
+ #
5
+ # A Stat represents a single Database Status code and its current highwater mark.
6
+ #
7
+ # Some stats may not have a current or a highwater value, in those cases
8
+ # the associated _has_current?_ or _has_highwater?_ method returns false and the
9
+ # _current_ or _highwater_ method also returns +nil+.
10
+ #
11
+ class Stat
12
+ attr_reader :name
13
+ attr_reader :code
14
+
15
+ def initialize( api_db, name )
16
+ @name = name
17
+ @code = ::Libsql::SQLite3::Constants::DBStatus.value_from_name( name )
18
+ @current = nil
19
+ @highwater = nil
20
+ @api_db = api_db
21
+ end
22
+
23
+ def current
24
+ update!
25
+ return @current
26
+ end
27
+
28
+ def highwater
29
+ update!
30
+ return @highwater
31
+ end
32
+
33
+ #
34
+ # reset the given stat's highwater mark. This will also populate the
35
+ # _@current_ and _@highwater_ instance variables
36
+ #
37
+ def reset!
38
+ update!( true )
39
+ end
40
+ end
41
+
42
+ #
43
+ # Top level Status object holding all the Stat objects indicating the DBStatus
44
+ # of the SQLite3 C library.
45
+ #
46
+ class DBStatus
47
+ ::Libsql::SQLite3::Constants::DBStatus.constants.each do |const_name|
48
+ method_name = const_name.downcase
49
+ module_eval( <<-code, __FILE__, __LINE__ )
50
+ def #{method_name}
51
+ @#{method_name} ||= ::Libsql::SQLite3::Database::Stat.new( self.api_db, '#{method_name}' )
52
+ end
53
+ code
54
+ end
55
+
56
+ attr_reader :api_db
57
+
58
+ def initialize( api_db )
59
+ @api_db = api_db
60
+ end
61
+ end
62
+
63
+ # return the DBstatus object for the sqlite database
64
+ def status
65
+ @status ||= DBStatus.new( self )
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,32 @@
1
+ #--
2
+ # Copyright (c) 2023 Jeremy Hinegardner
3
+ # All rights reserved. See LICENSE and/or COPYING for details.
4
+ #++
5
+ module ::Libsql
6
+ module SQLite3
7
+ module LibsqlVersion
8
+ def self.compiled_matches_runtime?
9
+ self.compiled_version == self.runtime_version
10
+ end
11
+ end
12
+
13
+ # Version of libsql that ships with
14
+ LIBSQL_VERSION = LibsqlVersion.to_s.freeze
15
+ end
16
+ end
17
+
18
+ unless ::Libsql::SQLite3::LibsqlVersion.compiled_matches_runtime? then
19
+ warn <<eom
20
+ You are seeing something odd. The compiled version of libsql that
21
+ is embedded in this extension is for some reason, not being used.
22
+ The version in the extension is #{::Libsql::SQLite3::LibsqlVersion.compiled_version} and the version that
23
+ as been loaded as a shared library is #{::Libsql::SQLite3::LibsqlVersion.runtime_version}. These versions
24
+ should be the same, but they are not.
25
+
26
+ One known issue is if you are using this libary in conjunction with
27
+ Hitimes on Mac OS X. You should make sure that "require 'libsql'"
28
+ appears before "require 'hitimes'" in your ruby code.
29
+
30
+ This is a non-trivial problem, and I am working on it.
31
+ eom
32
+ end
@@ -0,0 +1,60 @@
1
+ require 'libsql/sqlite3/constants'
2
+ module ::Libsql::SQLite3
3
+
4
+ #
5
+ # A Stat represents a single Status code and its current highwater mark.
6
+ #
7
+ # Some stats may not have a current or a highwater value, in those cases
8
+ # the associated _has_current?_ or _has_highwater?_ method returns false and the
9
+ # _current_ or _highwater_ method also returns +nil+.
10
+ #
11
+ class Stat
12
+ attr_reader :name
13
+ attr_reader :code
14
+
15
+ def initialize( name )
16
+ @name = name
17
+ @code = ::Libsql::SQLite3::Constants::Status.value_from_name( name )
18
+ @current = nil
19
+ @highwater = nil
20
+ end
21
+
22
+ def current
23
+ update!
24
+ return @current
25
+ end
26
+
27
+ def highwater
28
+ update!
29
+ return @highwater
30
+ end
31
+
32
+ #
33
+ # reset the given stat's highwater mark. This will also populate the
34
+ # _@current_ and _@highwater_ instance variables
35
+ #
36
+ def reset!
37
+ update!( true )
38
+ end
39
+ end
40
+
41
+ #
42
+ # Top level Status object holding all the Stat objects indicating the Status
43
+ # of the SQLite3 C library.
44
+ #
45
+ class Status
46
+ ::Libsql::SQLite3::Constants::Status.constants.each do |const_name|
47
+ method_name = const_name.downcase
48
+ module_eval( <<-code, __FILE__, __LINE__ )
49
+ def #{method_name}
50
+ @#{method_name} ||= ::Libsql::SQLite3::Stat.new( '#{method_name}' )
51
+ end
52
+ code
53
+ end
54
+ end
55
+
56
+ # return the status object for the sqlite database
57
+ def self.status
58
+ @status ||= Status.new
59
+ end
60
+ end
@@ -0,0 +1,55 @@
1
+ #--
2
+ # Copyright (c) 2023 Jeremy Hinegardner
3
+ # All rights reserved. See LICENSE and/or COPYING for details.
4
+ #++
5
+ module ::Libsql
6
+ module SQLite3
7
+ module Version
8
+ # Sqlite3 version number is equal to
9
+ # MAJOR * 1_000_000 + MINOR * 1_000 + RELEASE
10
+
11
+ # major version number of the SQLite C library
12
+ MAJOR = (to_i / 1_000_000).freeze
13
+
14
+ # minor version number of the SQLite C library
15
+ MINOR = ((to_i % 1_000_000) / 1_000).freeze
16
+
17
+ # release version number of the SQLite C library
18
+ RELEASE = (to_i % 1_000).freeze
19
+
20
+ #
21
+ # call-seq:
22
+ # ::Libsql::SQLite3::Version.to_a -> [ MAJOR, MINOR, RELEASE ]
23
+ #
24
+ # Return the SQLite C library version number as an array of MAJOR, MINOR,
25
+ # RELEASE
26
+ #
27
+ def self.to_a
28
+ [ MAJOR, MINOR, RELEASE ]
29
+ end
30
+
31
+ def self.compiled_matches_runtime?
32
+ self.compiled_version == self.runtime_version
33
+ end
34
+ end
35
+
36
+ # Version of SQLite that ships with ::Libsql
37
+ VERSION = Version.to_s.freeze
38
+ end
39
+ end
40
+
41
+ unless ::Libsql::SQLite3::Version.compiled_matches_runtime? then
42
+ warn <<eom
43
+ You are seeing something odd. The compiled version of SQLite that
44
+ is embedded in this extension is for some reason, not being used.
45
+ The version in the extension is #{::Libsql::SQLite3::Version.compiled_version} and the version that
46
+ as been loaded as a shared library is #{::Libsql::SQLite3::Version.runtime_version}. These versions
47
+ should be the same, but they are not.
48
+
49
+ One known issue is if you are using this libary in conjunction with
50
+ Hitimes on Mac OS X. You should make sure that "require 'libsql'"
51
+ appears before "require 'hitimes'" in your ruby code.
52
+
53
+ This is a non-trivial problem, and I am working on it.
54
+ eom
55
+ end
@@ -0,0 +1,7 @@
1
+ require 'libsql/version'
2
+ require 'libsql/sqlite3/version'
3
+ require 'libsql/sqlite3/libsql_version'
4
+ require 'libsql/sqlite3/constants'
5
+ require 'libsql/sqlite3/status'
6
+ require 'libsql/sqlite3/database/status'
7
+ require 'libsql/sqlite3/database/function'
@@ -0,0 +1,421 @@
1
+ #--
2
+ # Copyright (c) 2023 Jeremy Hinegardner
3
+ # All rights reserved. See LICENSE and/or COPYING for details.
4
+ #++
5
+ #
6
+ require 'date'
7
+ require 'arrayfields'
8
+ require 'ostruct'
9
+
10
+ module ::Libsql
11
+ class Statement
12
+
13
+ include ::Libsql::SQLite3::Constants
14
+
15
+ attr_reader :db
16
+ attr_reader :api
17
+
18
+ class << self
19
+ # special column names that indicate that indicate the column is a rowid
20
+ def rowid_column_names
21
+ @rowid_column_names ||= %w[ ROWID OID _ROWID_ ]
22
+ end
23
+ end
24
+
25
+ ##
26
+ # Initialize a new statement on the database.
27
+ #
28
+ def initialize( db, sql )
29
+ @db = db
30
+ #prepare_method = @db.utf16? ? :prepare16 : :prepare
31
+ prepare_method = :prepare
32
+ @param_positions = {}
33
+ @stmt_api = @db.api.send( prepare_method, sql )
34
+ @blobs_to_write = []
35
+ @rowid_index = nil
36
+ @result_meta = nil
37
+ @open = true
38
+ end
39
+
40
+ ##
41
+ # is the statement open for business
42
+ #
43
+ def open?
44
+ @open
45
+ end
46
+
47
+ ##
48
+ # Is the special column "ROWID", "OID", or "_ROWID_" used?
49
+ #
50
+ def using_rowid_column?
51
+ not @rowid_index.nil?
52
+ end
53
+
54
+ ##
55
+ # reset the Statement back to it state right after the constructor returned,
56
+ # except if any variables have been bound to parameters, those are still
57
+ # bound.
58
+ #
59
+ def reset!
60
+ @stmt_api.reset!
61
+ @param_positions = {}
62
+ @blobs_to_write.clear
63
+ @rowid_index = nil
64
+ end
65
+
66
+ ##
67
+ # reset the Statement back to it state right after the constructor returned,
68
+ # AND clear all parameter bindings.
69
+ #
70
+ def reset_and_clear_bindings!
71
+ reset!
72
+ @stmt_api.clear_bindings!
73
+ end
74
+
75
+ ##
76
+ # reset the statment in preparation for executing it again
77
+ #
78
+ def reset_for_next_execute!
79
+ @stmt_api.reset!
80
+ @stmt_api.clear_bindings!
81
+ @blobs_to_write.clear
82
+ end
83
+
84
+ ##
85
+ # Execute the statement with the given parameters
86
+ #
87
+ # If a block is given, then yield each returned row to the block. If no
88
+ # block is given then return all rows from the result. No matter what the
89
+ # prepared statement should be reset before returning the final time.
90
+ #
91
+ def execute( *params )
92
+ bind( *params )
93
+ begin
94
+ # save the error state at the beginning of the execution. We only want to
95
+ # reraise the error if it was raised during this execution.
96
+ s_before = $!
97
+ if block_given? then
98
+ while row = next_row
99
+ yield row
100
+ end
101
+ else
102
+ all_rows
103
+ end
104
+ ensure
105
+ s = $!
106
+ begin
107
+ reset_for_next_execute!
108
+ rescue
109
+ # rescuing nothing on purpose
110
+ end
111
+ raise s if s != s_before
112
+ end
113
+ end
114
+
115
+ ##
116
+ # Bind parameters to the sql statement.
117
+ #
118
+ # Bindings in SQLite can have a number of formats:
119
+ #
120
+ # ?
121
+ # ?num
122
+ # :var
123
+ # @var
124
+ # $var
125
+ #
126
+ # Where 'num' is an Integer and 'var'is an alphanumerical variable.
127
+ # They may exist in the SQL for which this Statement was created.
128
+ #
129
+ # ::Libsql binds parameters to these variables in the following manner:
130
+ #
131
+ # If bind is passed in an Array, either as +bind( "foo", "bar", "baz")+ or
132
+ # as bind( ["foo", "bar", "baz"] ) then each of the params is assumed to be
133
+ # positionally bound to the statement( ?, ?num ).
134
+ #
135
+ # If bind is passed a Hash, either as +bind( :foo => 1, :bar => 'sqlite' )+
136
+ # or as bind( { :foo => 1, 'bar' => 'sqlite' }) then it is assumed that each
137
+ # parameter should be bound as a named parameter (:var, @var, $var).
138
+ #
139
+ # If bind is not passed any parameters, or nil, then nothing happens.
140
+ #
141
+ def bind( *params )
142
+ if params.nil? or params.empty? then
143
+ check_parameter_count!( 0 )
144
+ return nil
145
+ end
146
+
147
+ if params.first.instance_of?( Hash ) then
148
+ bind_named_parameters( params.first )
149
+ elsif params.first.instance_of?( Array ) then
150
+ bind_positional_parameters( *params )
151
+ else
152
+ bind_positional_parameters( params )
153
+ end
154
+ end
155
+
156
+ ##
157
+ # Bind parameters to the statement based upon named parameters
158
+ #
159
+ def bind_named_parameters( params )
160
+ check_parameter_count!( params.size )
161
+ params.each_pair do | param, value |
162
+ position = param_position_of( param )
163
+ if position > 0 then
164
+ bind_parameter_to( position, value )
165
+ else
166
+ raise ::Libsql::Error, "Unable to find parameter '#{param}' in SQL statement [#{sql}]"
167
+ end
168
+ end
169
+ end
170
+
171
+ ##
172
+ # Bind parameters to the statements based upon positions.
173
+ #
174
+ def bind_positional_parameters( params )
175
+ check_parameter_count!( params.size )
176
+ params.each_with_index do |value, index|
177
+ position = index + 1
178
+ bind_parameter_to( position, value )
179
+ end
180
+ end
181
+
182
+ ##
183
+ # bind a single parameter to a particular position
184
+ #
185
+ def bind_parameter_to( position, value )
186
+ bind_type = db.type_map.bind_type_of( value )
187
+ case bind_type
188
+ when DataType::FLOAT
189
+ @stmt_api.bind_double( position, value )
190
+ when DataType::INTEGER
191
+ @stmt_api.bind_int64( position, value )
192
+ when DataType::NULL
193
+ @stmt_api.bind_null( position )
194
+ when DataType::TEXT
195
+ @stmt_api.bind_text( position, value.to_s )
196
+ when DataType::BLOB
197
+ if value.incremental? then
198
+ @stmt_api.bind_zeroblob( position, value.length )
199
+ @blobs_to_write << value
200
+ else
201
+ @stmt_api.bind_blob( position, value.source )
202
+ end
203
+ else
204
+ raise ::Libsql::Error, "Unknown binding type of #{bind_type} from #{db.type_map.class.name}.bind_type_of"
205
+ end
206
+ end
207
+
208
+
209
+ ##
210
+ # Find and cache the binding parameter indexes
211
+ #
212
+ def param_position_of( name )
213
+ ns = name.to_s
214
+ unless pos = @param_positions[ns]
215
+ pos = @param_positions[ns] = @stmt_api.parameter_index( ns )
216
+ end
217
+ return pos
218
+ end
219
+
220
+ ##
221
+ # Check and make sure that the number of parameters aligns with the number
222
+ # that sqlite expects
223
+ #
224
+ def check_parameter_count!( num )
225
+ expected = @stmt_api.parameter_count
226
+ if num != expected then
227
+ raise ::Libsql::Error, "#{sql} has #{expected} parameters, but #{num} were passed to bind."
228
+ end
229
+ return expected
230
+ end
231
+
232
+ ##
233
+ # Write any blobs that have been bound to parameters to the database. This
234
+ # assumes that the blobs go into the last inserted row
235
+ #
236
+ def write_blobs
237
+ unless @blobs_to_write.empty?
238
+ @blobs_to_write.each do |blob|
239
+ blob.write_to_column!
240
+ end
241
+ end
242
+ end
243
+
244
+ ##
245
+ # Iterate over the results of the statement returning each row of results
246
+ # as a hash by +column_name+. The column names are the value after an
247
+ # 'AS' in the query or default chosen by sqlite.
248
+ #
249
+ def each
250
+ while row = next_row
251
+ yield row
252
+ end
253
+ return self
254
+ end
255
+
256
+ ##
257
+ # Return the next row of data, with type conversion as indicated by the
258
+ # Database#type_map
259
+ #
260
+ def next_row
261
+ row = []
262
+ case rc = @stmt_api.step
263
+ when ResultCode::ROW
264
+ result_meta.each_with_index do |col, idx|
265
+ value = nil
266
+ column_type = @stmt_api.column_type( idx )
267
+ case column_type
268
+ when DataType::TEXT
269
+ value = @stmt_api.column_text( idx )
270
+ when DataType::FLOAT
271
+ value = @stmt_api.column_double( idx )
272
+ when DataType::INTEGER
273
+ value = @stmt_api.column_int64( idx )
274
+ when DataType::NULL
275
+ value = nil
276
+ when DataType::BLOB
277
+ # if the rowid column is encountered, then we can use an incremental
278
+ # blob api, otherwise we have to use the all at once version.
279
+ if using_rowid_column? then
280
+ value = ::Libsql::Blob.new( :db_blob => SQLite3::Blob.new( db.api,
281
+ col.schema.db,
282
+ col.schema.table,
283
+ col.schema.name,
284
+ @stmt_api.column_int64( @rowid_index ),
285
+ "r"),
286
+ :column => col.schema)
287
+ else
288
+ value = ::Libsql::Blob.new( :string => @stmt_api.column_blob( idx ), :column => col.schema )
289
+ end
290
+ else
291
+ raise ::Libsql::Error, "BUG! : Unknown SQLite column type of #{column_type}"
292
+ end
293
+
294
+ row << db.type_map.result_value_of( col.schema.declared_data_type, value )
295
+ end
296
+ row.fields = result_fields
297
+ when ResultCode::DONE
298
+ row = nil
299
+ write_blobs
300
+ else
301
+ self.close # must close so that the error message is guaranteed to be pushed into the database handler
302
+ # and we can can call last_error_message on it
303
+ msg = "SQLITE ERROR #{rc} (#{::Libsql::SQLite3::Constants::ResultCode.name_from_value( rc )}) : #{@db.api.last_error_message}"
304
+ raise ::Libsql::SQLite3::Error, msg
305
+ end
306
+ return row
307
+ end
308
+
309
+ ##
310
+ # Return all rows from the statement as one array
311
+ #
312
+ def all_rows
313
+ rows = []
314
+ while row = next_row
315
+ rows << row
316
+ end
317
+ return rows
318
+ end
319
+
320
+ ##
321
+ # Inspect the statement and gather all the meta information about the
322
+ # results, include the name of the column result column and the origin
323
+ # column. The origin column is the original database.table.column the value
324
+ # comes from.
325
+ #
326
+ # The full meta information from the origin column is also obtained for help
327
+ # in doing type conversion.
328
+ #
329
+ # As iteration over the row meta informatio happens, record if the special
330
+ # "ROWID", "OID", or "_ROWID_" column is encountered. If that column is
331
+ # encountered then we make note of it.
332
+ #
333
+ def result_meta
334
+ unless @result_meta
335
+ meta = []
336
+ column_count.times do |idx|
337
+ column_meta = ::OpenStruct.new
338
+ column_meta.name = @stmt_api.column_name( idx )
339
+
340
+ db_name = @stmt_api.column_database_name( idx )
341
+ tbl_name = @stmt_api.column_table_name( idx )
342
+ col_name = @stmt_api.column_origin_name( idx )
343
+
344
+ column_meta.schema = ::Libsql::Column.new( db_name, tbl_name, col_name, idx )
345
+ column_meta.schema.declared_data_type = @stmt_api.column_declared_type( idx )
346
+
347
+ # only check for rowid if we have a table name and it is not one of the
348
+ # sqlite_master tables. We could get recursion in those cases.
349
+ if not using_rowid_column? and tbl_name and
350
+ not %w[ sqlite_master sqlite_temp_master ].include?( tbl_name ) and is_column_rowid?( tbl_name, col_name ) then
351
+ @rowid_index = idx
352
+ end
353
+
354
+ meta << column_meta
355
+ end
356
+
357
+ @result_meta = meta
358
+ end
359
+ return @result_meta
360
+ end
361
+
362
+ ##
363
+ # is the column indicated by the Column a 'rowid' column
364
+ #
365
+ def is_column_rowid?( table_name, column_name )
366
+ table_schema = @db.schema.tables[table_name]
367
+ return false unless table_schema
368
+
369
+ column_schema = table_schema.columns[column_name]
370
+ if column_schema then
371
+ if column_schema.primary_key? and column_schema.declared_data_type and column_schema.declared_data_type.upcase == "INTEGER" then
372
+ return true
373
+ end
374
+ else
375
+ return true if Statement.rowid_column_names.include?( column_name.upcase )
376
+ end
377
+ return false
378
+ end
379
+
380
+ ##
381
+ # Return the array of field names for the result set, the field names are
382
+ # all strings
383
+ #
384
+ def result_fields
385
+ @fields ||= result_meta.collect { |m| m.name }
386
+ end
387
+
388
+ ##
389
+ # Return any unsued SQL from the statement
390
+ #
391
+ def remaining_sql
392
+ @stmt_api.remaining_sql
393
+ end
394
+
395
+
396
+ ##
397
+ # return the number of columns in the result of this query
398
+ #
399
+ def column_count
400
+ @stmt_api.column_count
401
+ end
402
+
403
+ ##
404
+ # return the raw sql that was originally used to prepare the statement
405
+ #
406
+ def sql
407
+ @stmt_api.sql
408
+ end
409
+
410
+ ##
411
+ # Close the statement. The statement is no longer valid for use after it
412
+ # has been closed.
413
+ #
414
+ def close
415
+ if open? then
416
+ @stmt_api.close
417
+ @open = false
418
+ end
419
+ end
420
+ end
421
+ end