sqlite3 1.5.0-arm64-darwin
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sqlite3 might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/.gemtest +0 -0
- data/API_CHANGES.md +49 -0
- data/CHANGELOG.md +425 -0
- data/CONTRIBUTING.md +24 -0
- data/ChangeLog.cvs +88 -0
- data/Gemfile +3 -0
- data/LICENSE +27 -0
- data/LICENSE-DEPENDENCIES +20 -0
- data/README.md +233 -0
- data/ext/sqlite3/aggregator.c +274 -0
- data/ext/sqlite3/aggregator.h +12 -0
- data/ext/sqlite3/backup.c +168 -0
- data/ext/sqlite3/backup.h +15 -0
- data/ext/sqlite3/database.c +853 -0
- data/ext/sqlite3/database.h +17 -0
- data/ext/sqlite3/exception.c +98 -0
- data/ext/sqlite3/exception.h +8 -0
- data/ext/sqlite3/extconf.rb +252 -0
- data/ext/sqlite3/sqlite3.c +163 -0
- data/ext/sqlite3/sqlite3_ruby.h +48 -0
- data/ext/sqlite3/statement.c +442 -0
- data/ext/sqlite3/statement.h +16 -0
- data/faq/faq.md +431 -0
- data/faq/faq.rb +145 -0
- data/faq/faq.yml +426 -0
- data/lib/sqlite3/2.6/sqlite3_native.bundle +0 -0
- data/lib/sqlite3/2.7/sqlite3_native.bundle +0 -0
- data/lib/sqlite3/3.0/sqlite3_native.bundle +0 -0
- data/lib/sqlite3/3.1/sqlite3_native.bundle +0 -0
- data/lib/sqlite3/constants.rb +50 -0
- data/lib/sqlite3/database.rb +741 -0
- data/lib/sqlite3/errors.rb +35 -0
- data/lib/sqlite3/pragmas.rb +595 -0
- data/lib/sqlite3/resultset.rb +187 -0
- data/lib/sqlite3/statement.rb +145 -0
- data/lib/sqlite3/translator.rb +118 -0
- data/lib/sqlite3/value.rb +57 -0
- data/lib/sqlite3/version.rb +23 -0
- data/lib/sqlite3.rb +15 -0
- data/test/helper.rb +27 -0
- data/test/test_backup.rb +33 -0
- data/test/test_collation.rb +82 -0
- data/test/test_database.rb +545 -0
- data/test/test_database_flags.rb +95 -0
- data/test/test_database_readonly.rb +36 -0
- data/test/test_database_readwrite.rb +41 -0
- data/test/test_deprecated.rb +44 -0
- data/test/test_encoding.rb +155 -0
- data/test/test_integration.rb +507 -0
- data/test/test_integration_aggregate.rb +336 -0
- data/test/test_integration_open_close.rb +30 -0
- data/test/test_integration_pending.rb +115 -0
- data/test/test_integration_resultset.rb +142 -0
- data/test/test_integration_statement.rb +194 -0
- data/test/test_result_set.rb +37 -0
- data/test/test_sqlite3.rb +30 -0
- data/test/test_statement.rb +263 -0
- data/test/test_statement_execute.rb +35 -0
- metadata +190 -0
@@ -0,0 +1,187 @@
|
|
1
|
+
require 'sqlite3/constants'
|
2
|
+
require 'sqlite3/errors'
|
3
|
+
|
4
|
+
module SQLite3
|
5
|
+
|
6
|
+
# The ResultSet object encapsulates the enumerability of a query's output.
|
7
|
+
# It is a simple cursor over the data that the query returns. It will
|
8
|
+
# very rarely (if ever) be instantiated directly. Instead, clients should
|
9
|
+
# obtain a ResultSet instance via Statement#execute.
|
10
|
+
class ResultSet
|
11
|
+
include Enumerable
|
12
|
+
|
13
|
+
class ArrayWithTypes < Array # :nodoc:
|
14
|
+
attr_accessor :types
|
15
|
+
end
|
16
|
+
|
17
|
+
class ArrayWithTypesAndFields < Array # :nodoc:
|
18
|
+
attr_writer :types
|
19
|
+
attr_writer :fields
|
20
|
+
|
21
|
+
def types
|
22
|
+
warn(<<-eowarn) if $VERBOSE
|
23
|
+
#{caller[0]} is calling #{self.class}#types. This method will be removed in
|
24
|
+
sqlite3 version 2.0.0, please call the `types` method on the SQLite3::ResultSet
|
25
|
+
object that created this object
|
26
|
+
eowarn
|
27
|
+
@types
|
28
|
+
end
|
29
|
+
|
30
|
+
def fields
|
31
|
+
warn(<<-eowarn) if $VERBOSE
|
32
|
+
#{caller[0]} is calling #{self.class}#fields. This method will be removed in
|
33
|
+
sqlite3 version 2.0.0, please call the `columns` method on the SQLite3::ResultSet
|
34
|
+
object that created this object
|
35
|
+
eowarn
|
36
|
+
@fields
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# The class of which we return an object in case we want a Hash as
|
41
|
+
# result.
|
42
|
+
class HashWithTypesAndFields < Hash # :nodoc:
|
43
|
+
attr_writer :types
|
44
|
+
attr_writer :fields
|
45
|
+
|
46
|
+
def types
|
47
|
+
warn(<<-eowarn) if $VERBOSE
|
48
|
+
#{caller[0]} is calling #{self.class}#types. This method will be removed in
|
49
|
+
sqlite3 version 2.0.0, please call the `types` method on the SQLite3::ResultSet
|
50
|
+
object that created this object
|
51
|
+
eowarn
|
52
|
+
@types
|
53
|
+
end
|
54
|
+
|
55
|
+
def fields
|
56
|
+
warn(<<-eowarn) if $VERBOSE
|
57
|
+
#{caller[0]} is calling #{self.class}#fields. This method will be removed in
|
58
|
+
sqlite3 version 2.0.0, please call the `columns` method on the SQLite3::ResultSet
|
59
|
+
object that created this object
|
60
|
+
eowarn
|
61
|
+
@fields
|
62
|
+
end
|
63
|
+
|
64
|
+
def [] key
|
65
|
+
key = fields[key] if key.is_a? Numeric
|
66
|
+
super key
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Create a new ResultSet attached to the given database, using the
|
71
|
+
# given sql text.
|
72
|
+
def initialize db, stmt
|
73
|
+
@db = db
|
74
|
+
@stmt = stmt
|
75
|
+
end
|
76
|
+
|
77
|
+
# Reset the cursor, so that a result set which has reached end-of-file
|
78
|
+
# can be rewound and reiterated.
|
79
|
+
def reset( *bind_params )
|
80
|
+
@stmt.reset!
|
81
|
+
@stmt.bind_params( *bind_params )
|
82
|
+
@eof = false
|
83
|
+
end
|
84
|
+
|
85
|
+
# Query whether the cursor has reached the end of the result set or not.
|
86
|
+
def eof?
|
87
|
+
@stmt.done?
|
88
|
+
end
|
89
|
+
|
90
|
+
# Obtain the next row from the cursor. If there are no more rows to be
|
91
|
+
# had, this will return +nil+. If type translation is active on the
|
92
|
+
# corresponding database, the values in the row will be translated
|
93
|
+
# according to their types.
|
94
|
+
#
|
95
|
+
# The returned value will be an array, unless Database#results_as_hash has
|
96
|
+
# been set to +true+, in which case the returned value will be a hash.
|
97
|
+
#
|
98
|
+
# For arrays, the column names are accessible via the +fields+ property,
|
99
|
+
# and the column types are accessible via the +types+ property.
|
100
|
+
#
|
101
|
+
# For hashes, the column names are the keys of the hash, and the column
|
102
|
+
# types are accessible via the +types+ property.
|
103
|
+
def next
|
104
|
+
if @db.results_as_hash
|
105
|
+
return next_hash
|
106
|
+
end
|
107
|
+
|
108
|
+
row = @stmt.step
|
109
|
+
return nil if @stmt.done?
|
110
|
+
|
111
|
+
row = @db.translate_from_db @stmt.types, row
|
112
|
+
|
113
|
+
if row.respond_to?(:fields)
|
114
|
+
# FIXME: this can only happen if the translator returns something
|
115
|
+
# that responds to `fields`. Since we're removing the translator
|
116
|
+
# in 2.0, we can remove this branch in 2.0.
|
117
|
+
row = ArrayWithTypes.new(row)
|
118
|
+
else
|
119
|
+
# FIXME: the `fields` and `types` methods are deprecated on this
|
120
|
+
# object for version 2.0, so we can safely remove this branch
|
121
|
+
# as well.
|
122
|
+
row = ArrayWithTypesAndFields.new(row)
|
123
|
+
end
|
124
|
+
|
125
|
+
row.fields = @stmt.columns
|
126
|
+
row.types = @stmt.types
|
127
|
+
row
|
128
|
+
end
|
129
|
+
|
130
|
+
# Required by the Enumerable mixin. Provides an internal iterator over the
|
131
|
+
# rows of the result set.
|
132
|
+
def each
|
133
|
+
while node = self.next
|
134
|
+
yield node
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Provides an internal iterator over the rows of the result set where
|
139
|
+
# each row is yielded as a hash.
|
140
|
+
def each_hash
|
141
|
+
while node = next_hash
|
142
|
+
yield node
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Closes the statement that spawned this result set.
|
147
|
+
# <em>Use with caution!</em> Closing a result set will automatically
|
148
|
+
# close any other result sets that were spawned from the same statement.
|
149
|
+
def close
|
150
|
+
@stmt.close
|
151
|
+
end
|
152
|
+
|
153
|
+
# Queries whether the underlying statement has been closed or not.
|
154
|
+
def closed?
|
155
|
+
@stmt.closed?
|
156
|
+
end
|
157
|
+
|
158
|
+
# Returns the types of the columns returned by this result set.
|
159
|
+
def types
|
160
|
+
@stmt.types
|
161
|
+
end
|
162
|
+
|
163
|
+
# Returns the names of the columns returned by this result set.
|
164
|
+
def columns
|
165
|
+
@stmt.columns
|
166
|
+
end
|
167
|
+
|
168
|
+
# Return the next row as a hash
|
169
|
+
def next_hash
|
170
|
+
row = @stmt.step
|
171
|
+
return nil if @stmt.done?
|
172
|
+
|
173
|
+
# FIXME: type translation is deprecated, so this can be removed
|
174
|
+
# in 2.0
|
175
|
+
row = @db.translate_from_db @stmt.types, row
|
176
|
+
|
177
|
+
# FIXME: this can be switched to a regular hash in 2.0
|
178
|
+
row = HashWithTypesAndFields[*@stmt.columns.zip(row).flatten]
|
179
|
+
|
180
|
+
# FIXME: these methods are deprecated for version 2.0, so we can remove
|
181
|
+
# this code in 2.0
|
182
|
+
row.fields = @stmt.columns
|
183
|
+
row.types = @stmt.types
|
184
|
+
row
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'sqlite3/errors'
|
2
|
+
require 'sqlite3/resultset'
|
3
|
+
|
4
|
+
class String
|
5
|
+
def to_blob
|
6
|
+
SQLite3::Blob.new( self )
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module SQLite3
|
11
|
+
# A statement represents a prepared-but-unexecuted SQL query. It will rarely
|
12
|
+
# (if ever) be instantiated directly by a client, and is most often obtained
|
13
|
+
# via the Database#prepare method.
|
14
|
+
class Statement
|
15
|
+
include Enumerable
|
16
|
+
|
17
|
+
# This is any text that followed the first valid SQL statement in the text
|
18
|
+
# with which the statement was initialized. If there was no trailing text,
|
19
|
+
# this will be the empty string.
|
20
|
+
attr_reader :remainder
|
21
|
+
|
22
|
+
# Binds the given variables to the corresponding placeholders in the SQL
|
23
|
+
# text.
|
24
|
+
#
|
25
|
+
# See Database#execute for a description of the valid placeholder
|
26
|
+
# syntaxes.
|
27
|
+
#
|
28
|
+
# Example:
|
29
|
+
#
|
30
|
+
# stmt = db.prepare( "select * from table where a=? and b=?" )
|
31
|
+
# stmt.bind_params( 15, "hello" )
|
32
|
+
#
|
33
|
+
# See also #execute, #bind_param, Statement#bind_param, and
|
34
|
+
# Statement#bind_params.
|
35
|
+
def bind_params( *bind_vars )
|
36
|
+
index = 1
|
37
|
+
bind_vars.flatten.each do |var|
|
38
|
+
if Hash === var
|
39
|
+
var.each { |key, val| bind_param key, val }
|
40
|
+
else
|
41
|
+
bind_param index, var
|
42
|
+
index += 1
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Execute the statement. This creates a new ResultSet object for the
|
48
|
+
# statement's virtual machine. If a block was given, the new ResultSet will
|
49
|
+
# be yielded to it; otherwise, the ResultSet will be returned.
|
50
|
+
#
|
51
|
+
# Any parameters will be bound to the statement using #bind_params.
|
52
|
+
#
|
53
|
+
# Example:
|
54
|
+
#
|
55
|
+
# stmt = db.prepare( "select * from table" )
|
56
|
+
# stmt.execute do |result|
|
57
|
+
# ...
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# See also #bind_params, #execute!.
|
61
|
+
def execute( *bind_vars )
|
62
|
+
reset! if active? || done?
|
63
|
+
|
64
|
+
bind_params(*bind_vars) unless bind_vars.empty?
|
65
|
+
@results = ResultSet.new(@connection, self)
|
66
|
+
|
67
|
+
step if 0 == column_count
|
68
|
+
|
69
|
+
yield @results if block_given?
|
70
|
+
@results
|
71
|
+
end
|
72
|
+
|
73
|
+
# Execute the statement. If no block was given, this returns an array of
|
74
|
+
# rows returned by executing the statement. Otherwise, each row will be
|
75
|
+
# yielded to the block.
|
76
|
+
#
|
77
|
+
# Any parameters will be bound to the statement using #bind_params.
|
78
|
+
#
|
79
|
+
# Example:
|
80
|
+
#
|
81
|
+
# stmt = db.prepare( "select * from table" )
|
82
|
+
# stmt.execute! do |row|
|
83
|
+
# ...
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# See also #bind_params, #execute.
|
87
|
+
def execute!( *bind_vars, &block )
|
88
|
+
execute(*bind_vars)
|
89
|
+
block_given? ? each(&block) : to_a
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns true if the statement is currently active, meaning it has an
|
93
|
+
# open result set.
|
94
|
+
def active?
|
95
|
+
!done?
|
96
|
+
end
|
97
|
+
|
98
|
+
# Return an array of the column names for this statement. Note that this
|
99
|
+
# may execute the statement in order to obtain the metadata; this makes it
|
100
|
+
# a (potentially) expensive operation.
|
101
|
+
def columns
|
102
|
+
get_metadata unless @columns
|
103
|
+
return @columns
|
104
|
+
end
|
105
|
+
|
106
|
+
def each
|
107
|
+
loop do
|
108
|
+
val = step
|
109
|
+
break self if done?
|
110
|
+
yield val
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Return an array of the data types for each column in this statement. Note
|
115
|
+
# that this may execute the statement in order to obtain the metadata; this
|
116
|
+
# makes it a (potentially) expensive operation.
|
117
|
+
def types
|
118
|
+
must_be_open!
|
119
|
+
get_metadata unless @types
|
120
|
+
@types
|
121
|
+
end
|
122
|
+
|
123
|
+
# Performs a sanity check to ensure that the statement is not
|
124
|
+
# closed. If it is, an exception is raised.
|
125
|
+
def must_be_open! # :nodoc:
|
126
|
+
if closed?
|
127
|
+
raise SQLite3::Exception, "cannot use a closed statement"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
# A convenience method for obtaining the metadata about the query. Note
|
133
|
+
# that this will actually execute the SQL, which means it can be a
|
134
|
+
# (potentially) expensive operation.
|
135
|
+
def get_metadata
|
136
|
+
@columns = Array.new(column_count) do |column|
|
137
|
+
column_name column
|
138
|
+
end
|
139
|
+
@types = Array.new(column_count) do |column|
|
140
|
+
val = column_decltype(column)
|
141
|
+
val.nil? ? nil : val.downcase
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
module SQLite3
|
5
|
+
|
6
|
+
# The Translator class encapsulates the logic and callbacks necessary for
|
7
|
+
# converting string data to a value of some specified type. Every Database
|
8
|
+
# instance may have a Translator instance, in order to assist in type
|
9
|
+
# translation (Database#type_translation).
|
10
|
+
#
|
11
|
+
# Further, applications may define their own custom type translation logic
|
12
|
+
# by registering translator blocks with the corresponding database's
|
13
|
+
# translator instance (Database#translator).
|
14
|
+
class Translator
|
15
|
+
|
16
|
+
# Create a new Translator instance. It will be preinitialized with default
|
17
|
+
# translators for most SQL data types.
|
18
|
+
def initialize
|
19
|
+
@translators = Hash.new( proc { |type,value| value } )
|
20
|
+
@type_name_cache = {}
|
21
|
+
register_default_translators
|
22
|
+
end
|
23
|
+
|
24
|
+
# Add a new translator block, which will be invoked to process type
|
25
|
+
# translations to the given type. The type should be an SQL datatype, and
|
26
|
+
# may include parentheses (i.e., "VARCHAR(30)"). However, any parenthetical
|
27
|
+
# information is stripped off and discarded, so type translation decisions
|
28
|
+
# are made solely on the "base" type name.
|
29
|
+
#
|
30
|
+
# The translator block itself should accept two parameters, "type" and
|
31
|
+
# "value". In this case, the "type" is the full type name (including
|
32
|
+
# parentheses), so the block itself may include logic for changing how a
|
33
|
+
# type is translated based on the additional data. The "value" parameter
|
34
|
+
# is the (string) data to convert.
|
35
|
+
#
|
36
|
+
# The block should return the translated value.
|
37
|
+
def add_translator( type, &block ) # :yields: type, value
|
38
|
+
warn(<<-eowarn) if $VERBOSE
|
39
|
+
#{caller[0]} is calling `add_translator`.
|
40
|
+
Built in translators are deprecated and will be removed in version 2.0.0
|
41
|
+
eowarn
|
42
|
+
@translators[ type_name( type ) ] = block
|
43
|
+
end
|
44
|
+
|
45
|
+
# Translate the given string value to a value of the given type. In the
|
46
|
+
# absence of an installed translator block for the given type, the value
|
47
|
+
# itself is always returned. Further, +nil+ values are never translated,
|
48
|
+
# and are always passed straight through regardless of the type parameter.
|
49
|
+
def translate( type, value )
|
50
|
+
unless value.nil?
|
51
|
+
# FIXME: this is a hack to support Sequel
|
52
|
+
if type && %w{ datetime timestamp }.include?(type.downcase)
|
53
|
+
@translators[ type_name( type ) ].call( type, value.to_s )
|
54
|
+
else
|
55
|
+
@translators[ type_name( type ) ].call( type, value )
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# A convenience method for working with type names. This returns the "base"
|
61
|
+
# type name, without any parenthetical data.
|
62
|
+
def type_name( type )
|
63
|
+
@type_name_cache[type] ||= begin
|
64
|
+
type = "" if type.nil?
|
65
|
+
type = $1 if type =~ /^(.*?)\(/
|
66
|
+
type.upcase
|
67
|
+
end
|
68
|
+
end
|
69
|
+
private :type_name
|
70
|
+
|
71
|
+
# Register the default translators for the current Translator instance.
|
72
|
+
# This includes translators for most major SQL data types.
|
73
|
+
def register_default_translators
|
74
|
+
[ "time",
|
75
|
+
"timestamp" ].each { |type| add_translator( type ) { |t, v| Time.parse( v ) } }
|
76
|
+
|
77
|
+
add_translator( "date" ) { |t,v| Date.parse(v) }
|
78
|
+
add_translator( "datetime" ) { |t,v| DateTime.parse(v) }
|
79
|
+
|
80
|
+
[ "decimal",
|
81
|
+
"float",
|
82
|
+
"numeric",
|
83
|
+
"double",
|
84
|
+
"real",
|
85
|
+
"dec",
|
86
|
+
"fixed" ].each { |type| add_translator( type ) { |t,v| v.to_f } }
|
87
|
+
|
88
|
+
[ "integer",
|
89
|
+
"smallint",
|
90
|
+
"mediumint",
|
91
|
+
"int",
|
92
|
+
"bigint" ].each { |type| add_translator( type ) { |t,v| v.to_i } }
|
93
|
+
|
94
|
+
[ "bit",
|
95
|
+
"bool",
|
96
|
+
"boolean" ].each do |type|
|
97
|
+
add_translator( type ) do |t,v|
|
98
|
+
!( v.strip.gsub(/00+/,"0") == "0" ||
|
99
|
+
v.downcase == "false" ||
|
100
|
+
v.downcase == "f" ||
|
101
|
+
v.downcase == "no" ||
|
102
|
+
v.downcase == "n" )
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
add_translator( "tinyint" ) do |type, value|
|
107
|
+
if type =~ /\(\s*1\s*\)/
|
108
|
+
value.to_i == 1
|
109
|
+
else
|
110
|
+
value.to_i
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
private :register_default_translators
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'sqlite3/constants'
|
2
|
+
|
3
|
+
module SQLite3
|
4
|
+
|
5
|
+
class Value
|
6
|
+
attr_reader :handle
|
7
|
+
|
8
|
+
def initialize( db, handle )
|
9
|
+
@driver = db.driver
|
10
|
+
@handle = handle
|
11
|
+
end
|
12
|
+
|
13
|
+
def null?
|
14
|
+
type == :null
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_blob
|
18
|
+
@driver.value_blob( @handle )
|
19
|
+
end
|
20
|
+
|
21
|
+
def length( utf16=false )
|
22
|
+
if utf16
|
23
|
+
@driver.value_bytes16( @handle )
|
24
|
+
else
|
25
|
+
@driver.value_bytes( @handle )
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_f
|
30
|
+
@driver.value_double( @handle )
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_i
|
34
|
+
@driver.value_int( @handle )
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_int64
|
38
|
+
@driver.value_int64( @handle )
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s( utf16=false )
|
42
|
+
@driver.value_text( @handle, utf16 )
|
43
|
+
end
|
44
|
+
|
45
|
+
def type
|
46
|
+
case @driver.value_type( @handle )
|
47
|
+
when Constants::ColumnType::INTEGER then :int
|
48
|
+
when Constants::ColumnType::FLOAT then :float
|
49
|
+
when Constants::ColumnType::TEXT then :text
|
50
|
+
when Constants::ColumnType::BLOB then :blob
|
51
|
+
when Constants::ColumnType::NULL then :null
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module SQLite3
|
2
|
+
|
3
|
+
VERSION = "1.5.0"
|
4
|
+
|
5
|
+
module VersionProxy
|
6
|
+
MAJOR = 1
|
7
|
+
MINOR = 5
|
8
|
+
TINY = 0
|
9
|
+
BUILD = nil
|
10
|
+
|
11
|
+
STRING = [ MAJOR, MINOR, TINY, BUILD ].compact.join( "." )
|
12
|
+
|
13
|
+
VERSION = ::SQLite3::VERSION
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.const_missing(name)
|
17
|
+
return super unless name == :Version
|
18
|
+
warn(<<-eowarn) if $VERBOSE
|
19
|
+
#{caller[0]}: SQLite::Version will be removed in sqlite3-ruby version 2.0.0
|
20
|
+
eowarn
|
21
|
+
VersionProxy
|
22
|
+
end
|
23
|
+
end
|
data/lib/sqlite3.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# support multiple ruby version (fat binaries under windows)
|
2
|
+
begin
|
3
|
+
RUBY_VERSION =~ /(\d+\.\d+)/
|
4
|
+
require "sqlite3/#{$1}/sqlite3_native"
|
5
|
+
rescue LoadError
|
6
|
+
require 'sqlite3/sqlite3_native'
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'sqlite3/database'
|
10
|
+
require 'sqlite3/version'
|
11
|
+
|
12
|
+
module SQLite3
|
13
|
+
# Was sqlite3 compiled with thread safety on?
|
14
|
+
def self.threadsafe?; threadsafe > 0; end
|
15
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'sqlite3'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
|
4
|
+
if ENV['GITHUB_ACTIONS'] == 'true' || ENV['CI']
|
5
|
+
$VERBOSE = nil
|
6
|
+
end
|
7
|
+
|
8
|
+
puts "info: sqlite3-ruby version: #{SQLite3::VERSION}/#{SQLite3::VersionProxy::STRING}"
|
9
|
+
puts "info: sqlite3 version: #{SQLite3::SQLITE_VERSION}/#{SQLite3::SQLITE_LOADED_VERSION}"
|
10
|
+
puts "info: sqlcipher?: #{SQLite3.sqlcipher?}"
|
11
|
+
puts "info: threadsafe?: #{SQLite3.threadsafe?}"
|
12
|
+
|
13
|
+
unless RUBY_VERSION >= "1.9"
|
14
|
+
require 'iconv'
|
15
|
+
end
|
16
|
+
|
17
|
+
module SQLite3
|
18
|
+
class TestCase < Minitest::Test
|
19
|
+
alias :assert_not_equal :refute_equal
|
20
|
+
alias :assert_not_nil :refute_nil
|
21
|
+
alias :assert_raise :assert_raises
|
22
|
+
|
23
|
+
def assert_nothing_raised
|
24
|
+
yield
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/test/test_backup.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module SQLite3
|
4
|
+
class TestBackup < SQLite3::TestCase
|
5
|
+
def setup
|
6
|
+
@sdb = SQLite3::Database.new(':memory:')
|
7
|
+
@ddb = SQLite3::Database.new(':memory:')
|
8
|
+
@sdb.execute('CREATE TABLE foo (idx, val);');
|
9
|
+
@data = ('A'..'Z').map{|x|x * 40}
|
10
|
+
@data.each_with_index do |v, i|
|
11
|
+
@sdb.execute('INSERT INTO foo (idx, val) VALUES (?, ?);', [i, v])
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_backup_step
|
16
|
+
b = SQLite3::Backup.new(@ddb, 'main', @sdb, 'main')
|
17
|
+
while b.step(1) == SQLite3::Constants::ErrorCode::OK
|
18
|
+
assert_not_equal(0, b.remaining)
|
19
|
+
end
|
20
|
+
assert_equal(0, b.remaining)
|
21
|
+
b.finish
|
22
|
+
assert_equal(@data.length, @ddb.execute('SELECT * FROM foo;').length)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_backup_all
|
26
|
+
b = SQLite3::Backup.new(@ddb, 'main', @sdb, 'main')
|
27
|
+
assert_equal(SQLite3::Constants::ErrorCode::DONE, b.step(-1))
|
28
|
+
assert_equal(0, b.remaining)
|
29
|
+
b.finish
|
30
|
+
assert_equal(@data.length, @ddb.execute('SELECT * FROM foo;').length)
|
31
|
+
end
|
32
|
+
end if defined?(SQLite3::Backup)
|
33
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'helper'
|
4
|
+
|
5
|
+
module SQLite3
|
6
|
+
class TestCollation < SQLite3::TestCase
|
7
|
+
class Comparator
|
8
|
+
attr_reader :calls
|
9
|
+
def initialize
|
10
|
+
@calls = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def compare left, right
|
14
|
+
@calls << [left, right]
|
15
|
+
left <=> right
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def setup
|
20
|
+
@db = SQLite3::Database.new(':memory:')
|
21
|
+
@create = "create table ex(id int, data string)"
|
22
|
+
@db.execute(@create);
|
23
|
+
[ [1, 'hello'], [2, 'world'] ].each do |vals|
|
24
|
+
@db.execute('insert into ex (id, data) VALUES (?, ?)', vals)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_custom_collation
|
29
|
+
comparator = Comparator.new
|
30
|
+
|
31
|
+
@db.collation 'foo', comparator
|
32
|
+
|
33
|
+
assert_equal comparator, @db.collations['foo']
|
34
|
+
@db.execute('select data from ex order by 1 collate foo')
|
35
|
+
assert_equal 1, comparator.calls.length
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_remove_collation
|
39
|
+
comparator = Comparator.new
|
40
|
+
|
41
|
+
@db.collation 'foo', comparator
|
42
|
+
@db.collation 'foo', nil
|
43
|
+
|
44
|
+
assert_nil @db.collations['foo']
|
45
|
+
assert_raises(SQLite3::SQLException) do
|
46
|
+
@db.execute('select data from ex order by 1 collate foo')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
if RUBY_VERSION >= '1.9.1'
|
51
|
+
def test_encoding
|
52
|
+
comparator = Comparator.new
|
53
|
+
@db.collation 'foo', comparator
|
54
|
+
@db.execute('select data from ex order by 1 collate foo')
|
55
|
+
|
56
|
+
a, b = *comparator.calls.first
|
57
|
+
|
58
|
+
assert_equal Encoding.find('UTF-8'), a.encoding
|
59
|
+
assert_equal Encoding.find('UTF-8'), b.encoding
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_encoding_default_internal
|
63
|
+
warn_before = $-w
|
64
|
+
$-w = false
|
65
|
+
before_enc = Encoding.default_internal
|
66
|
+
|
67
|
+
Encoding.default_internal = 'EUC-JP'
|
68
|
+
comparator = Comparator.new
|
69
|
+
@db.collation 'foo', comparator
|
70
|
+
@db.execute('select data from ex order by 1 collate foo')
|
71
|
+
|
72
|
+
a, b = *comparator.calls.first
|
73
|
+
|
74
|
+
assert_equal Encoding.find('EUC-JP'), a.encoding
|
75
|
+
assert_equal Encoding.find('EUC-JP'), b.encoding
|
76
|
+
ensure
|
77
|
+
Encoding.default_internal = before_enc
|
78
|
+
$-w = warn_before
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|