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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +57 -72
  3. data/Appraisals +7 -2
  4. data/Gemfile +3 -7
  5. data/History.md +0 -50
  6. data/RUNNING_TESTS.md +4 -0
  7. data/activerecord-jdbc-adapter.gemspec +2 -1
  8. data/lib/arjdbc/common_jdbc_methods.rb +89 -0
  9. data/lib/arjdbc/db2/adapter.rb +58 -69
  10. data/lib/arjdbc/db2/as400.rb +2 -13
  11. data/lib/arjdbc/db2/column.rb +1 -1
  12. data/lib/arjdbc/derby/adapter.rb +2 -6
  13. data/lib/arjdbc/firebird/adapter.rb +7 -16
  14. data/lib/arjdbc/h2/adapter.rb +4 -13
  15. data/lib/arjdbc/hsqldb/adapter.rb +5 -5
  16. data/lib/arjdbc/jdbc/adapter.rb +15 -76
  17. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  18. data/lib/arjdbc/jdbc/adapter_require.rb +12 -31
  19. data/lib/arjdbc/jdbc/base_ext.rb +6 -25
  20. data/lib/arjdbc/jdbc/column.rb +15 -1
  21. data/lib/arjdbc/jdbc/connection_methods.rb +7 -1
  22. data/lib/arjdbc/jdbc/type_cast.rb +16 -4
  23. data/lib/arjdbc/jdbc/type_converter.rb +0 -1
  24. data/lib/arjdbc/mssql/adapter.rb +9 -21
  25. data/lib/arjdbc/mysql/adapter.rb +14 -19
  26. data/lib/arjdbc/mysql/connection_methods.rb +3 -5
  27. data/lib/arjdbc/oracle/adapter.rb +4 -38
  28. data/lib/arjdbc/oracle/connection_methods.rb +0 -4
  29. data/lib/arjdbc/postgresql/adapter.rb +18 -22
  30. data/lib/arjdbc/postgresql/connection_methods.rb +2 -5
  31. data/lib/arjdbc/postgresql/oid/bytea.rb +0 -1
  32. data/lib/arjdbc/postgresql/oid_types.rb +6 -6
  33. data/lib/arjdbc/sqlite3/adapter.rb +493 -404
  34. data/lib/arjdbc/tasks/database_tasks.rb +1 -1
  35. data/lib/arjdbc/tasks/databases3.rake +1 -1
  36. data/lib/arjdbc/tasks/databases4.rake +3 -8
  37. data/lib/arjdbc/version.rb +1 -1
  38. data/rakelib/db.rake +5 -8
  39. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +102 -37
  40. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +0 -7
  41. metadata +10 -17
  42. data/lib/arjdbc/jdbc/arel_support.rb +0 -133
  43. data/lib/arjdbc/mssql/attributes_for_update.rb +0 -22
  44. data/lib/arjdbc/sqlite3/explain_support.rb +0 -29
@@ -1,3 +1,2 @@
1
1
  class ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Bytea
2
- remove_method :type_cast_from_database
3
2
  end
@@ -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', OID::Integer
168
- register_class_with_limit m, 'int4', OID::Integer
169
- register_class_with_limit m, 'int8', OID::Integer
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', OID::Float.new
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', OID::Date.new
183
- m.register_type 'time', OID::Time.new
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 'arjdbc/sqlite3/explain_support'
4
- require 'arjdbc/util/table_copier'
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
- include Util::TableCopier
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
- # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class
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
- value
40
- end
25
+ include Quoting
41
26
 
42
- # @override {ActiveRecord::ConnectionAdapters::Column#type_cast}
43
- def type_cast(value)
44
- return nil if value.nil?
45
- case type
46
- when :string then value
47
- when :primary_key
48
- value.respond_to?(:to_i) ? value.to_i : ( value ? 1 : 0 )
49
- when :float then value.to_f
50
- when :decimal then self.class.value_to_decimal(value)
51
- when :boolean then self.class.value_to_boolean(value)
52
- else super
53
- end
54
- end
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
- # @override {ActiveRecord::ConnectionAdapters::Column#simplified_type}
59
- def simplified_type(field_type)
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
- # @see ActiveRecord::ConnectionAdapters::Jdbc::ArelSupport
100
- def self.arel_visitor_type(config = nil)
101
- ::Arel::Visitors::SQLite
49
+ def schema_creation # :nodoc:
50
+ SQLite3::SchemaCreation.new self
102
51
  end
103
52
 
