activerecord-jdbc-adapter 1.3.25 → 5.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
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