activerecord-jdbc-adapter 1.3.25 → 5.0.pre1
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.
- checksums.yaml +4 -4
- data/.travis.yml +57 -72
- data/Appraisals +7 -2
- data/Gemfile +3 -7
- data/History.md +0 -50
- data/RUNNING_TESTS.md +4 -0
- data/activerecord-jdbc-adapter.gemspec +2 -1
- data/lib/arjdbc/common_jdbc_methods.rb +89 -0
- data/lib/arjdbc/db2/adapter.rb +58 -69
- data/lib/arjdbc/db2/as400.rb +2 -13
- data/lib/arjdbc/db2/column.rb +1 -1
- data/lib/arjdbc/derby/adapter.rb +2 -6
- data/lib/arjdbc/firebird/adapter.rb +7 -16
- data/lib/arjdbc/h2/adapter.rb +4 -13
- data/lib/arjdbc/hsqldb/adapter.rb +5 -5
- data/lib/arjdbc/jdbc/adapter.rb +15 -76
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/adapter_require.rb +12 -31
- data/lib/arjdbc/jdbc/base_ext.rb +6 -25
- data/lib/arjdbc/jdbc/column.rb +15 -1
- data/lib/arjdbc/jdbc/connection_methods.rb +7 -1
- data/lib/arjdbc/jdbc/type_cast.rb +16 -4
- data/lib/arjdbc/jdbc/type_converter.rb +0 -1
- data/lib/arjdbc/mssql/adapter.rb +9 -21
- data/lib/arjdbc/mysql/adapter.rb +14 -19
- data/lib/arjdbc/mysql/connection_methods.rb +3 -5
- data/lib/arjdbc/oracle/adapter.rb +4 -38
- data/lib/arjdbc/oracle/connection_methods.rb +0 -4
- data/lib/arjdbc/postgresql/adapter.rb +18 -22
- data/lib/arjdbc/postgresql/connection_methods.rb +2 -5
- data/lib/arjdbc/postgresql/oid/bytea.rb +0 -1
- data/lib/arjdbc/postgresql/oid_types.rb +6 -6
- data/lib/arjdbc/sqlite3/adapter.rb +493 -404
- data/lib/arjdbc/tasks/database_tasks.rb +1 -1
- data/lib/arjdbc/tasks/databases3.rake +1 -1
- data/lib/arjdbc/tasks/databases4.rake +3 -8
- data/lib/arjdbc/version.rb +1 -1
- data/rakelib/db.rake +5 -8
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +102 -37
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +0 -7
- metadata +10 -17
- data/lib/arjdbc/jdbc/arel_support.rb +0 -133
- data/lib/arjdbc/mssql/attributes_for_update.rb +0 -22
- data/lib/arjdbc/sqlite3/explain_support.rb +0 -29
@@ -164,11 +164,11 @@ module ArJdbc
|
|
164
164
|
end unless AR42
|
165
165
|
|
166
166
|
def initialize_type_map(m)
|
167
|
-
register_class_with_limit m, 'int2',
|
168
|
-
register_class_with_limit m, 'int4',
|
169
|
-
register_class_with_limit m, 'int8',
|
167
|
+
register_class_with_limit m, 'int2', Type::Integer
|
168
|
+
register_class_with_limit m, 'int4', Type::Integer
|
169
|
+
register_class_with_limit m, 'int8', Type::Integer
|
170
170
|
m.alias_type 'oid', 'int2'
|
171
|
-
m.register_type 'float4',
|
171
|
+
m.register_type 'float4', Type::Float.new
|
172
172
|
m.alias_type 'float8', 'float4'
|
173
173
|
m.register_type 'text', Type::Text.new
|
174
174
|
register_class_with_limit m, 'varchar', Type::String
|
@@ -179,8 +179,8 @@ module ArJdbc
|
|
179
179
|
register_class_with_limit m, 'bit', OID::Bit
|
180
180
|
register_class_with_limit m, 'varbit', OID::BitVarying
|
181
181
|
m.alias_type 'timestamptz', 'timestamp'
|
182
|
-
m.register_type 'date',
|
183
|
-
m.register_type 'time',
|
182
|
+
m.register_type 'date', Type::Date.new
|
183
|
+
m.register_type 'time', Type::Time.new
|
184
184
|
|
185
185
|
m.register_type 'money', OID::Money.new
|
186
186
|
m.register_type 'bytea', OID::Bytea.new
|
@@ -1,433 +1,346 @@
|
|
1
1
|
ArJdbc.load_java_part :SQLite3
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "arjdbc/common_jdbc_methods"
|
4
|
+
require "active_record/connection_adapters/statement_pool"
|
5
|
+
require "active_record/connection_adapters/abstract/database_statements"
|
6
|
+
require "active_record/connection_adapters/sqlite3/explain_pretty_printer"
|
7
|
+
require "active_record/connection_adapters/sqlite3/quoting"
|
8
|
+
require "active_record/connection_adapters/sqlite3/schema_creation"
|
5
9
|
|
6
10
|
module ArJdbc
|
11
|
+
# All the code in this module is a copy of ConnectionAdapters::SQLite3Adapter from active_record 5.
|
12
|
+
# The constants at the front of this file are to allow the rest of the file to remain with no modifications
|
13
|
+
# from its original source.
|
7
14
|
module SQLite3
|
8
|
-
|
15
|
+
# DIFFERENCE: Some common constant names to reduce differences in rest of this module from AR5 version
|
16
|
+
ConnectionAdapters = ::ActiveRecord::ConnectionAdapters
|
17
|
+
IndexDefinition = ::ActiveRecord::ConnectionAdapters::IndexDefinition
|
18
|
+
Quoting = ::ActiveRecord::ConnectionAdapters::SQLite3::Quoting
|
19
|
+
RecordNotUnique = ::ActiveRecord::RecordNotUnique
|
20
|
+
SchemaCreation = ConnectionAdapters::SQLite3::SchemaCreation
|
21
|
+
SQLite3Adapter = ConnectionAdapters::AbstractAdapter
|
9
22
|
|
10
|
-
|
11
|
-
def self.jdbc_connection_class
|
12
|
-
::ActiveRecord::ConnectionAdapters::SQLite3JdbcConnection
|
13
|
-
end
|
14
|
-
|
15
|
-
def jdbc_column_class; ::ActiveRecord::ConnectionAdapters::SQLite3Column end
|
16
|
-
|
17
|
-
# @see ActiveRecord::ConnectionAdapters::JdbcColumn#column_types
|
18
|
-
def self.column_selector
|
19
|
-
[ /sqlite/i, lambda { |config, column| column.extend(Column) } ]
|
20
|
-
end
|
21
|
-
|
22
|
-
# @see ActiveRecord::ConnectionAdapters::JdbcColumn
|
23
|
-
module Column
|
24
|
-
|
25
|
-
# @override {ActiveRecord::ConnectionAdapters::JdbcColumn#init_column}
|
26
|
-
def init_column(name, default, *args)
|
27
|
-
if default =~ /NULL/
|
28
|
-
@default = nil
|
29
|
-
else
|
30
|
-
super
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
# @override {ActiveRecord::ConnectionAdapters::JdbcColumn#default_value}
|
35
|
-
def default_value(value)
|
36
|
-
# JDBC returns column default strings with actual single quotes :
|
37
|
-
return $1 if value =~ /^'(.*)'$/
|
23
|
+
ADAPTER_NAME = 'SQLite'.freeze
|
38
24
|
|
39
|
-
|
40
|
-
end
|
25
|
+
include Quoting
|
41
26
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
27
|
+
NATIVE_DATABASE_TYPES = {
|
28
|
+
primary_key: "INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL",
|
29
|
+
string: { name: "varchar" },
|
30
|
+
text: { name: "text" },
|
31
|
+
integer: { name: "integer" },
|
32
|
+
float: { name: "float" },
|
33
|
+
decimal: { name: "decimal" },
|
34
|
+
datetime: { name: "datetime" },
|
35
|
+
time: { name: "time" },
|
36
|
+
date: { name: "date" },
|
37
|
+
binary: { name: "blob" },
|
38
|
+
boolean: { name: "boolean" }
|
39
|
+
}
|
55
40
|
|
41
|
+
class StatementPool < ConnectionAdapters::StatementPool
|
56
42
|
private
|
57
43
|
|
58
|
-
|
59
|
-
|
60
|
-
case field_type
|
61
|
-
when /boolean/i then :boolean
|
62
|
-
when /text/i then :text
|
63
|
-
when /varchar/i then :string
|
64
|
-
when /int/i then :integer
|
65
|
-
when /float/i then :float
|
66
|
-
when /real|decimal/i then
|
67
|
-
extract_scale(field_type) == 0 ? :integer : :decimal
|
68
|
-
when /datetime/i then :datetime
|
69
|
-
when /date/i then :date
|
70
|
-
when /time/i then :time
|
71
|
-
when /blob/i then :binary
|
72
|
-
else super
|
73
|
-
end
|
44
|
+
def dealloc(stmt)
|
45
|
+
stmt[:stmt].close unless stmt[:stmt].closed?
|
74
46
|
end
|
75
|
-
|
76
|
-
# @override {ActiveRecord::ConnectionAdapters::Column#extract_limit}
|
77
|
-
def extract_limit(sql_type)
|
78
|
-
return nil if sql_type =~ /^(real)\(\d+/i
|
79
|
-
super
|
80
|
-
end
|
81
|
-
|
82
|
-
def extract_precision(sql_type)
|
83
|
-
case sql_type
|
84
|
-
when /^(real)\((\d+)(,\d+)?\)/i then $2.to_i
|
85
|
-
else super
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def extract_scale(sql_type)
|
90
|
-
case sql_type
|
91
|
-
when /^(real)\((\d+)\)/i then 0
|
92
|
-
when /^(real)\((\d+)(,(\d+))\)/i then $4.to_i
|
93
|
-
else super
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
47
|
end
|
98
48
|
|
99
|
-
#
|
100
|
-
|
101
|
-
::Arel::Visitors::SQLite
|
49
|
+
def schema_creation # :nodoc:
|
50
|
+
SQLite3::SchemaCreation.new self
|
102
51
|
end
|
103
52
|
|
104
|
-
|
105
|
-
|
106
|
-
class BindSubstitution < ::Arel::Visitors::SQLite
|
107
|
-
include ::Arel::Visitors::BindVisitor
|
108
|
-
end if defined? ::Arel::Visitors::BindVisitor
|
109
|
-
|
110
|
-
ADAPTER_NAME = 'SQLite'.freeze
|
111
|
-
|
112
|
-
def adapter_name
|
113
|
-
ADAPTER_NAME
|
53
|
+
def arel_visitor # :nodoc:
|
54
|
+
Arel::Visitors::SQLite.new(self)
|
114
55
|
end
|
115
56
|
|
116
|
-
|
117
|
-
|
118
|
-
:string => { :name => "varchar", :limit => 255 },
|
119
|
-
:text => { :name => "text" },
|
120
|
-
:integer => { :name => "integer" },
|
121
|
-
:float => { :name => "float" },
|
122
|
-
# :real => { :name=>"real" },
|
123
|
-
:decimal => { :name => "decimal" },
|
124
|
-
:datetime => { :name => "datetime" },
|
125
|
-
:timestamp => { :name => "datetime" },
|
126
|
-
:time => { :name => "time" },
|
127
|
-
:date => { :name => "date" },
|
128
|
-
:binary => { :name => "blob" },
|
129
|
-
:boolean => { :name => "boolean" }
|
130
|
-
}
|
131
|
-
NATIVE_DATABASE_TYPES.update(
|
132
|
-
:string => { :name => "varchar" }
|
133
|
-
) if AR42
|
57
|
+
def initialize(connection, logger, connection_options, config)
|
58
|
+
super(connection, logger, config)
|
134
59
|
|
135
|
-
|
136
|
-
|
137
|
-
types = NATIVE_DATABASE_TYPES.dup
|
138
|
-
types[:primary_key] = default_primary_key_type
|
139
|
-
types
|
60
|
+
@active = nil
|
61
|
+
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
|
140
62
|
end
|
141
63
|
|
142
|
-
def default_primary_key_type
|
143
|
-
if supports_autoincrement?
|
144
|
-
'integer PRIMARY KEY AUTOINCREMENT NOT NULL'
|
145
|
-
else
|
146
|
-
'integer PRIMARY KEY NOT NULL'
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
# @override
|
151
64
|
def supports_ddl_transactions?
|
152
65
|
true
|
153
66
|
end
|
154
67
|
|
155
|
-
# @override
|
156
68
|
def supports_savepoints?
|
157
|
-
|
69
|
+
true
|
158
70
|
end
|
159
71
|
|
160
|
-
# @override
|
161
72
|
def supports_partial_index?
|
162
|
-
sqlite_version >=
|
73
|
+
sqlite_version >= "3.8.0"
|
163
74
|
end
|
164
75
|
|
165
|
-
#
|
166
|
-
|
76
|
+
# Returns true, since this connection adapter supports prepared statement
|
77
|
+
# caching.
|
78
|
+
def supports_statement_cache?
|
167
79
|
true
|
168
80
|
end
|
169
81
|
|
170
|
-
#
|
171
|
-
def
|
82
|
+
# Returns true, since this connection adapter supports migrations.
|
83
|
+
def supports_migrations? #:nodoc:
|
172
84
|
true
|
173
85
|
end
|
174
86
|
|
175
|
-
|
176
|
-
def supports_autoincrement?
|
87
|
+
def supports_primary_key? #:nodoc:
|
177
88
|
true
|
178
89
|
end
|
179
90
|
|
180
|
-
|
181
|
-
def supports_migrations?
|
91
|
+
def requires_reloading?
|
182
92
|
true
|
183
93
|
end
|
184
94
|
|
185
|
-
|
186
|
-
def supports_primary_key?
|
95
|
+
def supports_views?
|
187
96
|
true
|
188
97
|
end
|
189
98
|
|
190
|
-
|
191
|
-
def supports_add_column?
|
99
|
+
def supports_datetime_with_precision?
|
192
100
|
true
|
193
101
|
end
|
194
102
|
|
195
|
-
|
196
|
-
|
197
|
-
true
|
103
|
+
def supports_multi_insert?
|
104
|
+
sqlite_version >= "3.7.11"
|
198
105
|
end
|
199
106
|
|
200
|
-
|
201
|
-
|
202
|
-
|
107
|
+
def active?
|
108
|
+
@active != false
|
109
|
+
end
|
110
|
+
|
111
|
+
# Disconnects from the database if already connected. Otherwise, this
|
112
|
+
# method does nothing.
|
113
|
+
def disconnect!
|
114
|
+
super
|
115
|
+
@active = false
|
116
|
+
@connection.close rescue nil
|
117
|
+
end
|
118
|
+
|
119
|
+
# Clears the prepared statements cache.
|
120
|
+
def clear_cache!
|
121
|
+
@statements.clear
|
203
122
|
end
|
204
123
|
|
205
|
-
# @override
|
206
124
|
def supports_index_sort_order?
|
207
125
|
true
|
208
126
|
end
|
209
127
|
|
210
|
-
|
211
|
-
def supports_views?
|
128
|
+
def valid_type?(type)
|
212
129
|
true
|
213
130
|
end
|
214
131
|
|
215
|
-
|
216
|
-
|
132
|
+
# Returns 62. SQLite supports index names up to 64
|
133
|
+
# characters. The rest is used by Rails internally to perform
|
134
|
+
# temporary rename operations
|
135
|
+
def allowed_index_name_length
|
136
|
+
index_name_length - 2
|
217
137
|
end
|
218
|
-
private :sqlite_version
|
219
138
|
|
220
|
-
|
221
|
-
|
222
|
-
|
139
|
+
def native_database_types #:nodoc:
|
140
|
+
NATIVE_DATABASE_TYPES
|
141
|
+
end
|
223
142
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
else
|
232
|
-
super
|
233
|
-
end
|
143
|
+
# Returns the current database encoding format as a string, eg: 'UTF-8'
|
144
|
+
def encoding
|
145
|
+
@connection.encoding.to_s
|
146
|
+
end
|
147
|
+
|
148
|
+
def supports_explain?
|
149
|
+
true
|
234
150
|
end
|
235
151
|
|
236
|
-
|
237
|
-
|
238
|
-
|
152
|
+
#--
|
153
|
+
# DATABASE STATEMENTS ======================================
|
154
|
+
#++
|
239
155
|
|
240
|
-
|
241
|
-
|
242
|
-
|
156
|
+
def explain(arel, binds = [])
|
157
|
+
sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
|
158
|
+
::ActiveRecord::ConnectionAdapters::SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", []))
|
243
159
|
end
|
244
160
|
|
245
|
-
|
246
|
-
|
247
|
-
# @override
|
248
|
-
def quoted_date(value)
|
249
|
-
if value.acts_like?(:time) && value.respond_to?(:usec)
|
250
|
-
"#{super}.#{sprintf("%06d", value.usec)}"
|
251
|
-
else
|
252
|
-
super
|
253
|
-
end
|
254
|
-
end if ::ActiveRecord::VERSION::MAJOR >= 3
|
161
|
+
def exec_query(sql, name = nil, binds = [], prepare: false)
|
162
|
+
type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
|
255
163
|
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
164
|
+
log(sql, name, binds) do
|
165
|
+
# Don't cache statements if they are not prepared
|
166
|
+
unless prepare
|
167
|
+
stmt = @connection.prepare(sql)
|
168
|
+
begin
|
169
|
+
cols = stmt.columns
|
170
|
+
unless without_prepared_statement?(binds)
|
171
|
+
stmt.bind_params(type_casted_binds)
|
172
|
+
end
|
173
|
+
records = stmt.to_a
|
174
|
+
ensure
|
175
|
+
stmt.close
|
176
|
+
end
|
177
|
+
stmt = records
|
178
|
+
else
|
179
|
+
cache = @statements[sql] ||= {
|
180
|
+
:stmt => @connection.prepare(sql)
|
181
|
+
}
|
182
|
+
stmt = cache[:stmt]
|
183
|
+
cols = cache[:cols] ||= stmt.columns
|
184
|
+
stmt.reset!
|
185
|
+
stmt.bind_params(type_casted_binds)
|
186
|
+
end
|
264
187
|
|
265
|
-
|
188
|
+
ActiveRecord::Result.new(cols, stmt.to_a)
|
189
|
+
end
|
266
190
|
end
|
267
191
|
|
268
|
-
|
269
|
-
|
270
|
-
|
192
|
+
def exec_delete(sql, name = 'SQL', binds = [])
|
193
|
+
exec_query(sql, name, binds)
|
194
|
+
@connection.changes
|
271
195
|
end
|
196
|
+
alias :exec_update :exec_delete
|
272
197
|
|
273
|
-
def
|
274
|
-
|
198
|
+
def last_inserted_id(result)
|
199
|
+
@connection.last_insert_row_id
|
275
200
|
end
|
276
|
-
# NOTE: not part of official AR (4.2) alias truncate truncate_fake
|
277
201
|
|
278
|
-
|
279
|
-
|
280
|
-
# @return [Integer]
|
281
|
-
def allowed_index_name_length
|
282
|
-
index_name_length - 2
|
202
|
+
def execute(sql, name = nil) #:nodoc:
|
203
|
+
log(sql, name) { @connection.execute(sql) }
|
283
204
|
end
|
284
205
|
|
285
|
-
|
286
|
-
|
287
|
-
log("SAVEPOINT #{name}", 'Savepoint') { super }
|
206
|
+
def begin_db_transaction #:nodoc:
|
207
|
+
log("begin transaction",nil) { @connection.transaction }
|
288
208
|
end
|
289
209
|
|
290
|
-
|
291
|
-
|
292
|
-
log("ROLLBACK TO SAVEPOINT #{name}", 'Savepoint') { super }
|
210
|
+
def commit_db_transaction #:nodoc:
|
211
|
+
log("commit transaction",nil) { @connection.commit }
|
293
212
|
end
|
294
213
|
|
295
|
-
|
296
|
-
|
297
|
-
log("RELEASE SAVEPOINT #{name}", 'Savepoint') { super }
|
214
|
+
def exec_rollback_db_transaction #:nodoc:
|
215
|
+
log("rollback transaction",nil) { @connection.rollback }
|
298
216
|
end
|
299
217
|
|
300
|
-
#
|
301
|
-
|
302
|
-
|
303
|
-
|
218
|
+
# SCHEMA STATEMENTS ========================================
|
219
|
+
|
220
|
+
def tables(name = nil) # :nodoc:
|
221
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
222
|
+
#tables currently returns both tables and views.
|
223
|
+
This behavior is deprecated and will be changed with Rails 5.1 to only return tables.
|
224
|
+
Use #data_sources instead.
|
225
|
+
MSG
|
226
|
+
|
227
|
+
if name
|
228
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
229
|
+
Passing arguments to #tables is deprecated without replacement.
|
230
|
+
MSG
|
231
|
+
end
|
232
|
+
|
233
|
+
data_sources
|
304
234
|
end
|
305
235
|
|
306
|
-
|
307
|
-
|
236
|
+
def data_sources
|
237
|
+
select_values("SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'", "SCHEMA")
|
308
238
|
end
|
309
239
|
|
310
|
-
|
311
|
-
|
312
|
-
|
240
|
+
def table_exists?(table_name)
|
241
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
242
|
+
#table_exists? currently checks both tables and views.
|
243
|
+
This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
|
244
|
+
Use #data_source_exists? instead.
|
245
|
+
MSG
|
246
|
+
|
247
|
+
data_source_exists?(table_name)
|
313
248
|
end
|
314
249
|
|
315
|
-
def
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
result.map! do |row| # [ { 'id' => ... }, {...} ]
|
323
|
-
record = {}
|
324
|
-
row.each_key do |key|
|
325
|
-
if key.is_a?(String)
|
326
|
-
record[key.sub(/^"?\w+"?\./, '')] = row[key]
|
327
|
-
end
|
328
|
-
end
|
329
|
-
record
|
330
|
-
end
|
331
|
-
end
|
332
|
-
result
|
250
|
+
def data_source_exists?(table_name)
|
251
|
+
return false unless table_name.present?
|
252
|
+
|
253
|
+
sql = "SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name <> 'sqlite_sequence'"
|
254
|
+
sql << " AND name = #{quote(table_name)}"
|
255
|
+
|
256
|
+
select_values(sql, "SCHEMA").any?
|
333
257
|
end
|
334
258
|
|
335
|
-
|
336
|
-
|
337
|
-
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
|
338
|
-
result = execute(sql, name, binds)
|
339
|
-
id_value || last_inserted_id(result)
|
259
|
+
def views # :nodoc:
|
260
|
+
select_values("SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'", "SCHEMA")
|
340
261
|
end
|
341
262
|
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
263
|
+
def view_exists?(view_name) # :nodoc:
|
264
|
+
return false unless view_name.present?
|
265
|
+
|
266
|
+
sql = "SELECT name FROM sqlite_master WHERE type = 'view' AND name <> 'sqlite_sequence'"
|
267
|
+
sql << " AND name = #{quote(view_name)}"
|
268
|
+
|
269
|
+
select_values(sql, "SCHEMA").any?
|
348
270
|
end
|
349
271
|
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
rescue ActiveRecord::JDBCError => error
|
354
|
-
e = ActiveRecord::StatementInvalid.new("Could not find table '#{table_name}'")
|
355
|
-
e.set_backtrace error.backtrace
|
356
|
-
raise e
|
357
|
-
end
|
358
|
-
|
359
|
-
# @override
|
360
|
-
def columns(table_name, name = nil)
|
361
|
-
column = jdbc_column_class
|
362
|
-
pass_cast_type = respond_to?(:lookup_cast_type)
|
272
|
+
# Returns an array of +Column+ objects for the table specified by +table_name+.
|
273
|
+
def columns(table_name) # :nodoc:
|
274
|
+
table_name = table_name.to_s
|
363
275
|
table_structure(table_name).map do |field|
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
276
|
+
case field["dflt_value"]
|
277
|
+
when /^null$/i
|
278
|
+
field["dflt_value"] = nil
|
279
|
+
when /^'(.*)'$/m
|
280
|
+
field["dflt_value"] = $1.gsub("''", "'")
|
281
|
+
when /^"(.*)"$/m
|
282
|
+
field["dflt_value"] = $1.gsub('""', '"')
|
370
283
|
end
|
284
|
+
|
285
|
+
collation = field["collation"]
|
286
|
+
sql_type = field["type"]
|
287
|
+
type_metadata = fetch_type_metadata(sql_type)
|
288
|
+
new_column(field["name"], field["dflt_value"], type_metadata, field["notnull"].to_i == 0, table_name, nil, collation)
|
371
289
|
end
|
372
290
|
end
|
373
291
|
|
374
|
-
#
|
375
|
-
def
|
376
|
-
|
377
|
-
|
292
|
+
# Returns an array of indexes for the given table.
|
293
|
+
def indexes(table_name, name = nil) #:nodoc:
|
294
|
+
exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").map do |row|
|
295
|
+
sql = <<-SQL
|
296
|
+
SELECT sql
|
297
|
+
FROM sqlite_master
|
298
|
+
WHERE name=#{quote(row['name'])} AND type='index'
|
299
|
+
UNION ALL
|
300
|
+
SELECT sql
|
301
|
+
FROM sqlite_temp_master
|
302
|
+
WHERE name=#{quote(row['name'])} AND type='index'
|
303
|
+
SQL
|
304
|
+
index_sql = exec_query(sql).first["sql"]
|
305
|
+
match = /\sWHERE\s+(.+)$/i.match(index_sql)
|
306
|
+
where = match[1] if match
|
307
|
+
IndexDefinition.new(
|
308
|
+
table_name,
|
309
|
+
row["name"],
|
310
|
+
row["unique"] != 0,
|
311
|
+
exec_query("PRAGMA index_info('#{row['name']}')", "SCHEMA").map { |col|
|
312
|
+
col["name"]
|
313
|
+
}, nil, nil, where)
|
314
|
+
end
|
378
315
|
end
|
379
316
|
|
380
|
-
#
|
381
|
-
|
382
|
-
|
383
|
-
# on JDBC 3.7 we'll simply do super since it can not handle "PRAGMA index_info"
|
384
|
-
return @connection.indexes(table_name, name) if sqlite_version < '3.8' # super
|
385
|
-
|
386
|
-
name ||= 'SCHEMA'
|
387
|
-
exec_query_raw("PRAGMA index_list(#{quote_table_name(table_name)})", name).map do |row|
|
388
|
-
index_name = row['name']
|
389
|
-
sql = "SELECT sql FROM sqlite_master"
|
390
|
-
sql << " WHERE name=#{quote(index_name)} AND type='index'"
|
391
|
-
sql << " UNION ALL "
|
392
|
-
sql << "SELECT sql FROM sqlite_temp_master"
|
393
|
-
sql << " WHERE name=#{quote(index_name)} AND type='index'"
|
394
|
-
where = nil
|
395
|
-
exec_query_raw(sql, name) do |index_sql|
|
396
|
-
match = /\sWHERE\s+(.+)$/i.match(index_sql)
|
397
|
-
where = match[1] if match
|
398
|
-
end
|
399
|
-
begin
|
400
|
-
columns = exec_query_raw("PRAGMA index_info('#{index_name}')", name).map { |col| col['name'] }
|
401
|
-
rescue => e
|
402
|
-
# NOTE: JDBC <= 3.8.7 bug work-around :
|
403
|
-
if e.message && e.message.index('[SQLITE_ERROR] SQL error or missing database')
|
404
|
-
columns = []
|
405
|
-
end
|
406
|
-
raise e
|
407
|
-
end
|
408
|
-
new_index_definition(table_name, index_name, row['unique'] != 0, columns, nil, nil, where)
|
409
|
-
end
|
317
|
+
def primary_keys(table_name) # :nodoc:
|
318
|
+
pks = table_structure(table_name).select { |f| f["pk"] > 0 }
|
319
|
+
pks.sort_by { |f| f["pk"] }.map { |f| f["name"] }
|
410
320
|
end
|
411
321
|
|
412
|
-
|
413
|
-
|
414
|
-
|
322
|
+
def remove_index(table_name, options = {}) #:nodoc:
|
323
|
+
index_name = index_name_for_remove(table_name, options)
|
324
|
+
exec_query "DROP INDEX #{quote_column_name(index_name)}"
|
415
325
|
end
|
416
326
|
|
417
|
-
#
|
327
|
+
# Renames a table.
|
328
|
+
#
|
329
|
+
# Example:
|
330
|
+
# rename_table('octopuses', 'octopi')
|
418
331
|
def rename_table(table_name, new_name)
|
419
|
-
|
420
|
-
rename_table_indexes(table_name, new_name)
|
332
|
+
exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
333
|
+
rename_table_indexes(table_name, new_name)
|
421
334
|
end
|
422
335
|
|
423
|
-
#
|
424
|
-
#
|
425
|
-
def
|
336
|
+
# See: http://www.sqlite.org/lang_altertable.html
|
337
|
+
# SQLite has an additional restriction on the ALTER TABLE statement
|
338
|
+
def valid_alter_table_type?(type)
|
426
339
|
type.to_sym != :primary_key
|
427
340
|
end
|
428
341
|
|
429
|
-
def add_column(table_name, column_name, type, options = {})
|
430
|
-
if
|
342
|
+
def add_column(table_name, column_name, type, options = {}) #:nodoc:
|
343
|
+
if valid_alter_table_type?(type)
|
431
344
|
super(table_name, column_name, type, options)
|
432
345
|
else
|
433
346
|
alter_table(table_name) do |definition|
|
@@ -436,51 +349,30 @@ module ArJdbc
|
|
436
349
|
end
|
437
350
|
end
|
438
351
|
|
439
|
-
|
440
|
-
|
441
|
-
# @private
|
442
|
-
def remove_column(table_name, column_name, type = nil, options = {})
|
352
|
+
def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
|
443
353
|
alter_table(table_name) do |definition|
|
444
354
|
definition.remove_column column_name
|
445
355
|
end
|
446
356
|
end
|
447
357
|
|
448
|
-
|
449
|
-
|
450
|
-
# @private
|
451
|
-
def remove_column(table_name, *column_names)
|
452
|
-
if column_names.empty?
|
453
|
-
raise ArgumentError.new(
|
454
|
-
"You must specify at least one column name." +
|
455
|
-
" Example: remove_column(:people, :first_name)"
|
456
|
-
)
|
457
|
-
end
|
458
|
-
column_names.flatten.each do |column_name|
|
459
|
-
alter_table(table_name) do |definition|
|
460
|
-
definition.columns.delete(definition[column_name])
|
461
|
-
end
|
462
|
-
end
|
463
|
-
end
|
464
|
-
alias :remove_columns :remove_column
|
465
|
-
|
466
|
-
end
|
358
|
+
def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
|
359
|
+
default = extract_new_default_value(default_or_changes)
|
467
360
|
|
468
|
-
def change_column_default(table_name, column_name, default) #:nodoc:
|
469
361
|
alter_table(table_name) do |definition|
|
470
362
|
definition[column_name].default = default
|
471
363
|
end
|
472
364
|
end
|
473
365
|
|
474
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
366
|
+
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
475
367
|
unless null || default.nil?
|
476
|
-
|
368
|
+
exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
477
369
|
end
|
478
370
|
alter_table(table_name) do |definition|
|
479
371
|
definition[column_name].null = null
|
480
372
|
end
|
481
373
|
end
|
482
374
|
|
483
|
-
def change_column(table_name, column_name, type, options = {})
|
375
|
+
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
484
376
|
alter_table(table_name) do |definition|
|
485
377
|
include_default = options_include_default?(options)
|
486
378
|
definition[column_name].instance_eval do
|
@@ -490,88 +382,164 @@ module ArJdbc
|
|
490
382
|
self.null = options[:null] if options.include?(:null)
|
491
383
|
self.precision = options[:precision] if options.include?(:precision)
|
492
384
|
self.scale = options[:scale] if options.include?(:scale)
|
385
|
+
self.collation = options[:collation] if options.include?(:collation)
|
493
386
|
end
|
494
387
|
end
|
495
388
|
end
|
496
389
|
|
497
|
-
def rename_column(table_name, column_name, new_column_name)
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s})
|
502
|
-
rename_column_indexes(table_name, column_name, new_column_name) if respond_to?(:rename_column_indexes) # AR-4.0 SchemaStatements
|
390
|
+
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
391
|
+
column = column_for(table_name, column_name)
|
392
|
+
alter_table(table_name, rename: { column.name => new_column_name.to_s })
|
393
|
+
rename_column_indexes(table_name, column.name, new_column_name)
|
503
394
|
end
|
504
395
|
|
505
|
-
|
506
|
-
def add_lock!(sql, options)
|
507
|
-
sql # SELECT ... FOR UPDATE is redundant since the table is locked
|
508
|
-
end if ::ActiveRecord::VERSION::MAJOR < 3
|
396
|
+
protected
|
509
397
|
|
510
|
-
def
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
# on 4.0 no longer re-defined (thus inherits default)
|
515
|
-
"DEFAULT VALUES"
|
398
|
+
def table_structure(table_name)
|
399
|
+
structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
|
400
|
+
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
|
401
|
+
table_structure_with_collation(table_name, structure)
|
516
402
|
end
|
517
403
|
|
518
|
-
def
|
519
|
-
|
404
|
+
def alter_table(table_name, options = {}) #:nodoc:
|
405
|
+
altered_table_name = "a#{table_name}"
|
406
|
+
caller = lambda { |definition| yield definition if block_given? }
|
407
|
+
|
408
|
+
transaction do
|
409
|
+
move_table(table_name, altered_table_name,
|
410
|
+
options.merge(temporary: true))
|
411
|
+
move_table(altered_table_name, table_name, &caller)
|
412
|
+
end
|
520
413
|
end
|
521
414
|
|
522
|
-
def
|
523
|
-
|
415
|
+
def move_table(from, to, options = {}, &block) #:nodoc:
|
416
|
+
copy_table(from, to, options, &block)
|
417
|
+
drop_table(from)
|
418
|
+
end
|
419
|
+
|
420
|
+
def copy_table(from, to, options = {}) #:nodoc:
|
421
|
+
from_primary_key = primary_key(from)
|
422
|
+
options[:id] = false
|
423
|
+
create_table(to, options) do |definition|
|
424
|
+
@definition = definition
|
425
|
+
@definition.primary_key(from_primary_key) if from_primary_key.present?
|
426
|
+
columns(from).each do |column|
|
427
|
+
column_name = options[:rename] ?
|
428
|
+
(options[:rename][column.name] ||
|
429
|
+
options[:rename][column.name.to_sym] ||
|
430
|
+
column.name) : column.name
|
431
|
+
next if column_name == from_primary_key
|
432
|
+
|
433
|
+
@definition.column(column_name, column.type,
|
434
|
+
limit: column.limit, default: column.default,
|
435
|
+
precision: column.precision, scale: column.scale,
|
436
|
+
null: column.null, collation: column.collation)
|
437
|
+
end
|
438
|
+
yield @definition if block_given?
|
439
|
+
end
|
440
|
+
copy_table_indexes(from, to, options[:rename] || {})
|
441
|
+
copy_table_contents(from, to,
|
442
|
+
@definition.columns.map(&:name),
|
443
|
+
options[:rename] || {})
|
444
|
+
end
|
445
|
+
|
446
|
+
def copy_table_indexes(from, to, rename = {}) #:nodoc:
|
447
|
+
indexes(from).each do |index|
|
448
|
+
name = index.name
|
449
|
+
if to == "a#{from}"
|
450
|
+
name = "t#{name}"
|
451
|
+
elsif from == "a#{to}"
|
452
|
+
name = name[1..-1]
|
453
|
+
end
|
454
|
+
|
455
|
+
to_column_names = columns(to).map(&:name)
|
456
|
+
columns = index.columns.map { |c| rename[c] || c }.select do |column|
|
457
|
+
to_column_names.include?(column)
|
458
|
+
end
|
459
|
+
|
460
|
+
unless columns.empty?
|
461
|
+
# index name can't be the same
|
462
|
+
opts = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
|
463
|
+
opts[:unique] = true if index.unique
|
464
|
+
add_index(to, columns, opts)
|
465
|
+
end
|
466
|
+
end
|
524
467
|
end
|
525
468
|
|
526
|
-
|
469
|
+
def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
|
470
|
+
column_mappings = Hash[columns.map { |name| [name, name] }]
|
471
|
+
rename.each { |a| column_mappings[a.last] = a.first }
|
472
|
+
from_columns = columns(from).collect(&:name)
|
473
|
+
columns = columns.find_all { |col| from_columns.include?(column_mappings[col]) }
|
474
|
+
from_columns_to_copy = columns.map { |col| column_mappings[col] }
|
475
|
+
quoted_columns = columns.map { |col| quote_column_name(col) } * ","
|
476
|
+
quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","
|
527
477
|
|
528
|
-
|
529
|
-
|
478
|
+
exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
|
479
|
+
SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
|
480
|
+
end
|
481
|
+
|
482
|
+
def sqlite_version
|
483
|
+
@sqlite_version ||= SQLite3Adapter::Version.new(select_value("select sqlite_version(*)"))
|
530
484
|
end
|
531
485
|
|
532
486
|
def translate_exception(exception, message)
|
533
|
-
|
487
|
+
case exception.message
|
534
488
|
# SQLite 3.8.2 returns a newly formatted error message:
|
535
489
|
# UNIQUE constraint failed: *table_name*.*column_name*
|
536
490
|
# Older versions of SQLite return:
|
537
491
|
# column *column_name* is not unique
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
492
|
+
when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/
|
493
|
+
RecordNotUnique.new(message)
|
494
|
+
else
|
495
|
+
super
|
542
496
|
end
|
543
|
-
super
|
544
497
|
end
|
545
498
|
|
546
|
-
|
547
|
-
|
548
|
-
include Comparable
|
499
|
+
private
|
500
|
+
COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
|
549
501
|
|
550
|
-
|
551
|
-
|
552
|
-
|
502
|
+
def table_structure_with_collation(table_name, basic_structure)
|
503
|
+
collation_hash = {}
|
504
|
+
sql = "SELECT sql FROM
|
505
|
+
(SELECT * FROM sqlite_master UNION ALL
|
506
|
+
SELECT * FROM sqlite_temp_master)
|
507
|
+
WHERE type='table' and name='#{ table_name }' \;"
|
553
508
|
|
554
|
-
|
555
|
-
|
556
|
-
|
509
|
+
# Result will have following sample string
|
510
|
+
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
511
|
+
# "password_digest" varchar COLLATE "NOCASE");
|
512
|
+
result = exec_query(sql, 'SCHEMA').first
|
557
513
|
|
558
|
-
|
559
|
-
|
560
|
-
|
514
|
+
if result
|
515
|
+
# Splitting with left parentheses and picking up last will return all
|
516
|
+
# columns separated with comma(,).
|
517
|
+
columns_string = result["sql"].split('(').last
|
561
518
|
|
562
|
-
|
519
|
+
columns_string.split(',').each do |column_string|
|
520
|
+
# This regex will match the column name and collation type and will save
|
521
|
+
# the value in $1 and $2 respectively.
|
522
|
+
collation_hash[$1] = $2 if (COLLATE_REGEX =~ column_string)
|
523
|
+
end
|
524
|
+
|
525
|
+
basic_structure.map! do |column|
|
526
|
+
column_name = column['name']
|
563
527
|
|
528
|
+
if collation_hash.has_key? column_name
|
529
|
+
column['collation'] = collation_hash[column_name]
|
530
|
+
end
|
531
|
+
|
532
|
+
column
|
533
|
+
end
|
534
|
+
else
|
535
|
+
basic_structure.to_hash
|
536
|
+
end
|
537
|
+
end
|
564
538
|
end
|
565
539
|
end
|
566
540
|
|
567
541
|
module ActiveRecord::ConnectionAdapters
|
568
|
-
|
569
|
-
# NOTE: SQLite3Column exists in native adapter since AR 4.0
|
570
|
-
remove_const(:SQLite3Column) if const_defined?(:SQLite3Column)
|
571
|
-
|
572
542
|
class SQLite3Column < JdbcColumn
|
573
|
-
include ArJdbc::SQLite3::Column
|
574
|
-
|
575
543
|
def initialize(name, *args)
|
576
544
|
if Hash === name
|
577
545
|
super
|
@@ -590,29 +558,150 @@ module ActiveRecord::ConnectionAdapters
|
|
590
558
|
end
|
591
559
|
value
|
592
560
|
end
|
561
|
+
|
562
|
+
# @override {ActiveRecord::ConnectionAdapters::JdbcColumn#init_column}
|
563
|
+
def init_column(name, default, *args)
|
564
|
+
if default =~ /NULL/
|
565
|
+
@default = nil
|
566
|
+
else
|
567
|
+
super
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
# @override {ActiveRecord::ConnectionAdapters::JdbcColumn#default_value}
|
572
|
+
def default_value(value)
|
573
|
+
# JDBC returns column default strings with actual single quotes :
|
574
|
+
return $1 if value =~ /^'(.*)'$/
|
575
|
+
|
576
|
+
value
|
577
|
+
end
|
578
|
+
|
579
|
+
# @override {ActiveRecord::ConnectionAdapters::Column#type_cast}
|
580
|
+
def type_cast(value)
|
581
|
+
return nil if value.nil?
|
582
|
+
case type
|
583
|
+
when :string then value
|
584
|
+
when :primary_key
|
585
|
+
value.respond_to?(:to_i) ? value.to_i : ( value ? 1 : 0 )
|
586
|
+
when :float then value.to_f
|
587
|
+
when :decimal then self.class.value_to_decimal(value)
|
588
|
+
when :boolean then self.class.value_to_boolean(value)
|
589
|
+
else super
|
590
|
+
end
|
591
|
+
end
|
592
|
+
|
593
|
+
private
|
594
|
+
|
595
|
+
# @override {ActiveRecord::ConnectionAdapters::Column#simplified_type}
|
596
|
+
def simplified_type(field_type)
|
597
|
+
case field_type
|
598
|
+
when /boolean/i then :boolean
|
599
|
+
when /text/i then :text
|
600
|
+
when /varchar/i then :string
|
601
|
+
when /int/i then :integer
|
602
|
+
when /float/i then :float
|
603
|
+
when /real|decimal/i then
|
604
|
+
extract_scale(field_type) == 0 ? :integer : :decimal
|
605
|
+
when /datetime/i then :datetime
|
606
|
+
when /date/i then :date
|
607
|
+
when /time/i then :time
|
608
|
+
when /blob/i then :binary
|
609
|
+
else super
|
610
|
+
end
|
611
|
+
end
|
612
|
+
|
613
|
+
# @override {ActiveRecord::ConnectionAdapters::Column#extract_limit}
|
614
|
+
def extract_limit(sql_type)
|
615
|
+
return nil if sql_type =~ /^(real)\(\d+/i
|
616
|
+
super
|
617
|
+
end
|
618
|
+
|
619
|
+
def extract_precision(sql_type)
|
620
|
+
case sql_type
|
621
|
+
when /^(real)\((\d+)(,\d+)?\)/i then $2.to_i
|
622
|
+
else super
|
623
|
+
end
|
624
|
+
end
|
625
|
+
|
626
|
+
def extract_scale(sql_type)
|
627
|
+
case sql_type
|
628
|
+
when /^(real)\((\d+)\)/i then 0
|
629
|
+
when /^(real)\((\d+)(,(\d+))\)/i then $4.to_i
|
630
|
+
else super
|
631
|
+
end
|
632
|
+
end
|
593
633
|
end
|
594
634
|
|
595
635
|
remove_const(:SQLite3Adapter) if const_defined?(:SQLite3Adapter)
|
596
636
|
|
597
|
-
|
637
|
+
# Currently our adapter is named the same as what AR5 names its adapter. We will need to get
|
638
|
+
# this changed at some point so this can be a unique name and we can extend activerecord
|
639
|
+
# ActiveRecord::ConnectionAdapters::SQLite3Adapter. Once we can do that we can remove the
|
640
|
+
# module SQLite3 above and remove a majority of this file.
|
641
|
+
class SQLite3Adapter < AbstractAdapter
|
642
|
+
include ArJdbc::CommonJdbcMethods
|
598
643
|
include ArJdbc::SQLite3
|
599
|
-
include ArJdbc::SQLite3::ExplainSupport
|
600
644
|
|
601
|
-
|
602
|
-
|
645
|
+
# FIXME: Add @connection.encoding then remove this method
|
646
|
+
def encoding
|
647
|
+
select_value 'PRAGMA encoding'
|
603
648
|
end
|
604
649
|
|
605
|
-
|
606
|
-
|
650
|
+
def exec_query(sql, name = nil, binds = [], prepare: false)
|
651
|
+
use_prepared = prepare || !without_prepared_statement?(binds)
|
607
652
|
|
608
|
-
|
653
|
+
if use_prepared
|
654
|
+
type_casted_binds = prepare_binds_for_jdbc(binds)
|
655
|
+
log(sql, name, binds) { @connection.execute_prepared(sql, type_casted_binds) }
|
656
|
+
else
|
657
|
+
log(sql, name) { @connection.execute(sql) }
|
658
|
+
end
|
659
|
+
end
|
609
660
|
|
610
|
-
|
611
|
-
|
612
|
-
SQLiteColumn = SQLite3Column
|
661
|
+
def exec_update(sql, name = nil, binds = [])
|
662
|
+
use_prepared = !without_prepared_statement?(binds)
|
613
663
|
|
614
|
-
|
664
|
+
if use_prepared
|
665
|
+
type_casted_binds = prepare_binds_for_jdbc(binds)
|
666
|
+
log(sql, name, binds) { @connection.execute_prepared_update(sql, type_casted_binds) }
|
667
|
+
else
|
668
|
+
log(sql, name) { @connection.execute_update(sql, nil) }
|
669
|
+
end
|
670
|
+
end
|
671
|
+
alias :exec_delete :exec_update
|
672
|
+
|
673
|
+
# Sqlite3 JDBC types in prepared statements seem to report blob as varchar (12).
|
674
|
+
# So to work around this we will pass attribute type in with the value so we can
|
675
|
+
# then remap to appropriate type in JDBC without needing to ask JDBC what type
|
676
|
+
# it should be using. No one likes a stinking liar...
|
677
|
+
def prepare_binds_for_jdbc(binds)
|
678
|
+
binds.map do |attribute|
|
679
|
+
[attribute.type.type, type_cast(attribute.value_for_database)]
|
680
|
+
end
|
681
|
+
end
|
615
682
|
|
616
|
-
|
683
|
+
# last two values passed but not used so I cannot alias to exec_query
|
684
|
+
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
|
685
|
+
exec_update(sql, name, binds)
|
686
|
+
end
|
687
|
+
|
688
|
+
def indexes(table_name, name = nil) #:nodoc:
|
689
|
+
# on JDBC 3.7 we'll simply do super since it can not handle "PRAGMA index_info"
|
690
|
+
return @connection.indexes(table_name, name) if sqlite_version < '3.8' # super
|
691
|
+
super
|
692
|
+
end
|
693
|
+
|
694
|
+
def jdbc_column_class
|
695
|
+
::ActiveRecord::ConnectionAdapters::SQLite3Column
|
696
|
+
end
|
697
|
+
|
698
|
+
def jdbc_connection_class(spec)
|
699
|
+
self.class.jdbc_connection_class
|
700
|
+
end
|
701
|
+
|
702
|
+
# @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class
|
703
|
+
def self.jdbc_connection_class
|
704
|
+
::ActiveRecord::ConnectionAdapters::SQLite3JdbcConnection
|
705
|
+
end
|
617
706
|
end
|
618
707
|
end
|