104
- # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#bind_substitution
105
- # @private
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
- NATIVE_DATABASE_TYPES = {
117
- :primary_key => nil,
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
- # @override
136
- def native_database_types
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
- sqlite_version >= '3.6.8'
69
+ true
158
70
  end
159
71
 
160
- # @override
161
72
  def supports_partial_index?
162
- sqlite_version >= '3.8.0'
73
+ sqlite_version >= "3.8.0"
163
74
  end
164
75
 
165
- # @override
166
- def supports_add_column?
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
- # @override
171
- def supports_count_distinct?
82
+ # Returns true, since this connection adapter supports migrations.
83
+ def supports_migrations? #:nodoc:
172
84
  true
173
85
  end
174
86
 
175
- # @override
176
- def supports_autoincrement?
87
+ def supports_primary_key? #:nodoc:
177
88
  true
178
89
  end
179
90
 
180
- # @override
181
- def supports_migrations?
91
+ def requires_reloading?
182
92
  true
183
93
  end
184
94
 
185
- # @override
186
- def supports_primary_key?
95
+ def supports_views?
187
96
  true
188
97
  end
189
98
 
190
- # @override
191
- def supports_add_column?
99
+ def supports_datetime_with_precision?
192
100
  true
193
101
  end
194
102
 
195
- # @override
196
- def supports_count_distinct?
197
- true
103
+ def supports_multi_insert?
104
+ sqlite_version >= "3.7.11"
198
105
  end
199
106
 
200
- # @override
201
- def supports_autoincrement?
202
- true
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
- # @override
211
- def supports_views?
128
+ def valid_type?(type)
212
129
  true
213
130
  end
214
131
 
215
- def sqlite_version
216
- @sqlite_version ||= Version.new(select_value('SELECT sqlite_version(*)'))
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
- # @override
221
- def quote(value, column = nil)
222
- return value if sql_literal?(value)
139
+ def native_database_types #:nodoc:
140
+ NATIVE_DATABASE_TYPES
141
+ end
223
142
 
224
- if value.kind_of?(String)
225
- column_type = column && column.type
226
- if column_type == :binary
227
- "x'#{value.unpack("H*")[0]}'"
228
- else
229
- super
230
- end
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
- def quote_table_name_for_assignment(table, attr)
237
- quote_column_name(attr)
238
- end if ::ActiveRecord::VERSION::MAJOR >= 4
152
+ #--
153
+ # DATABASE STATEMENTS ======================================
154
+ #++
239
155
 
240
- # @override
241
- def quote_column_name(name)
242
- %Q("#{name.to_s.gsub('"', '""')}") # "' kludge for emacs font-lock
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
- # Quote date/time values for use in SQL input.
246
- # Includes microseconds if the value is a Time responding to usec.
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
- # @override
257
- def tables(name = nil, table_name = nil)
258
- sql = "SELECT name FROM sqlite_master WHERE type = 'table'"
259
- if table_name
260
- sql << " AND name = #{quote_table_name(table_name)}"
261
- else
262
- sql << " AND NOT name = 'sqlite_sequence'"
263
- end
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
- select_rows(sql, name).map { |row| row[0] }
188
+ ActiveRecord::Result.new(cols, stmt.to_a)
189
+ end
266
190
  end
267
191
 
268
- # @override
269
- def table_exists?(table_name)
270
- table_name && tables(nil, table_name).any?
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 truncate_fake(table_name, name = nil)
274
- execute "DELETE FROM #{quote_table_name(table_name)}; VACUUM", name
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
- # Returns 62. SQLite supports index names up to 64 characters.
279
- # The rest is used by Rails internally to perform temporary rename operations.
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
- # @override
286
- def create_savepoint(name = current_savepoint_name(true))
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
- # @override
291
- def rollback_to_savepoint(name = current_savepoint_name(true))
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
- # @override
296
- def release_savepoint(name = current_savepoint_name(false))
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
- # @private
301
- def recreate_database(name = nil, options = {})
302
- drop_database(name)
303
- create_database(name, options)
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
- # @private
307
- def create_database(name = nil, options = {})
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
- # @private
311
- def drop_database(name = nil)
312
- tables.each { |table| drop_table(table) }
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 select(sql, name = nil, binds = [])
316
- result = super # AR::Result (4.0) or Array (<= 3.2)
317
- if result.respond_to?(:columns) # 4.0
318
- result.columns.map! do |key| # [ [ 'id', ... ]
319
- key.is_a?(String) ? key.sub(/^"?\w+"?\./, '') : key
320
- end
321
- else
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
- # @note We have an extra binds argument at the end due AR-2.3 support.
336
- # @override
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
- # @note Does not support prepared statements for INSERT statements.
343
- # @override
344
- def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
345
- # NOTE: since SQLite JDBC does not support executeUpdate but only
346
- # statement.execute we can not support prepared statements here :
347
- execute(sql, name, binds)
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
- def table_structure(table_name)
351
- sql = "PRAGMA table_info(#{quote_table_name(table_name)})"
352
- log(sql, 'SCHEMA') { @connection.execute_query_raw(sql) }
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
- sql_type = field['type']
365
- if pass_cast_type
366
- cast_type = lookup_cast_type(sql_type)
367
- column.new(field['name'], field['dflt_value'], cast_type, sql_type, field['notnull'] == 0)
368
- else
369
- column.new(field['name'], field['dflt_value'], sql_type, field['notnull'] == 0)
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
- # @override
375
- def primary_key(table_name)
376
- column = table_structure(table_name).find { |field| field['pk'].to_i == 1 }
377
- column && column['name']
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
- # NOTE: do not override indexes without testing support for 3.7.2 & 3.8.7 !
381
- # @override
382
- def indexes(table_name, name = nil)
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
- # @override
413
- def remove_index!(table_name, index_name)
414
- execute "DROP INDEX #{quote_column_name(index_name)}"
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
- # @override
327
+ # Renames a table.
328
+ #
329
+ # Example:
330
+ # rename_table('octopuses', 'octopi')
418
331
  def rename_table(table_name, new_name)
419
- execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
420
- rename_table_indexes(table_name, new_name) if respond_to?(:rename_table_indexes) # AR-4.0 SchemaStatements
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
- # SQLite has an additional restriction on the ALTER TABLE statement.
424
- # @see http://www.sqlite.org/lang_altertable.html
425
- def valid_alter_table_options( type, options)
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 supports_add_column? && valid_alter_table_options( type, options )
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
- if ActiveRecord::VERSION::MAJOR >= 4
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
- else
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
- execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
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
- unless columns(table_name).detect{|c| c.name == column_name.to_s }
499
- raise ActiveRecord::ActiveRecordError, "Missing column #{table_name}.#{column_name}"
500
- end
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
- # @private
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 empty_insert_statement_value
511
- # inherited (default) on 3.2 : "VALUES(DEFAULT)"
512
- # inherited (default) on 4.0 : "DEFAULT VALUES"
513
- # re-defined in native adapter on 3.2 "VALUES(NULL)"
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 encoding
519
- select_value 'PRAGMA encoding'
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 last_insert_id
523
- @connection.last_insert_rowid
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
- protected
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
- def last_inserted_id(result)
529
- super || last_insert_id # NOTE: #last_insert_id call should not be needed
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
- if msg = exception.message
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
- if msg.index('UNIQUE constraint failed: ') ||
539
- msg =~ /column(s)? .* (is|are) not unique/
540
- return ::ActiveRecord::RecordNotUnique.new(message, exception)
541
- end
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
- # @private available in native adapter way back to AR-2.3
547
- class Version
548
- include Comparable
499
+ private
500
+ COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
549
501
 
550
- def initialize(version_string)
551
- @version = version_string.split('.').map! { |v| v.to_i }
552
- end
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
- def <=>(version_string)
555
- @version <=> version_string.split('.').map! { |v| v.to_i }
556
- end
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
- def to_s
559
- @version.join('.')
560
- end
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
- end
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
- class SQLite3Adapter < JdbcAdapter
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
- def jdbc_connection_class(spec)
602
- ::ArJdbc::SQLite3.jdbc_connection_class
645
+ # FIXME: Add @connection.encoding then remove this method
646
+ def encoding
647
+ select_value 'PRAGMA encoding'
603
648
  end
604
649
 
605
- # @private
606
- Version = ArJdbc::SQLite3::Version
650
+ def exec_query(sql, name = nil, binds = [], prepare: false)
651
+ use_prepared = prepare || !without_prepared_statement?(binds)
607
652
 
608
- end
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
- if ActiveRecord::VERSION::MAJOR <= 3
611
- remove_const(:SQLiteColumn) if const_defined?(:SQLiteColumn)
612
- SQLiteColumn = SQLite3Column
661
+ def exec_update(sql, name = nil, binds = [])
662
+ use_prepared = !without_prepared_statement?(binds)
613
663
 
614
- remove_const(:SQLiteAdapter) if const_defined?(:SQLiteAdapter)
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
- SQLiteAdapter = SQLite3Adapter
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