amalgalite 0.10.1-x86-mingw32
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.
- data/HISTORY +201 -0
- data/LICENSE +29 -0
- data/README +51 -0
- data/bin/amalgalite-pack +126 -0
- data/examples/a.rb +9 -0
- data/examples/blob.rb +88 -0
- data/examples/bootstrap.rb +36 -0
- data/examples/define_aggregate.rb +75 -0
- data/examples/define_function.rb +104 -0
- data/examples/gem-db.rb +94 -0
- data/examples/gems.db +0 -0
- data/examples/require_me.rb +11 -0
- data/examples/requires.rb +42 -0
- data/examples/schema-info.rb +34 -0
- data/ext/amalgalite/amalgalite3.c +290 -0
- data/ext/amalgalite/amalgalite3.h +151 -0
- data/ext/amalgalite/amalgalite3_blob.c +240 -0
- data/ext/amalgalite/amalgalite3_constants.c +221 -0
- data/ext/amalgalite/amalgalite3_database.c +1148 -0
- data/ext/amalgalite/amalgalite3_requires_bootstrap.c +210 -0
- data/ext/amalgalite/amalgalite3_statement.c +639 -0
- data/ext/amalgalite/extconf.rb +36 -0
- data/ext/amalgalite/gen_constants.rb +130 -0
- data/ext/amalgalite/sqlite3.c +106729 -0
- data/ext/amalgalite/sqlite3.h +5626 -0
- data/ext/amalgalite/sqlite3_options.h +4 -0
- data/ext/amalgalite/sqlite3ext.h +380 -0
- data/gemspec.rb +60 -0
- data/lib/amalgalite.rb +43 -0
- data/lib/amalgalite/1.8/amalgalite3.so +0 -0
- data/lib/amalgalite/1.9/amalgalite3.so +0 -0
- data/lib/amalgalite/aggregate.rb +67 -0
- data/lib/amalgalite/blob.rb +186 -0
- data/lib/amalgalite/boolean.rb +42 -0
- data/lib/amalgalite/busy_timeout.rb +47 -0
- data/lib/amalgalite/column.rb +97 -0
- data/lib/amalgalite/core_ext/kernel/require.rb +21 -0
- data/lib/amalgalite/database.rb +947 -0
- data/lib/amalgalite/function.rb +61 -0
- data/lib/amalgalite/index.rb +43 -0
- data/lib/amalgalite/packer.rb +226 -0
- data/lib/amalgalite/paths.rb +70 -0
- data/lib/amalgalite/profile_tap.rb +131 -0
- data/lib/amalgalite/progress_handler.rb +21 -0
- data/lib/amalgalite/requires.rb +120 -0
- data/lib/amalgalite/schema.rb +191 -0
- data/lib/amalgalite/sqlite3.rb +6 -0
- data/lib/amalgalite/sqlite3/constants.rb +80 -0
- data/lib/amalgalite/sqlite3/database/function.rb +48 -0
- data/lib/amalgalite/sqlite3/database/status.rb +68 -0
- data/lib/amalgalite/sqlite3/status.rb +60 -0
- data/lib/amalgalite/sqlite3/version.rb +37 -0
- data/lib/amalgalite/statement.rb +414 -0
- data/lib/amalgalite/table.rb +90 -0
- data/lib/amalgalite/taps.rb +2 -0
- data/lib/amalgalite/taps/console.rb +27 -0
- data/lib/amalgalite/taps/io.rb +71 -0
- data/lib/amalgalite/trace_tap.rb +35 -0
- data/lib/amalgalite/type_map.rb +63 -0
- data/lib/amalgalite/type_maps/default_map.rb +167 -0
- data/lib/amalgalite/type_maps/storage_map.rb +40 -0
- data/lib/amalgalite/type_maps/text_map.rb +22 -0
- data/lib/amalgalite/version.rb +37 -0
- data/lib/amalgalite/view.rb +26 -0
- data/spec/aggregate_spec.rb +169 -0
- data/spec/amalgalite_spec.rb +4 -0
- data/spec/blob_spec.rb +81 -0
- data/spec/boolean_spec.rb +23 -0
- data/spec/busy_handler.rb +165 -0
- data/spec/database_spec.rb +494 -0
- data/spec/default_map_spec.rb +87 -0
- data/spec/function_spec.rb +94 -0
- data/spec/integeration_spec.rb +111 -0
- data/spec/packer_spec.rb +60 -0
- data/spec/paths_spec.rb +28 -0
- data/spec/progress_handler_spec.rb +105 -0
- data/spec/requires_spec.rb +23 -0
- data/spec/rtree_spec.rb +71 -0
- data/spec/schema_spec.rb +120 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/sqlite3/constants_spec.rb +65 -0
- data/spec/sqlite3/database_status_spec.rb +36 -0
- data/spec/sqlite3/status_spec.rb +18 -0
- data/spec/sqlite3/version_spec.rb +14 -0
- data/spec/sqlite3_spec.rb +53 -0
- data/spec/statement_spec.rb +161 -0
- data/spec/storage_map_spec.rb +41 -0
- data/spec/tap_spec.rb +59 -0
- data/spec/text_map_spec.rb +23 -0
- data/spec/type_map_spec.rb +17 -0
- data/spec/version_spec.rb +15 -0
- data/tasks/announce.rake +43 -0
- data/tasks/config.rb +107 -0
- data/tasks/distribution.rake +77 -0
- data/tasks/documentation.rake +32 -0
- data/tasks/extension.rake +141 -0
- data/tasks/rspec.rake +33 -0
- data/tasks/rubyforge.rake +59 -0
- data/tasks/utils.rb +80 -0
- metadata +237 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'amalgalite/sqlite3/constants'
|
2
|
+
module Amalgalite::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 = ::Amalgalite::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
|
+
::Amalgalite::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} ||= Amalgalite::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,60 @@
|
|
1
|
+
require 'amalgalite/sqlite3/constants'
|
2
|
+
module Amalgalite::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 = ::Amalgalite::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
|
+
::Amalgalite::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} ||= Amalgalite::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,37 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2008 Jeremy Hinegardner
|
3
|
+
# All rights reserved. See LICENSE and/or COPYING for details.
|
4
|
+
#++
|
5
|
+
module Amalgalite
|
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
|
+
# Amalgalite::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
|
+
end
|
32
|
+
|
33
|
+
# Version of SQLite that ships with Amalgalite
|
34
|
+
VERSION = Version.to_s.freeze
|
35
|
+
end
|
36
|
+
Version.freeze
|
37
|
+
end
|
@@ -0,0 +1,414 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2008 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 Amalgalite
|
11
|
+
class Statement
|
12
|
+
|
13
|
+
include ::Amalgalite::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 => e
|
109
|
+
end
|
110
|
+
raise s if s != s_before
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# Bind parameters to the sql statement.
|
116
|
+
#
|
117
|
+
# Bindings in SQLite can have a number of formats:
|
118
|
+
#
|
119
|
+
# ?
|
120
|
+
# ?num
|
121
|
+
# :var
|
122
|
+
# @var
|
123
|
+
# $var
|
124
|
+
#
|
125
|
+
# Where 'num' is an Integer and 'var'is an alphanumerical variable.
|
126
|
+
# They may exist in the SQL for which this Statement was created.
|
127
|
+
#
|
128
|
+
# Amalgalite binds parameters to these variables in the following manner:
|
129
|
+
#
|
130
|
+
# If bind is passed in an Array, either as +bind( "foo", "bar", "baz")+ or
|
131
|
+
# as bind( ["foo", "bar", "baz"] ) then each of the params is assumed to be
|
132
|
+
# positionally bound to the statement( ?, ?num ).
|
133
|
+
#
|
134
|
+
# If bind is passed a Hash, either as +bind( :foo => 1, :bar => 'sqlite' )+
|
135
|
+
# or as bind( { :foo => 1, 'bar' => 'sqlite' }) then it is assumed that each
|
136
|
+
# parameter should be bound as a named parameter (:var, @var, $var).
|
137
|
+
#
|
138
|
+
# If bind is not passed any parameters, or nil, then nothing happens.
|
139
|
+
#
|
140
|
+
def bind( *params )
|
141
|
+
if params.nil? or params.empty? then
|
142
|
+
check_parameter_count!( 0 )
|
143
|
+
return nil
|
144
|
+
end
|
145
|
+
|
146
|
+
if params.first.instance_of?( Hash ) then
|
147
|
+
bind_named_parameters( params.first )
|
148
|
+
else
|
149
|
+
bind_positional_parameters( params )
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
##
|
154
|
+
# Bind parameters to the statement based upon named parameters
|
155
|
+
#
|
156
|
+
def bind_named_parameters( params )
|
157
|
+
check_parameter_count!( params.size )
|
158
|
+
params.each_pair do | param, value |
|
159
|
+
position = param_position_of( param )
|
160
|
+
if position > 0 then
|
161
|
+
bind_parameter_to( position, value )
|
162
|
+
else
|
163
|
+
raise Amalgalite::Error, "Unable to find parameter '#{param}' in SQL statement [#{sql}]"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
##
|
169
|
+
# Bind parameters to the statements based upon positions.
|
170
|
+
#
|
171
|
+
def bind_positional_parameters( params )
|
172
|
+
check_parameter_count!( params.size )
|
173
|
+
params.each_with_index do |value, index|
|
174
|
+
position = index + 1
|
175
|
+
bind_parameter_to( position, value )
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
##
|
180
|
+
# bind a single parameter to a particular position
|
181
|
+
#
|
182
|
+
def bind_parameter_to( position, value )
|
183
|
+
bind_type = db.type_map.bind_type_of( value )
|
184
|
+
case bind_type
|
185
|
+
when DataType::FLOAT
|
186
|
+
@stmt_api.bind_double( position, value )
|
187
|
+
when DataType::INTEGER
|
188
|
+
@stmt_api.bind_int64( position, value )
|
189
|
+
when DataType::NULL
|
190
|
+
@stmt_api.bind_null( position )
|
191
|
+
when DataType::TEXT
|
192
|
+
@stmt_api.bind_text( position, value.to_s )
|
193
|
+
when DataType::BLOB
|
194
|
+
if value.incremental? then
|
195
|
+
@stmt_api.bind_zeroblob( position, value.length )
|
196
|
+
@blobs_to_write << value
|
197
|
+
else
|
198
|
+
@stmt_api.bind_blob( position, value.source )
|
199
|
+
end
|
200
|
+
else
|
201
|
+
raise ::Amalgalite::Error, "Unknown binding type of #{bind_type} from #{db.type_map.class.name}.bind_type_of"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
|
206
|
+
##
|
207
|
+
# Find and cache the binding parameter indexes
|
208
|
+
#
|
209
|
+
def param_position_of( name )
|
210
|
+
ns = name.to_s
|
211
|
+
unless pos = @param_positions[ns]
|
212
|
+
pos = @param_positions[ns] = @stmt_api.parameter_index( ns )
|
213
|
+
end
|
214
|
+
return pos
|
215
|
+
end
|
216
|
+
|
217
|
+
##
|
218
|
+
# Check and make sure that the number of parameters aligns with the number
|
219
|
+
# that sqlite expects
|
220
|
+
#
|
221
|
+
def check_parameter_count!( num )
|
222
|
+
expected = @stmt_api.parameter_count
|
223
|
+
if num != expected then
|
224
|
+
raise Amalgalite::Error, "#{sql} has #{expected} parameters, but #{num} were passed to bind."
|
225
|
+
end
|
226
|
+
return expected
|
227
|
+
end
|
228
|
+
|
229
|
+
##
|
230
|
+
# Write any blobs that have been bound to parameters to the database. This
|
231
|
+
# assumes that the blobs go into the last inserted row
|
232
|
+
#
|
233
|
+
def write_blobs
|
234
|
+
unless @blobs_to_write.empty?
|
235
|
+
@blobs_to_write.each do |blob|
|
236
|
+
blob.write_to_column!
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
##
|
242
|
+
# Iterate over the results of the statement returning each row of results
|
243
|
+
# as a hash by +column_name+. The column names are the value after an
|
244
|
+
# 'AS' in the query or default chosen by sqlite.
|
245
|
+
#
|
246
|
+
def each
|
247
|
+
while row = next_row
|
248
|
+
yield row
|
249
|
+
end
|
250
|
+
return self
|
251
|
+
end
|
252
|
+
|
253
|
+
##
|
254
|
+
# Return the next row of data, with type conversion as indicated by the
|
255
|
+
# Database#type_map
|
256
|
+
#
|
257
|
+
def next_row
|
258
|
+
row = []
|
259
|
+
case rc = @stmt_api.step
|
260
|
+
when ResultCode::ROW
|
261
|
+
result_meta.each_with_index do |col, idx|
|
262
|
+
value = nil
|
263
|
+
column_type = @stmt_api.column_type( idx )
|
264
|
+
case column_type
|
265
|
+
when DataType::TEXT
|
266
|
+
value = @stmt_api.column_text( idx )
|
267
|
+
when DataType::FLOAT
|
268
|
+
value = @stmt_api.column_double( idx )
|
269
|
+
when DataType::INTEGER
|
270
|
+
value = @stmt_api.column_int64( idx )
|
271
|
+
when DataType::NULL
|
272
|
+
value = nil
|
273
|
+
when DataType::BLOB
|
274
|
+
# if the rowid column is encountered, then we can use an incremental
|
275
|
+
# blob api, otherwise we have to use the all at once version.
|
276
|
+
if using_rowid_column? then
|
277
|
+
value = Amalgalite::Blob.new( :db_blob => SQLite3::Blob.new( db.api,
|
278
|
+
col.schema.db,
|
279
|
+
col.schema.table,
|
280
|
+
col.schema.name,
|
281
|
+
@stmt_api.column_int64( @rowid_index ),
|
282
|
+
"r"),
|
283
|
+
:column => col.schema)
|
284
|
+
else
|
285
|
+
value = Amalgalite::Blob.new( :string => @stmt_api.column_blob( idx ), :column => col.schema )
|
286
|
+
end
|
287
|
+
else
|
288
|
+
raise ::Amalgalite::Error, "BUG! : Unknown SQLite column type of #{column_type}"
|
289
|
+
end
|
290
|
+
|
291
|
+
row << db.type_map.result_value_of( col.schema.declared_data_type, value )
|
292
|
+
end
|
293
|
+
row.fields = result_fields
|
294
|
+
when ResultCode::DONE
|
295
|
+
row = nil
|
296
|
+
write_blobs
|
297
|
+
else
|
298
|
+
self.close # must close so that the error message is guaranteed to be pushed into the database handler
|
299
|
+
# and we can can call last_error_message on it
|
300
|
+
msg = "SQLITE ERROR #{rc} (#{Amalgalite::SQLite3::Constants::ResultCode.name_from_value( rc )}) : #{@db.api.last_error_message}"
|
301
|
+
raise Amalgalite::SQLite3::Error, msg
|
302
|
+
end
|
303
|
+
return row
|
304
|
+
end
|
305
|
+
|
306
|
+
##
|
307
|
+
# Return all rows from the statement as one array
|
308
|
+
#
|
309
|
+
def all_rows
|
310
|
+
rows = []
|
311
|
+
while row = next_row
|
312
|
+
rows << row
|
313
|
+
end
|
314
|
+
return rows
|
315
|
+
end
|
316
|
+
|
317
|
+
##
|
318
|
+
# Inspect the statement and gather all the meta information about the
|
319
|
+
# results, include the name of the column result column and the origin
|
320
|
+
# column. The origin column is the original database.table.column the value
|
321
|
+
# comes from.
|
322
|
+
#
|
323
|
+
# The full meta information from teh origin column is also obtained for help
|
324
|
+
# in doing type conversion.
|
325
|
+
#
|
326
|
+
# As iteration over the row meta informatio happens, record if the special
|
327
|
+
# "ROWID", "OID", or "_ROWID_" column is encountered. If that column is
|
328
|
+
# encountered then we make note of it.
|
329
|
+
#
|
330
|
+
def result_meta
|
331
|
+
unless @result_meta
|
332
|
+
meta = []
|
333
|
+
column_count.times do |idx|
|
334
|
+
column_meta = ::OpenStruct.new
|
335
|
+
column_meta.name = @stmt_api.column_name( idx )
|
336
|
+
|
337
|
+
db_name = @stmt_api.column_database_name( idx )
|
338
|
+
tbl_name = @stmt_api.column_table_name( idx )
|
339
|
+
col_name = @stmt_api.column_origin_name( idx )
|
340
|
+
|
341
|
+
column_meta.schema = ::Amalgalite::Column.new( db_name, tbl_name, col_name, idx )
|
342
|
+
column_meta.schema.declared_data_type = @stmt_api.column_declared_type( idx )
|
343
|
+
|
344
|
+
# only check for rowid if we have a table name and it is not the
|
345
|
+
# sqlite_master table. We could get recursion in those cases.
|
346
|
+
if not using_rowid_column? and tbl_name and tbl_name != 'sqlite_master' and is_column_rowid?( tbl_name, col_name ) then
|
347
|
+
@rowid_index = idx
|
348
|
+
end
|
349
|
+
|
350
|
+
meta << column_meta
|
351
|
+
end
|
352
|
+
|
353
|
+
@result_meta = meta
|
354
|
+
end
|
355
|
+
return @result_meta
|
356
|
+
end
|
357
|
+
|
358
|
+
##
|
359
|
+
# is the column indicated by the Column a 'rowid' column
|
360
|
+
#
|
361
|
+
def is_column_rowid?( table_name, column_name )
|
362
|
+
column_schema = @db.schema.tables[table_name].columns[column_name]
|
363
|
+
if column_schema then
|
364
|
+
if column_schema.primary_key? and column_schema.declared_data_type and column_schema.declared_data_type.upcase == "INTEGER" then
|
365
|
+
return true
|
366
|
+
end
|
367
|
+
else
|
368
|
+
return true if Statement.rowid_column_names.include?( column_name.upcase )
|
369
|
+
end
|
370
|
+
return false
|
371
|
+
end
|
372
|
+
|
373
|
+
##
|
374
|
+
# Return the array of field names for the result set, the field names are
|
375
|
+
# all strings
|
376
|
+
#
|
377
|
+
def result_fields
|
378
|
+
@fields ||= result_meta.collect { |m| m.name }
|
379
|
+
end
|
380
|
+
|
381
|
+
##
|
382
|
+
# Return any unsued SQL from the statement
|
383
|
+
#
|
384
|
+
def remaining_sql
|
385
|
+
@stmt_api.remaining_sql
|
386
|
+
end
|
387
|
+
|
388
|
+
|
389
|
+
##
|
390
|
+
# return the number of columns in the result of this query
|
391
|
+
#
|
392
|
+
def column_count
|
393
|
+
@stmt_api.column_count
|
394
|
+
end
|
395
|
+
|
396
|
+
##
|
397
|
+
# return the raw sql that was originally used to prepare the statement
|
398
|
+
#
|
399
|
+
def sql
|
400
|
+
@stmt_api.sql
|
401
|
+
end
|
402
|
+
|
403
|
+
##
|
404
|
+
# Close the statement. The statement is no longer valid for use after it
|
405
|
+
# has been closed.
|
406
|
+
#
|
407
|
+
def close
|
408
|
+
if open? then
|
409
|
+
@stmt_api.close
|
410
|
+
@open = false
|
411
|
+
end
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|