amalgalite 0.4.2-x86-mswin32-60
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +81 -0
- data/LICENSE +29 -0
- data/README +40 -0
- data/bin/amalgalite-pack-into-db +155 -0
- data/examples/a.rb +9 -0
- data/examples/blob.rb +105 -0
- data/examples/bootstrap.rb +36 -0
- data/examples/gem-db.rb +94 -0
- data/examples/requires.rb +54 -0
- data/examples/schema-info.rb +34 -0
- data/ext/amalgalite3.c +201 -0
- data/ext/amalgalite3.h +121 -0
- data/ext/amalgalite3_blob.c +241 -0
- data/ext/amalgalite3_constants.c +221 -0
- data/ext/amalgalite3_database.c +550 -0
- data/ext/amalgalite3_requires_bootstrap.c +210 -0
- data/ext/amalgalite3_statement.c +628 -0
- data/ext/extconf.rb +19 -0
- data/ext/gen_constants.rb +130 -0
- data/ext/rbconfig-mingw.rb +178 -0
- data/ext/sqlite3.c +97092 -0
- data/ext/sqlite3.h +6364 -0
- data/ext/sqlite3_options.h +4 -0
- data/ext/sqlite3ext.h +372 -0
- data/gemspec.rb +55 -0
- data/lib/amalgalite.rb +33 -0
- data/lib/amalgalite/blob.rb +186 -0
- data/lib/amalgalite/boolean.rb +42 -0
- data/lib/amalgalite/column.rb +86 -0
- data/lib/amalgalite/core_ext/kernel/require.rb +14 -0
- data/lib/amalgalite/database.rb +514 -0
- data/lib/amalgalite/index.rb +43 -0
- data/lib/amalgalite/paths.rb +70 -0
- data/lib/amalgalite/profile_tap.rb +130 -0
- data/lib/amalgalite/requires.rb +112 -0
- data/lib/amalgalite/schema.rb +115 -0
- data/lib/amalgalite/sqlite3.rb +6 -0
- data/lib/amalgalite/sqlite3/constants.rb +82 -0
- data/lib/amalgalite/sqlite3/database/status.rb +69 -0
- data/lib/amalgalite/sqlite3/status.rb +61 -0
- data/lib/amalgalite/sqlite3/version.rb +38 -0
- data/lib/amalgalite/statement.rb +394 -0
- data/lib/amalgalite/table.rb +36 -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 +41 -0
- data/lib/amalgalite/type_maps/text_map.rb +23 -0
- data/lib/amalgalite/version.rb +37 -0
- data/lib/amalgalite/view.rb +26 -0
- data/lib/amalgalite3.so +0 -0
- data/spec/amalgalite_spec.rb +4 -0
- data/spec/blob_spec.rb +81 -0
- data/spec/boolean_spec.rb +23 -0
- data/spec/database_spec.rb +238 -0
- data/spec/default_map_spec.rb +87 -0
- data/spec/integeration_spec.rb +111 -0
- data/spec/paths_spec.rb +28 -0
- data/spec/schema_spec.rb +60 -0
- data/spec/spec_helper.rb +25 -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 +23 -0
- data/spec/statement_spec.rb +134 -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 +9 -0
- data/tasks/announce.rake +39 -0
- data/tasks/config.rb +110 -0
- data/tasks/distribution.rake +53 -0
- data/tasks/documentation.rake +33 -0
- data/tasks/extension.rake +100 -0
- data/tasks/rspec.rake +32 -0
- data/tasks/rubyforge.rake +59 -0
- data/tasks/utils.rb +80 -0
- metadata +192 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2008 Jeremy Hinegardner
|
3
|
+
# All rights reserved. See LICENSE and/or COPYING for details.
|
4
|
+
#++
|
5
|
+
|
6
|
+
require 'amalgalite3'
|
7
|
+
module Amalgalite::SQLite3
|
8
|
+
module Constants
|
9
|
+
module Helpers
|
10
|
+
#
|
11
|
+
# convert an integer value into the string representation of the associated
|
12
|
+
# constant. this is a helper method used by some of the other modules
|
13
|
+
#
|
14
|
+
def name_from_value( value )
|
15
|
+
unless @const_map_from_value
|
16
|
+
@const_map_from_value = {}
|
17
|
+
constants.each do |const_name|
|
18
|
+
c_int = const_get( const_name )
|
19
|
+
@const_map_from_value[c_int] = const_name
|
20
|
+
end
|
21
|
+
end
|
22
|
+
return @const_map_from_value[ value ]
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# convert a string into the constant value. This is helper method used by
|
27
|
+
# some of the other modules
|
28
|
+
#
|
29
|
+
def value_from_name( name )
|
30
|
+
unless @const_map_from_name
|
31
|
+
@const_map_from_name = {}
|
32
|
+
constants.each do |const_name|
|
33
|
+
c_int = const_get( const_name )
|
34
|
+
@const_map_from_name[ const_name ] = c_int
|
35
|
+
end
|
36
|
+
end
|
37
|
+
return @const_map_from_name[ name.upcase ]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
##
|
43
|
+
# DataType defines the namespace for all possible SQLite data types.
|
44
|
+
#
|
45
|
+
module DataType
|
46
|
+
end
|
47
|
+
DataType.freeze
|
48
|
+
|
49
|
+
##
|
50
|
+
# Open defines the namespace for all possible flags to the Database.open
|
51
|
+
# method
|
52
|
+
#
|
53
|
+
module Open
|
54
|
+
end
|
55
|
+
Open.freeze
|
56
|
+
|
57
|
+
##
|
58
|
+
# Status defines the namespace for all the possible status flags for
|
59
|
+
# Amalgalite::SQLite3::Status objects
|
60
|
+
#
|
61
|
+
module Status
|
62
|
+
extend Helpers
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
##
|
67
|
+
# DBStatus defines the namespace for all the possible status codes for the
|
68
|
+
# Amalgalite::SQlite3::Database::Status objects.
|
69
|
+
#
|
70
|
+
module DBStatus
|
71
|
+
extend Helpers
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# ResultCode defines the namespace for all possible result codes from an
|
76
|
+
# SQLite API call.
|
77
|
+
#
|
78
|
+
module ResultCode
|
79
|
+
extend Helpers
|
80
|
+
end # end ResultCode
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'amalgalite3'
|
2
|
+
require 'amalgalite/sqlite3/constants'
|
3
|
+
module Amalgalite::SQLite3
|
4
|
+
class Database
|
5
|
+
#
|
6
|
+
# A Stat represents a single Database Status code and its current highwater mark.
|
7
|
+
#
|
8
|
+
# Some stats may not have a current or a highwater value, in those cases
|
9
|
+
# the associated _has_current?_ or _has_highwater?_ method returns false and the
|
10
|
+
# _current_ or _highwater_ method also returns +nil+.
|
11
|
+
#
|
12
|
+
class Stat
|
13
|
+
attr_reader :name
|
14
|
+
attr_reader :code
|
15
|
+
|
16
|
+
def initialize( api_db, name )
|
17
|
+
@name = name
|
18
|
+
@code = ::Amalgalite::SQLite3::Constants::DBStatus.value_from_name( name )
|
19
|
+
@current = nil
|
20
|
+
@highwater = nil
|
21
|
+
@api_db = api_db
|
22
|
+
end
|
23
|
+
|
24
|
+
def current
|
25
|
+
update!
|
26
|
+
return @current
|
27
|
+
end
|
28
|
+
|
29
|
+
def highwater
|
30
|
+
update!
|
31
|
+
return @highwater
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# reset the given stat's highwater mark. This will also populate the
|
36
|
+
# _@current_ and _@highwater_ instance variables
|
37
|
+
#
|
38
|
+
def reset!
|
39
|
+
update!( true )
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# Top level Status object holding all the Stat objects indicating the DBStatus
|
45
|
+
# of the SQLite3 C library.
|
46
|
+
#
|
47
|
+
class DBStatus
|
48
|
+
::Amalgalite::SQLite3::Constants::DBStatus.constants.each do |const_name|
|
49
|
+
method_name = const_name.downcase
|
50
|
+
module_eval( <<-code, __FILE__, __LINE__ )
|
51
|
+
def #{method_name}
|
52
|
+
@#{method_name} ||= Amalgalite::SQLite3::Database::Stat.new( self.api_db, '#{method_name}' )
|
53
|
+
end
|
54
|
+
code
|
55
|
+
end
|
56
|
+
|
57
|
+
attr_reader :api_db
|
58
|
+
|
59
|
+
def initialize( api_db )
|
60
|
+
@api_db = api_db
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# return the DBstatus object for the sqlite database
|
65
|
+
def status
|
66
|
+
@status ||= DBStatus.new( self )
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'amalgalite3'
|
2
|
+
require 'amalgalite/sqlite3/constants'
|
3
|
+
module Amalgalite::SQLite3
|
4
|
+
|
5
|
+
#
|
6
|
+
# A Stat represents a single Status code and its current highwater mark.
|
7
|
+
#
|
8
|
+
# Some stats may not have a current or a highwater value, in those cases
|
9
|
+
# the associated _has_current?_ or _has_highwater?_ method returns false and the
|
10
|
+
# _current_ or _highwater_ method also returns +nil+.
|
11
|
+
#
|
12
|
+
class Stat
|
13
|
+
attr_reader :name
|
14
|
+
attr_reader :code
|
15
|
+
|
16
|
+
def initialize( name )
|
17
|
+
@name = name
|
18
|
+
@code = ::Amalgalite::SQLite3::Constants::Status.value_from_name( name )
|
19
|
+
@current = nil
|
20
|
+
@highwater = nil
|
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 Status
|
44
|
+
# of the SQLite3 C library.
|
45
|
+
#
|
46
|
+
class Status
|
47
|
+
::Amalgalite::SQLite3::Constants::Status.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::Stat.new( '#{method_name}' )
|
52
|
+
end
|
53
|
+
code
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# return the status object for the sqlite database
|
58
|
+
def self.status
|
59
|
+
@status ||= Status.new
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2008 Jeremy Hinegardner
|
3
|
+
# All rights reserved. See LICENSE and/or COPYING for details.
|
4
|
+
#++
|
5
|
+
require 'amalgalite3'
|
6
|
+
module Amalgalite
|
7
|
+
module SQLite3
|
8
|
+
module Version
|
9
|
+
# Sqlite3 version number is equal to
|
10
|
+
# MAJOR * 1_000_000 + MINOR * 1_000 + RELEASE
|
11
|
+
|
12
|
+
# major version number of the SQLite C library
|
13
|
+
MAJOR = (to_i / 1_000_000).freeze
|
14
|
+
|
15
|
+
# minor version number of the SQLite C library
|
16
|
+
MINOR = ((to_i % 1_000_000) / 1_000).freeze
|
17
|
+
|
18
|
+
# release version number of the SQLite C library
|
19
|
+
RELEASE = (to_i % 1_000).freeze
|
20
|
+
|
21
|
+
#
|
22
|
+
# call-seq:
|
23
|
+
# Amalgalite::SQLite3::Version.to_a -> [ MAJOR, MINOR, RELEASE ]
|
24
|
+
#
|
25
|
+
# Return the SQLite C library version number as an array of MAJOR, MINOR,
|
26
|
+
# RELEASE
|
27
|
+
#
|
28
|
+
def self.to_a
|
29
|
+
[ MAJOR, MINOR, RELEASE ]
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
# Version of SQLite that ships with Amalgalite
|
35
|
+
VERSION = Version.to_s.freeze
|
36
|
+
end
|
37
|
+
Version.freeze
|
38
|
+
end
|
@@ -0,0 +1,394 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2008 Jeremy Hinegardner
|
3
|
+
# All rights reserved. See LICENSE and/or COPYING for details.
|
4
|
+
#++
|
5
|
+
#
|
6
|
+
require 'amalgalite3'
|
7
|
+
require 'date'
|
8
|
+
require 'arrayfields'
|
9
|
+
require 'ostruct'
|
10
|
+
|
11
|
+
module Amalgalite
|
12
|
+
class Statement
|
13
|
+
|
14
|
+
include ::Amalgalite::SQLite3::Constants
|
15
|
+
|
16
|
+
attr_reader :db
|
17
|
+
attr_reader :sql
|
18
|
+
attr_reader :api
|
19
|
+
|
20
|
+
class << self
|
21
|
+
# special column names that indicate that indicate the column is a rowid
|
22
|
+
def rowid_column_names
|
23
|
+
@rowid_column_names ||= %w[ ROWID OID _ROWID_ ]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Initialize a new statement on the database.
|
29
|
+
#
|
30
|
+
def initialize( db, sql )
|
31
|
+
@db = db
|
32
|
+
prepare_method = @db.utf16? ? :prepare16 : :prepare
|
33
|
+
@param_positions = {}
|
34
|
+
@stmt_api = @db.api.send( prepare_method, sql )
|
35
|
+
@blobs_to_write = []
|
36
|
+
@rowid_index = nil
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Is the special column "ROWID", "OID", or "_ROWID_" used?
|
41
|
+
#
|
42
|
+
def using_rowid_column?
|
43
|
+
not @rowid_index.nil?
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# reset the Statement back to it state right after the constructor returned,
|
48
|
+
# except if any variables have been bound to parameters, those are still
|
49
|
+
# bound.
|
50
|
+
#
|
51
|
+
def reset!
|
52
|
+
@stmt_api.reset!
|
53
|
+
@param_positions = {}
|
54
|
+
@blobs_to_write.clear
|
55
|
+
@rowid_index = nil
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# reset the Statement back to it state right after the constructor returned,
|
60
|
+
# AND clear all parameter bindings.
|
61
|
+
#
|
62
|
+
def reset_and_clear_bindings!
|
63
|
+
reset!
|
64
|
+
@stmt_api.clear_bindings!
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# reset the statment in preparation for executing it again
|
69
|
+
#
|
70
|
+
def reset_for_next_execute!
|
71
|
+
@stmt_api.reset!
|
72
|
+
@stmt_api.clear_bindings!
|
73
|
+
@blobs_to_write.clear
|
74
|
+
end
|
75
|
+
|
76
|
+
##
|
77
|
+
# Execute the statement with the given parameters
|
78
|
+
#
|
79
|
+
# If a block is given, then yield each returned row to the block. If no
|
80
|
+
# block is given then return all rows from the result. No matter what the
|
81
|
+
# prepared statement should be reset before returning the final time.
|
82
|
+
#
|
83
|
+
def execute( *params )
|
84
|
+
bind( *params )
|
85
|
+
begin
|
86
|
+
if block_given? then
|
87
|
+
while row = next_row
|
88
|
+
yield row
|
89
|
+
end
|
90
|
+
else
|
91
|
+
all_rows
|
92
|
+
end
|
93
|
+
ensure
|
94
|
+
s = $!
|
95
|
+
begin
|
96
|
+
reset_for_next_execute!
|
97
|
+
rescue => e
|
98
|
+
end
|
99
|
+
raise s if s
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
##
|
104
|
+
# Bind parameters to the sql statement.
|
105
|
+
#
|
106
|
+
# Bindings in SQLite can have a number of formats:
|
107
|
+
#
|
108
|
+
# ?
|
109
|
+
# ?num
|
110
|
+
# :var
|
111
|
+
# @var
|
112
|
+
# $var
|
113
|
+
#
|
114
|
+
# Where 'num' is an Integer and 'var'is an alphanumerical variable.
|
115
|
+
# They may exist in the SQL for which this Statement was created.
|
116
|
+
#
|
117
|
+
# Amalgalite binds parameters to these variables in the following manner:
|
118
|
+
#
|
119
|
+
# If bind is passed in an Array, either as +bind( "foo", "bar", "baz")+ or
|
120
|
+
# as bind( ["foo", "bar", "baz"] ) then each of the params is assumed to be
|
121
|
+
# positionally bound to the statement( ?, ?num ).
|
122
|
+
#
|
123
|
+
# If bind is passed a Hash, either as +bind( :foo => 1, :bar => 'sqlite' )+
|
124
|
+
# or as bind( { :foo => 1, 'bar' => 'sqlite' }) then it is assumed that each
|
125
|
+
# parameter should be bound as a named parameter (:var, @var, $var).
|
126
|
+
#
|
127
|
+
# If bind is not passed any parameters, or nil, then nothing happens.
|
128
|
+
#
|
129
|
+
def bind( *params )
|
130
|
+
if params.nil? or params.empty? then
|
131
|
+
check_parameter_count!( 0 )
|
132
|
+
return nil
|
133
|
+
end
|
134
|
+
|
135
|
+
if params.first.instance_of?( Hash ) then
|
136
|
+
bind_named_parameters( params.first )
|
137
|
+
else
|
138
|
+
bind_positional_parameters( params )
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
##
|
143
|
+
# Bind parameters to the statement based upon named parameters
|
144
|
+
#
|
145
|
+
def bind_named_parameters( params )
|
146
|
+
check_parameter_count!( params.size )
|
147
|
+
params.each_pair do | param, value |
|
148
|
+
position = param_position_of( param )
|
149
|
+
if position > 0 then
|
150
|
+
bind_parameter_to( position, value )
|
151
|
+
else
|
152
|
+
raise Amalgalite::Error, "Unable to find parameter '#{param}' in SQL statement [#{sql}]"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
##
|
158
|
+
# Bind parameters to the statements based upon positions.
|
159
|
+
#
|
160
|
+
def bind_positional_parameters( params )
|
161
|
+
check_parameter_count!( params.size )
|
162
|
+
params.each_with_index do |value, index|
|
163
|
+
position = index + 1
|
164
|
+
bind_parameter_to( position, value )
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
##
|
169
|
+
# bind a single parameter to a particular position
|
170
|
+
#
|
171
|
+
def bind_parameter_to( position, value )
|
172
|
+
bind_type = db.type_map.bind_type_of( value )
|
173
|
+
case bind_type
|
174
|
+
when DataType::FLOAT
|
175
|
+
@stmt_api.bind_double( position, value )
|
176
|
+
when DataType::INTEGER
|
177
|
+
@stmt_api.bind_int64( position, value )
|
178
|
+
when DataType::NULL
|
179
|
+
@stmt_api.bind_null( position )
|
180
|
+
when DataType::TEXT
|
181
|
+
@stmt_api.bind_text( position, value.to_s )
|
182
|
+
when DataType::BLOB
|
183
|
+
if value.incremental? then
|
184
|
+
@stmt_api.bind_zeroblob( position, value.length )
|
185
|
+
@blobs_to_write << value
|
186
|
+
else
|
187
|
+
@stmt_api.bind_blob( position, value.source )
|
188
|
+
end
|
189
|
+
else
|
190
|
+
raise ::Amalgalite::Error, "Unknown binding type of #{bind_type} from #{db.type_map.class.name}.bind_type_of"
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
##
|
196
|
+
# Find and cache the binding parameter indexes
|
197
|
+
#
|
198
|
+
def param_position_of( name )
|
199
|
+
ns = name.to_s
|
200
|
+
unless pos = @param_positions[ns]
|
201
|
+
pos = @param_positions[ns] = @stmt_api.parameter_index( ns )
|
202
|
+
end
|
203
|
+
return pos
|
204
|
+
end
|
205
|
+
|
206
|
+
##
|
207
|
+
# Check and make sure that the number of parameters aligns with the number
|
208
|
+
# that sqlite expects
|
209
|
+
#
|
210
|
+
def check_parameter_count!( num )
|
211
|
+
expected = @stmt_api.parameter_count
|
212
|
+
if num != expected then
|
213
|
+
raise Amalgalite::Error, "#{sql} has #{expected} parameters, but #{num} were passed to bind."
|
214
|
+
end
|
215
|
+
return expected
|
216
|
+
end
|
217
|
+
|
218
|
+
##
|
219
|
+
# Write any blobs that have been bound to parameters to the database. This
|
220
|
+
# assumes that the blobs go into the last inserted row
|
221
|
+
#
|
222
|
+
def write_blobs
|
223
|
+
unless @blobs_to_write.empty?
|
224
|
+
@blobs_to_write.each do |blob|
|
225
|
+
blob.write_to_column!
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
##
|
231
|
+
# Iterate over the results of the statement returning each row of results
|
232
|
+
# as a hash by +column_name+. The column names are the value after an
|
233
|
+
# 'AS' in the query or default chosen by sqlite.
|
234
|
+
#
|
235
|
+
def each
|
236
|
+
while row = next_row
|
237
|
+
yield row
|
238
|
+
end
|
239
|
+
return self
|
240
|
+
end
|
241
|
+
|
242
|
+
##
|
243
|
+
# Return the next row of data, with type conversion as indicated by the
|
244
|
+
# Database#type_map
|
245
|
+
#
|
246
|
+
def next_row
|
247
|
+
row = []
|
248
|
+
case rc = @stmt_api.step
|
249
|
+
when ResultCode::ROW
|
250
|
+
result_meta.each_with_index do |col, idx|
|
251
|
+
value = nil
|
252
|
+
column_type = @stmt_api.column_type( idx )
|
253
|
+
case column_type
|
254
|
+
when DataType::TEXT
|
255
|
+
value = @stmt_api.column_text( idx )
|
256
|
+
when DataType::FLOAT
|
257
|
+
value = @stmt_api.column_double( idx )
|
258
|
+
when DataType::INTEGER
|
259
|
+
value = @stmt_api.column_int64( idx )
|
260
|
+
when DataType::NULL
|
261
|
+
value = nil
|
262
|
+
when DataType::BLOB
|
263
|
+
# if the rowid column is encountered, then we can use an incremental
|
264
|
+
# blob api, otherwise we have to use the all at once version.
|
265
|
+
if using_rowid_column? then
|
266
|
+
value = Amalgalite::Blob.new( :db_blob => SQLite3::Blob.new( db.api,
|
267
|
+
col.schema.db,
|
268
|
+
col.schema.table,
|
269
|
+
col.schema.name,
|
270
|
+
@stmt_api.column_int64( @rowid_index ),
|
271
|
+
"r"),
|
272
|
+
:column => col.schema)
|
273
|
+
else
|
274
|
+
value = Amalgalite::Blob.new( :string => @stmt_api.column_blob( idx ), :column => col.schema )
|
275
|
+
end
|
276
|
+
else
|
277
|
+
raise ::Amalgalite::Error, "BUG! : Unknown SQLite column type of #{column_type}"
|
278
|
+
end
|
279
|
+
|
280
|
+
row << db.type_map.result_value_of( col.schema.declared_data_type, value )
|
281
|
+
end
|
282
|
+
row.fields = result_fields
|
283
|
+
when ResultCode::DONE
|
284
|
+
row = nil
|
285
|
+
write_blobs
|
286
|
+
else
|
287
|
+
raise Amalgalite::SQLite3::Error,
|
288
|
+
"SQLITE ERROR #{rc} (#{Amalgalite::SQLite3::Constants::ResultCode.from_int( rc )}) : #{@db.api.last_error_message}"
|
289
|
+
end
|
290
|
+
return row
|
291
|
+
end
|
292
|
+
|
293
|
+
##
|
294
|
+
# Return all rows from the statement as one array
|
295
|
+
#
|
296
|
+
def all_rows
|
297
|
+
rows = []
|
298
|
+
while row = next_row
|
299
|
+
rows << row
|
300
|
+
end
|
301
|
+
return rows
|
302
|
+
end
|
303
|
+
|
304
|
+
##
|
305
|
+
# Inspect the statement and gather all the meta information about the
|
306
|
+
# results, include the name of the column result column and the origin
|
307
|
+
# column. The origin column is the original database.table.column the value
|
308
|
+
# comes from.
|
309
|
+
#
|
310
|
+
# The full meta information from teh origin column is also obtained for help
|
311
|
+
# in doing type conversion.
|
312
|
+
#
|
313
|
+
# As iteration over the row meta informatio happens, record if the special
|
314
|
+
# "ROWID", "OID", or "_ROWID_" column is encountered. If that column is
|
315
|
+
# encountered then we make note of it.
|
316
|
+
#
|
317
|
+
def result_meta
|
318
|
+
unless @result_meta
|
319
|
+
meta = []
|
320
|
+
column_count.times do |idx|
|
321
|
+
column_meta = ::OpenStruct.new
|
322
|
+
column_meta.name = @stmt_api.column_name( idx )
|
323
|
+
|
324
|
+
db_name = @stmt_api.column_database_name( idx )
|
325
|
+
tbl_name = @stmt_api.column_table_name( idx )
|
326
|
+
col_name = @stmt_api.column_origin_name( idx )
|
327
|
+
|
328
|
+
column_meta.schema = ::Amalgalite::Column.new( db_name, tbl_name, col_name )
|
329
|
+
column_meta.schema.declared_data_type = @stmt_api.column_declared_type( idx )
|
330
|
+
|
331
|
+
# only check for rowid if we have a table name and it is not the
|
332
|
+
# sqlite_master table. We could get recursion in those cases.
|
333
|
+
if not using_rowid_column? and tbl_name and tbl_name != 'sqlite_master' and is_column_rowid?( tbl_name, col_name ) then
|
334
|
+
@rowid_index = idx
|
335
|
+
end
|
336
|
+
|
337
|
+
meta << column_meta
|
338
|
+
end
|
339
|
+
|
340
|
+
@result_meta = meta
|
341
|
+
end
|
342
|
+
return @result_meta
|
343
|
+
end
|
344
|
+
|
345
|
+
##
|
346
|
+
# is the column indicated by the Column a 'rowid' column
|
347
|
+
#
|
348
|
+
def is_column_rowid?( table_name, column_name )
|
349
|
+
column_schema = @db.schema.tables[table_name].columns[column_name]
|
350
|
+
if column_schema.primary_key? and column_schema.declared_data_type and column_schema.declared_data_type.upcase == "INTEGER" then
|
351
|
+
return true
|
352
|
+
end
|
353
|
+
return false
|
354
|
+
end
|
355
|
+
|
356
|
+
##
|
357
|
+
# Return the array of field names for the result set, the field names are
|
358
|
+
# all strings
|
359
|
+
#
|
360
|
+
def result_fields
|
361
|
+
@fields ||= result_meta.collect { |m| m.name }
|
362
|
+
end
|
363
|
+
|
364
|
+
##
|
365
|
+
# Return any unsued SQL from the statement
|
366
|
+
#
|
367
|
+
def remaining_sql
|
368
|
+
@stmt_api.remaining_sql
|
369
|
+
end
|
370
|
+
|
371
|
+
|
372
|
+
##
|
373
|
+
# return the number of columns in the result of this query
|
374
|
+
#
|
375
|
+
def column_count
|
376
|
+
@stmt_api.column_count
|
377
|
+
end
|
378
|
+
|
379
|
+
##
|
380
|
+
# return the raw sql that was originally used to prepare the statement
|
381
|
+
#
|
382
|
+
def sql
|
383
|
+
@stmt_api.sql
|
384
|
+
end
|
385
|
+
|
386
|
+
##
|
387
|
+
# Close the statement. The statement is no longer valid for use after it
|
388
|
+
# has been closed.
|
389
|
+
#
|
390
|
+
def close
|
391
|
+
@stmt_api.close
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|