activerecord-jdbc-adapter 5.0.pre1 → 51.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -2
  3. data/.travis.yml +15 -416
  4. data/Gemfile +35 -37
  5. data/README.md +23 -118
  6. data/RUNNING_TESTS.md +31 -26
  7. data/Rakefile +2 -3
  8. data/activerecord-jdbc-adapter.gemspec +1 -2
  9. data/lib/arjdbc/abstract/connection_management.rb +21 -0
  10. data/lib/arjdbc/abstract/core.rb +62 -0
  11. data/lib/arjdbc/abstract/database_statements.rb +46 -0
  12. data/lib/arjdbc/abstract/statement_cache.rb +58 -0
  13. data/lib/arjdbc/abstract/transaction_support.rb +86 -0
  14. data/lib/arjdbc/derby/adapter.rb +6 -1
  15. data/lib/arjdbc/discover.rb +0 -7
  16. data/lib/arjdbc/firebird/adapter.rb +2 -2
  17. data/lib/arjdbc/jdbc/adapter.rb +10 -252
  18. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  19. data/lib/arjdbc/jdbc/connection.rb +6 -0
  20. data/lib/arjdbc/jdbc.rb +2 -2
  21. data/lib/arjdbc/mysql/adapter.rb +87 -944
  22. data/lib/arjdbc/mysql/connection_methods.rb +4 -2
  23. data/lib/arjdbc/postgresql/adapter.rb +288 -1023
  24. data/lib/arjdbc/postgresql/base/array_decoder.rb +26 -0
  25. data/lib/arjdbc/postgresql/base/array_encoder.rb +25 -0
  26. data/lib/arjdbc/postgresql/base/pgconn.rb +8 -5
  27. data/lib/arjdbc/postgresql/column.rb +10 -599
  28. data/lib/arjdbc/postgresql/connection_methods.rb +9 -0
  29. data/lib/arjdbc/postgresql/name.rb +24 -0
  30. data/lib/arjdbc/postgresql/oid_types.rb +25 -110
  31. data/lib/arjdbc/sqlite3/adapter.rb +171 -170
  32. data/lib/arjdbc/tasks/database_tasks.rb +1 -3
  33. data/lib/arjdbc/tasks/db2_database_tasks.rb +2 -2
  34. data/lib/arjdbc/version.rb +1 -1
  35. data/pom.xml +3 -3
  36. data/rakelib/02-test.rake +0 -12
  37. data/rakelib/compile.rake +1 -1
  38. data/rakelib/db.rake +7 -5
  39. data/rakelib/rails.rake +63 -64
  40. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +1 -17
  41. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +518 -1260
  42. data/src/java/arjdbc/mysql/MySQLModule.java +3 -3
  43. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +53 -134
  44. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +214 -240
  45. data/src/java/arjdbc/sqlite3/SQLite3Module.java +0 -20
  46. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +85 -10
  47. metadata +20 -34
  48. data/Appraisals +0 -41
  49. data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -1
  50. data/lib/arjdbc/common_jdbc_methods.rb +0 -89
  51. data/lib/arjdbc/mysql/bulk_change_table.rb +0 -150
  52. data/lib/arjdbc/mysql/column.rb +0 -162
  53. data/lib/arjdbc/mysql/explain_support.rb +0 -82
  54. data/lib/arjdbc/mysql/schema_creation.rb +0 -58
  55. data/lib/arjdbc/oracle/adapter.rb +0 -952
  56. data/lib/arjdbc/oracle/column.rb +0 -126
  57. data/lib/arjdbc/oracle/connection_methods.rb +0 -21
  58. data/lib/arjdbc/oracle.rb +0 -4
  59. data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +0 -21
  60. data/lib/arjdbc/postgresql/base/oid.rb +0 -412
  61. data/lib/arjdbc/postgresql/base/schema_definitions.rb +0 -131
  62. data/lib/arjdbc/postgresql/explain_support.rb +0 -53
  63. data/lib/arjdbc/postgresql/oid/bytea.rb +0 -2
  64. data/lib/arjdbc/postgresql/schema_creation.rb +0 -60
  65. data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +0 -297
  66. data/lib/arjdbc/tasks/oracle_database_tasks.rb +0 -65
  67. data/src/java/arjdbc/oracle/OracleModule.java +0 -75
  68. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +0 -465
@@ -2,30 +2,39 @@
2
2
  ArJdbc.load_java_part :PostgreSQL
3
3
 
4
4
  require 'ipaddr'
5
+ require 'active_record/connection_adapters/abstract_adapter'
6
+ require 'active_record/connection_adapters/postgresql/column'
7
+ require 'active_record/connection_adapters/postgresql/explain_pretty_printer'
8
+ require 'active_record/connection_adapters/postgresql/quoting'
9
+ require 'active_record/connection_adapters/postgresql/referential_integrity'
10
+ require 'active_record/connection_adapters/postgresql/schema_creation'
11
+ require 'active_record/connection_adapters/postgresql/schema_dumper'
12
+ require 'active_record/connection_adapters/postgresql/schema_statements'
13
+ require 'active_record/connection_adapters/postgresql/type_metadata'
14
+ require 'active_record/connection_adapters/postgresql/utils'
15
+ require 'arjdbc/abstract/core'
16
+ require 'arjdbc/abstract/connection_management'
17
+ require 'arjdbc/abstract/database_statements'
18
+ require 'arjdbc/abstract/statement_cache'
19
+ require 'arjdbc/abstract/transaction_support'
20
+ require 'arjdbc/postgresql/base/array_decoder'
21
+ require 'arjdbc/postgresql/base/array_encoder'
22
+ require 'arjdbc/postgresql/name'
5
23
 
6
24
  module ArJdbc
7
25
  # Strives to provide Rails built-in PostgreSQL adapter (API) compatibility.
8
26
  module PostgreSQL
9
27
 
10
- # @deprecated no longer used
11
- # @private
12
- AR4_COMPAT = AR40
13
- # @deprecated no longer used
14
- # @private
15
- AR42_COMPAT = AR42
16
-
17
28
  require 'arjdbc/postgresql/column'
18
- require 'arjdbc/postgresql/explain_support'
19
- require 'arjdbc/postgresql/schema_creation' # AR 4.x
20
29
  require 'arel/visitors/postgresql_jdbc'
21
30
  # @private
22
31
  IndexDefinition = ::ActiveRecord::ConnectionAdapters::IndexDefinition
23
32
 
24
33
  # @private
25
- ForeignKeyDefinition = ::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition if ::ActiveRecord::ConnectionAdapters.const_defined? :ForeignKeyDefinition
34
+ ForeignKeyDefinition = ::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition
26
35
 
27
36
  # @private
28
- Type = ::ActiveRecord::Type if AR42
37
+ Type = ::ActiveRecord::Type
29
38
 
30
39
  # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class
31
40
  def self.jdbc_connection_class
@@ -130,172 +139,60 @@ module ArJdbc
130
139
  # @private
131
140
  ActiveRecordError = ::ActiveRecord::ActiveRecordError
132
141
 
133
- # Maps logical Rails types to PostgreSQL-specific data types.
134
- def type_to_sql(type, limit = nil, precision = nil, scale = nil)
135
- case type.to_s
136
- when 'binary'
137
- # PostgreSQL doesn't support limits on binary (bytea) columns.
138
- # The hard limit is 1Gb, because of a 32-bit size field, and TOAST.
139
- case limit
140
- when nil, 0..0x3fffffff; super(type)
141
- else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
142
- end
143
- when 'text'
144
- # PostgreSQL doesn't support limits on text columns.
145
- # The hard limit is 1Gb, according to section 8.3 in the manual.
146
- case limit
147
- when nil, 0..0x3fffffff; super(type)
148
- else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
149
- end
150
- when 'integer'
151
- return 'integer' unless limit
152
-
153
- case limit
154
- when 1, 2; 'smallint'
155
- when 3, 4; 'integer'
156
- when 5..8; 'bigint'
157
- else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
158
- end
159
- when 'datetime'
160
- return super unless precision
161
-
162
- case precision
163
- when 0..6; "timestamp(#{precision})"
164
- else raise(ActiveRecordError, "No timestamp type has precision of #{precision}. The allowed range of precision is from 0 to 6")
165
- end
166
- else
167
- super
168
- end
169
- end
170
-
171
- def type_cast(value, column, array_member = false)
172
- return super(value, nil) unless column
173
-
174
- case value
175
- when String
176
- return super(value, column) unless 'bytea' == column.sql_type
177
- value # { :value => value, :format => 1 }
178
- when Array
179
- case column.sql_type
180
- when 'point'
181
- jdbc_column_class.point_to_string(value)
182
- when 'json', 'jsonb'
183
- jdbc_column_class.json_to_string(value)
184
- else
185
- return super(value, column) unless column.array?
186
- jdbc_column_class.array_to_string(value, column, self)
187
- end
188
- when NilClass
189
- if column.array? && array_member
190
- 'NULL'
191
- elsif column.array?
192
- value
193
- else
194
- super(value, column)
195
- end
196
- when Hash
197
- case column.sql_type
198
- when 'hstore'
199
- jdbc_column_class.hstore_to_string(value, array_member)
200
- when 'json', 'jsonb'
201
- jdbc_column_class.json_to_string(value)
202
- else super(value, column)
203
- end
204
- when IPAddr
205
- return super unless column.sql_type == 'inet' || column.sql_type == 'cidr'
206
- jdbc_column_class.cidr_to_string(value)
207
- when Range
208
- return super(value, column) unless /range$/ =~ column.sql_type
209
- jdbc_column_class.range_to_string(value)
210
- else
211
- super(value, column)
212
- end
213
- end if AR40 && ! AR42
214
-
215
- # @private
216
- def _type_cast(value)
217
- case value
218
- when Type::Binary::Data
219
- # Return a bind param hash with format as binary.
220
- # See http://deveiate.org/code/pg/PGconn.html#method-i-exec_prepared-doc
221
- # for more information
222
- { :value => value.to_s, :format => 1 }
223
- when OID::Xml::Data, OID::Bit::Data
224
- value.to_s
225
- else
226
- super
227
- end
228
- end if AR42
229
- private :_type_cast if AR42
230
-
231
142
  NATIVE_DATABASE_TYPES = {
232
- :primary_key => "serial primary key",
233
- :string => { :name => "character varying", :limit => 255 },
234
- :text => { :name => "text" },
235
- :integer => { :name => "integer" },
236
- :float => { :name => "float" },
237
- :numeric => { :name => "numeric" },
238
- :decimal => { :name => "decimal" }, # :limit => 1000
239
- :datetime => { :name => "timestamp" },
240
- :timestamp => { :name => "timestamp" },
241
- :time => { :name => "time" },
242
- :date => { :name => "date" },
243
- :binary => { :name => "bytea" },
244
- :boolean => { :name => "boolean" },
245
- :xml => { :name => "xml" },
246
- # AR-JDBC added :
247
- #:timestamptz => { :name => "timestamptz" },
248
- #:timetz => { :name => "timetz" },
249
- :money => { :name=>"money" },
250
- :char => { :name => "char" },
251
- :serial => { :name => "serial" }, # auto-inc integer, bigserial, smallserial
143
+ primary_key: 'bigserial primary key',
144
+ bigint: { name: 'bigint' },
145
+ binary: { name: 'bytea' },
146
+ bit: { name: 'bit' },
147
+ bit_varying: { name: 'bit varying' },
148
+ boolean: { name: 'boolean' },
149
+ box: { name: 'box' },
150
+ char: { name: 'char' },
151
+ cidr: { name: 'cidr' },
152
+ circle: { name: 'circle' },
153
+ citext: { name: 'citext' },
154
+ date: { name: 'date' },
155
+ daterange: { name: 'daterange' },
156
+ datetime: { name: 'timestamp' },
157
+ decimal: { name: 'decimal' }, # :limit => 1000
158
+ float: { name: 'float' },
159
+ hstore: { name: 'hstore' },
160
+ inet: { name: 'inet' },
161
+ int4range: { name: 'int4range' },
162
+ int8range: { name: 'int8range' },
163
+ integer: { name: 'integer' },
164
+ interval: { name: 'interval' }, # This doesn't get added to AR's postgres adapter until 5.1 but it fixes broken tests in 5.0 ...
165
+ json: { name: 'json' },
166
+ jsonb: { name: 'jsonb' },
167
+ line: { name: 'line' },
168
+ lseg: { name: 'lseg' },
169
+ ltree: { name: 'ltree' },
170
+ macaddr: { name: 'macaddr' },
171
+ money: { name: 'money' },
172
+ numeric: { name: 'numeric' },
173
+ numrange: { name: 'numrange' },
174
+ oid: { name: 'oid' },
175
+ path: { name: 'path' },
176
+ point: { name: 'point' },
177
+ polygon: { name: 'polygon' },
178
+ string: { name: 'character varying' },
179
+ text: { name: 'text' },
180
+ time: { name: 'time' },
181
+ timestamp: { name: 'timestamp' },
182
+ tsrange: { name: 'tsrange' },
183
+ tstzrange: { name: 'tstzrange' },
184
+ tsvector: { name: 'tsvector' },
185
+ uuid: { name: 'uuid' },
186
+ xml: { name: 'xml' }
252
187
  }
253
188
 
254
- NATIVE_DATABASE_TYPES.update({
255
- :tsvector => { :name => "tsvector" },
256
- :hstore => { :name => "hstore" },
257
- :inet => { :name => "inet" },
258
- :cidr => { :name => "cidr" },
259
- :macaddr => { :name => "macaddr" },
260
- :uuid => { :name => "uuid" },
261
- :json => { :name => "json" },
262
- :jsonb => { :name => "jsonb" },
263
- :ltree => { :name => "ltree" },
264
- # ranges :
265
- :daterange => { :name => "daterange" },
266
- :numrange => { :name => "numrange" },
267
- :tsrange => { :name => "tsrange" },
268
- :tstzrange => { :name => "tstzrange" },
269
- :int4range => { :name => "int4range" },
270
- :int8range => { :name => "int8range" },
271
- }) if AR40
272
-
273
- NATIVE_DATABASE_TYPES.update(
274
- :string => { :name => "character varying" },
275
- :bigserial => "bigserial",
276
- :bigint => { :name => "bigint" },
277
- :bit => { :name => "bit" },
278
- :bit_varying => { :name => "bit varying" }
279
- ) if AR42
280
-
281
189
  def native_database_types
282
190
  NATIVE_DATABASE_TYPES
283
191
  end
284
192
 
285
- # Adds `:array` option to the default set provided by the `AbstractAdapter`.
286
- # @override
287
- def prepare_column_options(column, types)
288
- spec = super
289
- spec[:array] = 'true' if column.respond_to?(:array) && column.array
290
- spec[:default] = "\"#{column.default_function}\"" if column.default_function
291
- spec
292
- end if AR40
293
-
294
- # Adds `:array` as a valid migration key.
295
- # @override
296
- def migration_keys
297
- super + [:array]
298
- end if AR40
193
+ def valid_type?(type)
194
+ !native_database_types[type].nil?
195
+ end
299
196
 
300
197
  # Enable standard-conforming strings if available.
301
198
  def set_standard_conforming_strings
@@ -333,15 +230,21 @@ module ArJdbc
333
230
  @standard_conforming_strings == true # return false if :unsupported
334
231
  end
335
232
 
336
- # Does PostgreSQL support migrations?
337
- def supports_migrations?
338
- true
339
- end
233
+ def supports_ddl_transactions?; true end
340
234
 
341
- # Does PostgreSQL support finding primary key on non-Active Record tables?
342
- def supports_primary_key?
343
- true
344
- end
235
+ def supports_explain?; true end
236
+
237
+ def supports_expression_index?; true end
238
+
239
+ def supports_index_sort_order?; true end
240
+
241
+ def supports_partial_index?; true end
242
+
243
+ def supports_savepoints?; true end
244
+
245
+ def supports_transaction_isolation?(level = nil); true end
246
+
247
+ def supports_views?; true end
345
248
 
346
249
  # Does PostgreSQL support standard conforming strings?
347
250
  def supports_standard_conforming_strings?
@@ -357,56 +260,24 @@ module ArJdbc
357
260
  postgresql_version >= 80200
358
261
  end
359
262
 
360
- def supports_ddl_transactions?; true end
361
-
362
- def supports_transaction_isolation?; true end
363
-
364
- def supports_index_sort_order?; true end
365
-
366
- def supports_partial_index?; true end if AR40
263
+ def supports_pgcrypto_uuid?
264
+ postgresql_version >= 90400
265
+ end
367
266
 
368
267
  # Range data-types weren't introduced until PostgreSQL 9.2.
369
268
  def supports_ranges?
370
269
  postgresql_version >= 90200
371
- end if AR40
372
-
373
- def supports_transaction_isolation?(level = nil)
374
- true
375
- end
376
-
377
- # @override
378
- def supports_views?; true end
379
-
380
- if ArJdbc::AR50
381
- def views
382
- select_values("SELECT table_name FROM INFORMATION_SCHEMA.views WHERE table_schema = ANY (current_schemas(false))")
383
- end
384
- end
385
-
386
- # NOTE: handled by JdbcAdapter we override only to have save-point in logs :
387
-
388
- # @override
389
- def supports_savepoints?; true end
390
-
391
- # @override
392
- def create_savepoint(name = current_savepoint_name(true))
393
- log("SAVEPOINT #{name}", 'Savepoint') { super }
394
- end
395
-
396
- # @override
397
- def rollback_to_savepoint(name = current_savepoint_name(true))
398
- log("ROLLBACK TO SAVEPOINT #{name}", 'Savepoint') { super }
399
- end
400
-
401
- # @override
402
- def release_savepoint(name = current_savepoint_name(false))
403
- log("RELEASE SAVEPOINT #{name}", 'Savepoint') { super }
404
270
  end
405
271
 
406
272
  def supports_extensions?
407
273
  postgresql_version >= 90200
408
274
  end # NOTE: only since AR-4.0 but should not hurt on other versions
409
275
 
276
+ # From AR 5.1 postgres_adapter.rb
277
+ def default_index_type?(index) # :nodoc:
278
+ index.using == :btree || super
279
+ end
280
+
410
281
  def enable_extension(name)
411
282
  execute("CREATE EXTENSION IF NOT EXISTS \"#{name}\"")
412
283
  end
@@ -441,159 +312,76 @@ module ArJdbc
441
312
  execute "SET SESSION AUTHORIZATION #{user}"
442
313
  end
443
314
 
315
+ # Came from postgres_adapter
316
+ def get_advisory_lock(lock_id) # :nodoc:
317
+ unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
318
+ raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
319
+ end
320
+ select_value("SELECT pg_try_advisory_lock(#{lock_id});")
321
+ end
322
+
323
+ # Came from postgres_adapter
324
+ def release_advisory_lock(lock_id) # :nodoc:
325
+ unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
326
+ raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
327
+ end
328
+ select_value("SELECT pg_advisory_unlock(#{lock_id})") == 't'.freeze
329
+ end
330
+
444
331
  # Returns the configured supported identifier length supported by PostgreSQL,
445
332
  # or report the default of 63 on PostgreSQL 7.x.
446
333
  def table_alias_length
447
334
  @table_alias_length ||= (
448
335
  postgresql_version >= 80000 ?
449
- select_one('SHOW max_identifier_length')['max_identifier_length'].to_i :
336
+ select_one('SHOW max_identifier_length', 'SCHEMA'.freeze)['max_identifier_length'].to_i :
450
337
  63
451
338
  )
452
339
  end
340
+ alias index_name_length table_alias_length
453
341
 
454
- def default_sequence_name(table_name, pk = nil)
455
- default_pk, default_seq = pk_and_sequence_for(table_name)
456
- default_seq || "#{table_name}_#{pk || default_pk || 'id'}_seq"
457
- end
458
-
459
- # Resets sequence to the max value of the table's primary key if present.
460
- def reset_pk_sequence!(table, pk = nil, sequence = nil)
461
- if ! pk || ! sequence
462
- default_pk, default_sequence = pk_and_sequence_for(table)
463
- pk ||= default_pk; sequence ||= default_sequence
464
- end
465
- if pk && sequence
466
- quoted_sequence = quote_column_name(sequence)
467
-
468
- select_value <<-end_sql, 'Reset Sequence'
469
- SELECT setval('#{quoted_sequence}', (SELECT COALESCE(MAX(#{quote_column_name pk})+(SELECT increment_by FROM #{quoted_sequence}), (SELECT min_value FROM #{quoted_sequence})) FROM #{quote_table_name(table)}), false)
470
- end_sql
471
- end
472
- end
473
-
474
- # Find a table's primary key and sequence.
475
- def pk_and_sequence_for(table)
476
- # try looking for a seq with a dependency on the table's primary key :
477
- result = select(<<-end_sql, 'PK and Serial Sequence')[0]
478
- SELECT attr.attname, seq.relname
479
- FROM pg_class seq,
480
- pg_attribute attr,
481
- pg_depend dep,
482
- pg_constraint cons
483
- WHERE seq.oid = dep.objid
484
- AND seq.relkind = 'S'
485
- AND attr.attrelid = dep.refobjid
486
- AND attr.attnum = dep.refobjsubid
487
- AND attr.attrelid = cons.conrelid
488
- AND attr.attnum = cons.conkey[1]
489
- AND cons.contype = 'p'
490
- AND dep.refobjid = '#{quote_table_name(table)}'::regclass
491
- end_sql
492
-
493
- if result.nil? || result.empty?
494
- # if that fails, try parsing the primary key's default value :
495
- result = select(<<-end_sql, 'PK and Custom Sequence')[0]
496
- SELECT attr.attname,
497
- CASE
498
- WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
499
- WHEN split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2) ~ '.' THEN
500
- substr(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2),
501
- strpos(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2), '.')+1)
502
- ELSE split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2)
503
- END as relname
504
- FROM pg_class t
505
- JOIN pg_attribute attr ON (t.oid = attrelid)
506
- JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
507
- JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
508
- WHERE t.oid = '#{quote_table_name(table)}'::regclass
509
- AND cons.contype = 'p'
510
- AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
511
- end_sql
342
+ def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
343
+ val = super
344
+ if !use_insert_returning? && pk
345
+ unless sequence_name
346
+ table_ref = extract_table_ref_from_insert_sql(sql)
347
+ sequence_name = default_sequence_name(table_ref, pk)
348
+ return val unless sequence_name
349
+ end
350
+ last_insert_id_result(sequence_name)
351
+ else
352
+ val
512
353
  end
513
-
514
- [ result['attname'], result['relname'] ]
515
- rescue
516
- nil
517
354
  end
518
355
 
519
- def primary_key(table)
520
- result = select(<<-end_sql, 'SCHEMA').first
521
- SELECT attr.attname
522
- FROM pg_attribute attr
523
- INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = any(cons.conkey)
524
- WHERE cons.contype = 'p' AND cons.conrelid = '#{quote_table_name(table)}'::regclass
525
- end_sql
526
-
527
- result && result['attname']
528
- # pk_and_sequence = pk_and_sequence_for(table)
529
- # pk_and_sequence && pk_and_sequence.first
356
+ def explain(arel, binds = [])
357
+ sql = "EXPLAIN #{to_sql(arel, binds)}"
358
+ ActiveRecord::ConnectionAdapters::PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
530
359
  end
531
360
 
532
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
533
- unless pk
534
- # Extract the table from the insert sql. Yuck.
535
- table_ref = extract_table_ref_from_insert_sql(sql)
536
- pk = primary_key(table_ref) if table_ref
537
- end
538
-
539
- if pk && use_insert_returning? # && id_value.nil?
540
- select_value("#{to_sql(sql, binds)} RETURNING #{quote_column_name(pk)}")
361
+ # Take an id from the result of an INSERT query.
362
+ # @return [Integer, NilClass]
363
+ def last_inserted_id(result)
364
+ if result.is_a?(Hash) || result.is_a?(ActiveRecord::Result)
365
+ result.first.first[1] # .first = { "id"=>1 } .first = [ "id", 1 ]
541
366
  else
542
- execute(sql, name, binds) # super
543
- unless id_value
544
- table_ref ||= extract_table_ref_from_insert_sql(sql)
545
- # If neither PK nor sequence name is given, look them up.
546
- if table_ref && ! ( pk ||= primary_key(table_ref) ) && ! sequence_name
547
- pk, sequence_name = pk_and_sequence_for(table_ref)
548
- end
549
- # If a PK is given, fallback to default sequence name.
550
- # Don't fetch last insert id for a table without a PK.
551
- if pk && sequence_name ||= default_sequence_name(table_ref, pk)
552
- id_value = last_insert_id(table_ref, sequence_name)
553
- end
554
- end
555
- id_value
367
+ result
556
368
  end
557
369
  end
558
370
 
559
- # @override
560
- def sql_for_insert(sql, pk, id_value, sequence_name, binds)
561
- unless pk
371
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds) # :nodoc:
372
+ if pk.nil?
562
373
  # Extract the table from the insert sql. Yuck.
563
374
  table_ref = extract_table_ref_from_insert_sql(sql)
564
375
  pk = primary_key(table_ref) if table_ref
565
376
  end
566
377
 
378
+ pk = nil if pk.is_a?(Array)
379
+
567
380
  if pk && use_insert_returning?
568
381
  sql = "#{sql} RETURNING #{quote_column_name(pk)}"
569
382
  end
570
383
 
571
- [ sql, binds ]
572
- end
573
-
574
- # @override due RETURNING clause
575
- def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
576
- # NOTE: 3.2 does not pass the PK on #insert (passed only into #sql_for_insert) :
577
- # sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
578
- # 3.2 :
579
- # value = exec_insert(sql, name, binds)
580
- # 4.x :
581
- # value = exec_insert(sql, name, binds, pk, sequence_name)
582
- if use_insert_returning? && ( pk || (sql.is_a?(String) && sql =~ /RETURNING "?\S+"?$/) )
583
- exec_query(sql, name, binds) # due RETURNING clause returns a result set
584
- else
585
- result = super
586
- if pk
587
- unless sequence_name
588
- table_ref = extract_table_ref_from_insert_sql(sql)
589
- sequence_name = default_sequence_name(table_ref, pk)
590
- return result unless sequence_name
591
- end
592
- last_insert_id_result(sequence_name)
593
- else
594
- result
595
- end
596
- end
384
+ super
597
385
  end
598
386
 
599
387
  # @note Only for "better" AR 4.0 compatibility.
@@ -601,103 +389,29 @@ module ArJdbc
601
389
  def query(sql, name = nil)
602
390
  log(sql, name) do
603
391
  result = []
604
- @connection.execute_query_raw(sql, nil) do |*values|
605
- result << values
392
+ @connection.execute_query_raw(sql, []) do |*values|
393
+ # We need to use #deep_dup here because it appears that
394
+ # the java method is reusing an object in some cases
395
+ # which makes all of the entries in the "result"
396
+ # array end up with the same values as the last row
397
+ result << values.deep_dup
606
398
  end
607
399
  result
608
400
  end
609
401
  end
610
402
 
611
- # Returns an array of schema names.
612
- def schema_names
613
- select_values(
614
- "SELECT nspname FROM pg_namespace" <<
615
- " WHERE nspname !~ '^pg_.*' AND nspname NOT IN ('information_schema')" <<
616
- " ORDER by nspname;",
617
- 'SCHEMA')
618
- end
619
-
620
- # Returns true if schema exists.
621
- def schema_exists?(name)
622
- select_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = '#{name}'", 'SCHEMA').to_i > 0
623
- end
624
-
625
- # Returns the current schema name.
626
- def current_schema
627
- select_value('SELECT current_schema', 'SCHEMA')
628
- end
629
-
630
- # current database name
631
- def current_database
632
- select_value('SELECT current_database()', 'SCHEMA')
633
- end
634
-
635
- # Returns the current database encoding format.
636
- def encoding
637
- select_value(
638
- "SELECT pg_encoding_to_char(pg_database.encoding)" <<
639
- " FROM pg_database" <<
640
- " WHERE pg_database.datname LIKE '#{current_database}'",
641
- 'SCHEMA')
642
- end
643
-
644
- # Returns the current database collation.
645
- def collation
646
- select_value(
647
- "SELECT pg_database.datcollate" <<
648
- " FROM pg_database" <<
649
- " WHERE pg_database.datname LIKE '#{current_database}'",
650
- 'SCHEMA')
651
- end
652
-
653
- # Returns the current database ctype.
654
- def ctype
655
- select_value(
656
- "SELECT pg_database.datctype FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'",
657
- 'SCHEMA')
658
- end
659
-
660
- # Returns the active schema search path.
661
- def schema_search_path
662
- @schema_search_path ||= select_value('SHOW search_path', 'SCHEMA')
663
- end
664
-
665
- # Sets the schema search path to a string of comma-separated schema names.
666
- # Names beginning with $ have to be quoted (e.g. $user => '$user').
667
- # See: http://www.postgresql.org/docs/current/static/ddl-schemas.html
668
- #
669
- # This should be not be called manually but set in database.yml.
670
- def schema_search_path=(schema_csv)
671
- if schema_csv
672
- execute "SET search_path TO #{schema_csv}"
673
- @schema_search_path = schema_csv
674
- end
675
- end
676
-
677
- # Take an id from the result of an INSERT query.
678
- # @return [Integer, NilClass]
679
- def last_inserted_id(result)
680
- return nil if result.nil?
681
- return result if result.is_a? Integer
682
- # <ActiveRecord::Result @hash_rows=nil, @columns=["id"], @rows=[[3]]>
683
- # but it will work with [{ 'id' => 1 }] Hash wrapped results as well
684
- result.first.first[1] # .first = { "id"=>1 } .first = [ "id", 1 ]
685
- end
686
-
687
- def last_insert_id(table, sequence_name = nil)
688
- sequence_name = table if sequence_name.nil? # AR-4.0 1 argument
689
- last_insert_id_result(sequence_name)
403
+ def reset!
404
+ clear_cache!
405
+ reset_transaction
406
+ @connection.rollback # Have to deal with rollbacks differently than the AR adapter
407
+ @connection.execute 'DISCARD ALL'
408
+ configure_connection
690
409
  end
691
410
 
692
411
  def last_insert_id_result(sequence_name)
693
412
  select_value("SELECT currval('#{sequence_name}')", 'SQL')
694
413
  end
695
414
 
696
- def recreate_database(name, options = {})
697
- drop_database(name)
698
- create_database(name, options)
699
- end
700
-
701
415
  # Create a new PostgreSQL database. Options include <tt>:owner</tt>, <tt>:template</tt>,
702
416
  # <tt>:encoding</tt>, <tt>:collation</tt>, <tt>:ctype</tt>,
703
417
  # <tt>:tablespace</tt>, and <tt>:connection_limit</tt> (note that MySQL uses
@@ -733,56 +447,10 @@ module ArJdbc
733
447
  execute "CREATE DATABASE #{quote_table_name(name)}#{option_string}"
734
448
  end
735
449
 
736
- def drop_database(name)
737
- execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
738
- end
739
-
740
- # Creates a schema for the given schema name.
741
- def create_schema(schema_name, pg_username = nil)
742
- if pg_username.nil? # AR 4.0 compatibility - accepts only single argument
743
- execute "CREATE SCHEMA #{schema_name}"
744
- else
745
- execute("CREATE SCHEMA \"#{schema_name}\" AUTHORIZATION \"#{pg_username}\"")
746
- end
747
- end
748
-
749
- # Drops the schema for the given schema name.
750
- def drop_schema schema_name
751
- execute "DROP SCHEMA #{schema_name} CASCADE"
752
- end
753
-
754
450
  def all_schemas
755
451
  select('SELECT nspname FROM pg_namespace').map { |row| row["nspname"] }
756
452
  end
757
453
 
758
- # @deprecated no longer used - handled with (AR built-in) Rake tasks
759
- def structure_dump
760
- database = @config[:database]
761
- if database.nil?
762
- if @config[:url] =~ /\/([^\/]*)$/
763
- database = $1
764
- else
765
- raise "Could not figure out what database this url is for #{@config["url"]}"
766
- end
767
- end
768
-
769
- ENV['PGHOST'] = @config[:host] if @config[:host]
770
- ENV['PGPORT'] = @config[:port].to_s if @config[:port]
771
- ENV['PGPASSWORD'] = @config[:password].to_s if @config[:password]
772
- search_path = "--schema=#{@config[:schema_search_path]}" if @config[:schema_search_path]
773
-
774
- @connection.connection.close
775
- begin
776
- definition = `pg_dump -i -U "#{@config[:username]}" -s -x -O #{search_path} #{database}`
777
- raise "Error dumping database" if $?.exitstatus == 1
778
-
779
- # need to patch away any references to SQL_ASCII as it breaks the JDBC driver
780
- definition.gsub(/SQL_ASCII/, 'UNICODE')
781
- ensure
782
- reconnect!
783
- end
784
- end
785
-
786
454
  # Returns the current client message level.
787
455
  def client_min_messages
788
456
  return nil if redshift? # not supported on Redshift
@@ -797,42 +465,6 @@ module ArJdbc
797
465
  execute("SET client_min_messages TO '#{level}'", 'SCHEMA')
798
466
  end
799
467
 
800
- # Gets the maximum number columns postgres has, default 32
801
- def multi_column_index_limit
802
- defined?(@multi_column_index_limit) && @multi_column_index_limit || 32
803
- end
804
-
805
- # Sets the maximum number columns postgres has, default 32
806
- def multi_column_index_limit=(limit)
807
- @multi_column_index_limit = limit
808
- end
809
-
810
- # @override
811
- def distinct(columns, orders)
812
- "DISTINCT #{columns_for_distinct(columns, orders)}"
813
- end
814
-
815
- # PostgreSQL requires the ORDER BY columns in the select list for distinct
816
- # queries, and requires that the ORDER BY include the distinct column.
817
- # @override Since AR 4.0 (on 4.1 {#distinct} is gone and won't be called).
818
- def columns_for_distinct(columns, orders)
819
- if orders.is_a?(String)
820
- orders = orders.split(','); orders.each(&:strip!)
821
- end
822
-
823
- order_columns = orders.reject(&:blank?).map! do |column|
824
- column = column.is_a?(String) ? column.dup : column.to_sql # AREL node
825
- column.gsub!(/\s+(?:ASC|DESC)\s*/i, '') # remove any ASC/DESC modifiers
826
- column.gsub!(/\s*NULLS\s+(?:FIRST|LAST)?\s*/i, '')
827
- column
828
- end
829
- order_columns.reject!(&:empty?)
830
- i = -1; order_columns.map! { |column| "#{column} AS alias_#{i += 1}" }
831
-
832
- columns = [ columns ]; columns.flatten!
833
- columns.push( *order_columns ).join(', ')
834
- end
835
-
836
468
  # ORDER BY clause for the passed order option.
837
469
  #
838
470
  # PostgreSQL does not allow arbitrary ordering when using DISTINCT ON,
@@ -848,94 +480,6 @@ module ArJdbc
848
480
  sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}"
849
481
  end
850
482
 
851
- # @return [String]
852
- # @override
853
- def quote(value, column = nil)
854
- return super unless column && column.type
855
- return value if sql_literal?(value)
856
-
857
- case value
858
- when Float
859
- if value.infinite? && ( column.type == :datetime || column.type == :timestamp )
860
- "'#{value.to_s.downcase}'"
861
- elsif value.infinite? || value.nan?
862
- "'#{value.to_s}'"
863
- else super
864
- end
865
- when Numeric
866
- if column.respond_to?(:sql_type) && column.sql_type == 'money'
867
- "'#{value}'"
868
- elsif column.type == :string || column.type == :text
869
- "'#{value}'"
870
- else super
871
- end
872
- when String
873
- return "E'#{escape_bytea(value)}'::bytea" if column.type == :binary
874
- return "xml '#{quote_string(value)}'" if column.type == :xml
875
- sql_type = column.respond_to?(:sql_type) && column.sql_type
876
- sql_type && sql_type[0, 3] == 'bit' ? quote_bit(value) : super
877
- when Array
878
- if AR40 && column.array? # will be always falsy in AR < 4.0
879
- "'#{jdbc_column_class.array_to_string(value, column, self).gsub(/'/, "''")}'"
880
- elsif column.type == :json # only in AR-4.0
881
- super(jdbc_column_class.json_to_string(value), column)
882
- elsif column.type == :jsonb # only in AR-4.0
883
- super(jdbc_column_class.json_to_string(value), column)
884
- elsif column.type == :point # only in AR-4.0
885
- super(jdbc_column_class.point_to_string(value), column)
886
- else super
887
- end
888
- when Hash
889
- if column.type == :hstore # only in AR-4.0
890
- super(jdbc_column_class.hstore_to_string(value), column)
891
- elsif column.type == :json # only in AR-4.0
892
- super(jdbc_column_class.json_to_string(value), column)
893
- elsif column.type == :jsonb # only in AR-4.0
894
- super(jdbc_column_class.json_to_string(value), column)
895
- else super
896
- end
897
- when Range
898
- sql_type = column.respond_to?(:sql_type) && column.sql_type
899
- if sql_type && sql_type[-5, 5] == 'range' && AR40
900
- escaped = quote_string(jdbc_column_class.range_to_string(value))
901
- "'#{escaped}'::#{sql_type}"
902
- else super
903
- end
904
- when IPAddr
905
- if column.type == :inet || column.type == :cidr # only in AR-4.0
906
- super(jdbc_column_class.cidr_to_string(value), column)
907
- else super
908
- end
909
- else
910
- super
911
- end
912
- end unless AR42
913
-
914
- # @private
915
- def _quote(value)
916
- case value
917
- when Type::Binary::Data
918
- "E'#{escape_bytea(value.to_s)}'"
919
- when OID::Xml::Data
920
- "xml '#{quote_string(value.to_s)}'"
921
- when OID::Bit::Data
922
- if value.binary?
923
- "B'#{value}'"
924
- elsif value.hex?
925
- "X'#{value}'"
926
- end
927
- when Float
928
- if value.infinite? || value.nan?
929
- "'#{value}'"
930
- else
931
- super
932
- end
933
- else
934
- super
935
- end
936
- end if AR42
937
- private :_quote if AR42
938
-
939
483
  # Quotes a string, escaping any ' (single quote) and \ (backslash) chars.
940
484
  # @return [String]
941
485
  # @override
@@ -947,24 +491,6 @@ module ArJdbc
947
491
  quoted
948
492
  end
949
493
 
950
- # @return [String]
951
- def quote_bit(value)
952
- case value
953
- # NOTE: as reported with #60 this is not quite "right" :
954
- # "0103" will be treated as hexadecimal string
955
- # "0102" will be treated as hexadecimal string
956
- # "0101" will be treated as binary string
957
- # "0100" will be treated as binary string
958
- # ... but is kept due Rails compatibility
959
- when /\A[01]*\Z/ then "B'#{value}'" # Bit-string notation
960
- when /\A[0-9A-F]*\Z/i then "X'#{value}'" # Hexadecimal notation
961
- end
962
- end
963
-
964
- def quote_bit(value)
965
- "B'#{value}'"
966
- end if AR40
967
-
968
494
  def escape_bytea(string)
969
495
  return unless string
970
496
  if supports_hex_escaped_bytea?
@@ -988,285 +514,19 @@ module ArJdbc
988
514
  end
989
515
  end
990
516
 
991
- # @override
992
- def quote_table_name_for_assignment(table, attr)
993
- quote_column_name(attr)
994
- end if AR40
995
-
996
517
  # @override
997
518
  def quote_column_name(name)
998
519
  %("#{name.to_s.gsub("\"", "\"\"")}")
999
520
  end
1000
-
1001
- # @private
1002
- def quote_default_value(value, column)
1003
- # Do not quote function default values for UUID columns
1004
- if column.type == :uuid && value =~ /\(\)/
1005
- value
1006
- else
1007
- quote(value, column)
1008
- end
1009
- end
1010
-
1011
- # Quote date/time values for use in SQL input.
1012
- # Includes microseconds if the value is a Time responding to `usec`.
1013
- # @override
1014
- def quoted_date(value)
1015
- result = super
1016
- if value.acts_like?(:time) && value.respond_to?(:usec) && !AR50
1017
- result = "#{result}.#{sprintf("%06d", value.usec)}"
1018
- end
1019
- result = "#{result.sub(/^-/, '')} BC" if value.year < 0
1020
- result
1021
- end if ::ActiveRecord::VERSION::MAJOR >= 3
1022
-
1023
- # @override
1024
- def supports_disable_referential_integrity?
1025
- true
1026
- end
1027
-
1028
- def disable_referential_integrity
1029
- if supports_disable_referential_integrity?
1030
- begin
1031
- execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
1032
- rescue
1033
- execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER USER" }.join(";"))
1034
- end
1035
- end
1036
- yield
1037
- ensure
1038
- if supports_disable_referential_integrity?
1039
- begin
1040
- execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
1041
- rescue
1042
- execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER USER" }.join(";"))
1043
- end
1044
- end
1045
- end
1046
-
1047
- def rename_table(table_name, new_name)
1048
- execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
1049
- pk, seq = pk_and_sequence_for(new_name)
1050
- if seq == "#{table_name}_#{pk}_seq"
1051
- new_seq = "#{new_name}_#{pk}_seq"
1052
- idx = "#{table_name}_pkey"
1053
- new_idx = "#{new_name}_pkey"
1054
- execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
1055
- execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
1056
- end
1057
- rename_table_indexes(table_name, new_name) if respond_to?(:rename_table_indexes) # AR-4.0 SchemaStatements
1058
- end
1059
-
1060
- # Adds a new column to the named table.
1061
- # See TableDefinition#column for details of the options you can use.
1062
- def add_column(table_name, column_name, type, options = {})
1063
- default = options[:default]
1064
- notnull = options[:null] == false
1065
-
1066
- sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale])
1067
- sql_type << "[]" if options[:array]
1068
-
1069
- # Add the column.
1070
- execute("ALTER TABLE #{quote_table_name(table_name)} ADD COLUMN #{quote_column_name(column_name)} #{sql_type}")
1071
-
1072
- change_column_default(table_name, column_name, default) if options_include_default?(options)
1073
- change_column_null(table_name, column_name, false, default) if notnull
1074
- end if ::ActiveRecord::VERSION::MAJOR < 4
1075
-
1076
- # @private documented above
1077
- def add_column(table_name, column_name, type, options = {}); super end if AR42
1078
-
1079
- # Changes the column of a table.
1080
- def change_column(table_name, column_name, type, options = {})
1081
- quoted_table_name = quote_table_name(table_name)
1082
- quoted_column_name = quote_table_name(column_name)
1083
-
1084
- sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale])
1085
- sql_type << "[]" if options[:array]
1086
-
1087
- sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}"
1088
- sql << " USING #{options[:using]}" if options[:using]
1089
- if options[:cast_as]
1090
- sql << " USING CAST(#{quoted_column_name} AS #{type_to_sql(options[:cast_as], options[:limit], options[:precision], options[:scale])})"
1091
- end
1092
- begin
1093
- execute sql
1094
- rescue ActiveRecord::StatementInvalid => e
1095
- raise e if postgresql_version > 80000
1096
- change_column_pg7(table_name, column_name, type, options)
1097
- end
1098
-
1099
- change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
1100
- change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
1101
- end # unless const_defined? :SchemaCreation
1102
-
1103
- def change_column_pg7(table_name, column_name, type, options)
1104
- quoted_table_name = quote_table_name(table_name)
1105
- # This is PostgreSQL 7.x, so we have to use a more arcane way of doing it.
1106
- begin
1107
- begin_db_transaction
1108
- tmp_column_name = "#{column_name}_ar_tmp"
1109
- add_column(table_name, tmp_column_name, type, options)
1110
- execute "UPDATE #{quoted_table_name} SET #{quote_column_name(tmp_column_name)} = CAST(#{quote_column_name(column_name)} AS #{sql_type})"
1111
- remove_column(table_name, column_name)
1112
- rename_column(table_name, tmp_column_name, column_name)
1113
- commit_db_transaction
1114
- rescue
1115
- rollback_db_transaction
1116
- end
1117
- end
1118
- private :change_column_pg7
1119
-
1120
- # Changes the default value of a table column.
1121
- def change_column_default(table_name, column_name, default)
1122
- if column = column_for(table_name, column_name) # (backwards) compatible with AR 3.x - 4.x
1123
- execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote_default_value(default, column)}"
1124
- else
1125
- execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}"
1126
- end
1127
- end unless AR42 # unless const_defined? :SchemaCreation
1128
-
1129
- # @private documented above
1130
- def change_column_default(table_name, column_name, default)
1131
- return unless column = column_for(table_name, column_name)
1132
-
1133
- alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s"
1134
- if default.nil?
1135
- # <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
1136
- # cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
1137
- execute alter_column_query % "DROP DEFAULT"
1138
- else
1139
- execute alter_column_query % "SET DEFAULT #{quote_default_value(default, column)}"
1140
- end
1141
- end if AR42
1142
-
1143
- # @private
1144
- def change_column_null(table_name, column_name, null, default = nil)
1145
- unless null || default.nil?
1146
- if column = column_for(table_name, column_name) # (backwards) compatible with AR 3.x - 4.x
1147
- execute "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_value(default, column)} WHERE #{quote_column_name(column_name)} IS NULL"
1148
- else
1149
- execute "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL"
1150
- end
1151
- end
1152
- execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
1153
- end unless AR42 # unless const_defined? :SchemaCreation
1154
-
1155
- # @private
1156
- def change_column_null(table_name, column_name, null, default = nil)
1157
- unless null || default.nil?
1158
- column = column_for(table_name, column_name)
1159
- execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_value(default, column)} WHERE #{quote_column_name(column_name)} IS NULL") if column
1160
- end
1161
- execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
1162
- end if AR42
1163
-
1164
- def rename_column(table_name, column_name, new_column_name)
1165
- execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
1166
- rename_column_indexes(table_name, column_name, new_column_name) if respond_to?(:rename_column_indexes) # AR-4.0 SchemaStatements
1167
- end # unless const_defined? :SchemaCreation
1168
-
1169
- def add_index(table_name, column_name, options = {})
1170
- index_name, index_type, index_columns, index_options, index_algorithm, index_using = add_index_options(table_name, column_name, options)
1171
- execute "CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns})#{index_options}"
1172
- end if AR40
521
+ alias_method :quote_schema_name, :quote_column_name
1173
522
 
1174
523
  def remove_index!(table_name, index_name)
1175
524
  execute "DROP INDEX #{quote_table_name(index_name)}"
1176
525
  end
1177
526
 
1178
- def rename_index(table_name, old_name, new_name)
1179
- validate_index_length!(table_name, new_name) if respond_to?(:validate_index_length!)
1180
-
1181
- execute "ALTER INDEX #{quote_column_name(old_name)} RENAME TO #{quote_table_name(new_name)}"
1182
- end
1183
-
1184
527
  # @override
1185
528
  def supports_foreign_keys?; true end
1186
529
 
1187
- def foreign_keys(table_name)
1188
- fk_info = select_all "" <<
1189
- "SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete " <<
1190
- "FROM pg_constraint c " <<
1191
- "JOIN pg_class t1 ON c.conrelid = t1.oid " <<
1192
- "JOIN pg_class t2 ON c.confrelid = t2.oid " <<
1193
- "JOIN pg_attribute a1 ON a1.attnum = c.conkey[1] AND a1.attrelid = t1.oid " <<
1194
- "JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid " <<
1195
- "JOIN pg_namespace t3 ON c.connamespace = t3.oid " <<
1196
- "WHERE c.contype = 'f' " <<
1197
- " AND t1.relname = #{quote(table_name)} " <<
1198
- " AND t3.nspname = ANY (current_schemas(false)) " <<
1199
- "ORDER BY c.conname "
1200
-
1201
- fk_info.map! do |row|
1202
- options = {
1203
- :column => row['column'], :name => row['name'], :primary_key => row['primary_key']
1204
- }
1205
- options[:on_delete] = extract_foreign_key_action(row['on_delete'])
1206
- options[:on_update] = extract_foreign_key_action(row['on_update'])
1207
-
1208
- ForeignKeyDefinition.new(table_name, row['to_table'], options)
1209
- end
1210
- end if defined? ForeignKeyDefinition
1211
-
1212
- # @private
1213
- def extract_foreign_key_action(specifier)
1214
- case specifier
1215
- when 'c'; :cascade
1216
- when 'n'; :nullify
1217
- when 'r'; :restrict
1218
- end
1219
- end
1220
- private :extract_foreign_key_action
1221
-
1222
- def index_name_length
1223
- 63
1224
- end
1225
-
1226
- # Returns the list of all column definitions for a table.
1227
- def columns(table_name, name = nil)
1228
- column = jdbc_column_class
1229
- column_definitions(table_name).map! do |row|
1230
- # |name, type, default, notnull, oid, fmod|
1231
- name = row[0]; type = row[1]; default = row[2]
1232
- notnull = row[3]; oid = row[4]; fmod = row[5]
1233
- # oid = OID::TYPE_MAP.fetch(oid.to_i, fmod.to_i) { OID::Identity.new }
1234
- notnull = notnull == 't' if notnull.is_a?(String) # JDBC gets true/false
1235
- # for ID columns we get a bit of non-sense default :
1236
- # e.g. "nextval('mixed_cases_id_seq'::regclass"
1237
- if default =~ /^nextval\(.*?\:\:regclass\)$/
1238
- default = nil
1239
- elsif default =~ /^\(([-+]?[\d\.]+)\)$/ # e.g. "(-1)" for a negative default
1240
- default = $1
1241
- end
1242
-
1243
- column.new(name, default, oid, type, ! notnull, fmod, self)
1244
- end
1245
- end
1246
-
1247
- # @private documented above
1248
- def columns(table_name)
1249
- column = jdbc_column_class
1250
- # Limit, precision, and scale are all handled by the superclass.
1251
- column_definitions(table_name).map! do |row|
1252
- # |name, type, default, notnull, oid, fmod|
1253
- name = row[0]; type = row[1]; default = row[2]
1254
- notnull = row[3]; oid = row[4]; fmod = row[5]
1255
- notnull = notnull == 't' if notnull.is_a?(String) # JDBC gets true/false
1256
-
1257
- oid_type = get_oid_type(oid.to_i, fmod.to_i, name, type)
1258
- default_value = extract_value_from_default(oid, default)
1259
- default_function = extract_default_function(default_value, default)
1260
-
1261
- column.new(name, default_value, oid_type, type, ! notnull, default_function, oid, self)
1262
- end
1263
- end if AR42
1264
-
1265
- # @private only for API compatibility
1266
- def new_column(name, default, cast_type, sql_type = nil, null = true, default_function = nil)
1267
- jdbc_column_class.new(name, default, cast_type, sql_type, null, default_function)
1268
- end if AR42
1269
-
1270
530
  # @private
1271
531
  def column_for(table_name, column_name)
1272
532
  column_name = column_name.to_s
@@ -1285,139 +545,90 @@ module ArJdbc
1285
545
  # - format_type includes the column size constraint, e.g. varchar(50)
1286
546
  # - ::regclass is a function that gives the id for a table name
1287
547
  def column_definitions(table_name)
1288
- select_rows(<<-end_sql, 'SCHEMA')
548
+ rows = select_rows(<<-end_sql, 'SCHEMA')
1289
549
  SELECT a.attname, format_type(a.atttypid, a.atttypmod),
1290
- pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
1291
- FROM pg_attribute a LEFT JOIN pg_attrdef d
1292
- ON a.attrelid = d.adrelid AND a.attnum = d.adnum
550
+ pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
551
+ (SELECT c.collname FROM pg_collation c, pg_type t
552
+ WHERE c.oid = a.attcollation AND t.oid = a.atttypid
553
+ AND a.attcollation <> t.typcollation),
554
+ col_description(a.attrelid, a.attnum) AS comment
555
+ FROM pg_attribute a
556
+ LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
1293
557
  WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
1294
558
  AND a.attnum > 0 AND NOT a.attisdropped
1295
559
  ORDER BY a.attnum
1296
560
  end_sql
1297
- end
1298
- private :column_definitions
1299
-
1300
- # @private
1301
- TABLES_SQL = 'SELECT tablename FROM pg_tables WHERE schemaname = ANY (current_schemas(false))'
1302
- private_constant :TABLES_SQL rescue nil
1303
-
1304
- # @override
1305
- def tables(name = nil)
1306
- select_values(TABLES_SQL, 'SCHEMA')
1307
- end
1308
561
 
1309
- # @private
1310
- TABLE_EXISTS_SQL_PREFIX = 'SELECT COUNT(*) as table_count FROM pg_class c'
1311
- TABLE_EXISTS_SQL_PREFIX << ' LEFT JOIN pg_namespace n ON n.oid = c.relnamespace'
1312
- if AR42 # -- (r)elation/table, (v)iew, (m)aterialized view
1313
- TABLE_EXISTS_SQL_PREFIX << " WHERE c.relkind IN ('r','v','m')"
1314
- else
1315
- TABLE_EXISTS_SQL_PREFIX << " WHERE c.relkind IN ('r','v')"
1316
- end
1317
- TABLE_EXISTS_SQL_PREFIX << " AND c.relname = ?"
1318
- private_constant :TABLE_EXISTS_SQL_PREFIX rescue nil
1319
-
1320
- # Returns true if table exists.
1321
- # If the schema is not specified as part of +name+ then it will only find tables within
1322
- # the current schema search path (regardless of permissions to access tables in other schemas)
1323
- def table_exists?(name)
1324
- schema, table = extract_schema_and_table(name.to_s)
1325
- return false unless table
1326
-
1327
- binds = [[nil, table]]
1328
- binds << [nil, schema] if schema
1329
-
1330
- sql = "#{TABLE_EXISTS_SQL_PREFIX} AND n.nspname = #{schema ? "?" : 'ANY (current_schemas(false))'}"
1331
-
1332
- log(sql, 'SCHEMA', binds) do
1333
- @connection.execute_query_raw(sql, binds).first['table_count'] > 0
562
+ # Force the notnull attribute to a boolean
563
+ rows.each do |row|
564
+ row[3] = row[3] == 't' if row[3].is_a?(String)
1334
565
  end
1335
566
  end
1336
- alias data_source_exists? table_exists?
1337
-
1338
- # @private
1339
- DATA_SOURCES_SQL = 'SELECT c.relname FROM pg_class c'
1340
- DATA_SOURCES_SQL << ' LEFT JOIN pg_namespace n ON n.oid = c.relnamespace'
1341
- DATA_SOURCES_SQL << " WHERE c.relkind IN ('r', 'v','m')" # -- (r)elation/table, (v)iew, (m)aterialized view
1342
- DATA_SOURCES_SQL << ' AND n.nspname = ANY (current_schemas(false))'
1343
- private_constant :DATA_SOURCES_SQL rescue nil
1344
-
1345
- # @override
1346
- def data_sources
1347
- select_values(DATA_SOURCES_SQL, 'SCHEMA')
1348
- end
1349
-
1350
- def drop_table(table_name, options = {})
1351
- execute "DROP TABLE #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
1352
- end
567
+ private :column_definitions
1353
568
 
1354
569
  def truncate(table_name, name = nil)
1355
570
  execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
1356
571
  end
1357
572
 
1358
- def index_name_exists?(table_name, index_name, default)
1359
- exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
1360
- SELECT COUNT(*)
1361
- FROM pg_class t
1362
- INNER JOIN pg_index d ON t.oid = d.indrelid
1363
- INNER JOIN pg_class i ON d.indexrelid = i.oid
1364
- WHERE i.relkind = 'i'
1365
- AND i.relname = '#{index_name}'
1366
- AND t.relname = '#{table_name}'
1367
- AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
1368
- SQL
1369
- end if AR42
1370
-
1371
573
  # Returns an array of indexes for the given table.
1372
574
  def indexes(table_name, name = nil)
1373
- # NOTE: maybe it's better to leave things of to the JDBC API ?!
1374
- result = select_rows(<<-SQL, 'SCHEMA')
1375
- SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
1376
- FROM pg_class t
1377
- INNER JOIN pg_index d ON t.oid = d.indrelid
1378
- INNER JOIN pg_class i ON d.indexrelid = i.oid
1379
- WHERE i.relkind = 'i'
1380
- AND d.indisprimary = 'f'
1381
- AND t.relname = '#{table_name}'
1382
- AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
1383
- ORDER BY i.relname
575
+ if name
576
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
577
+ Passing name to #indexes is deprecated without replacement.
578
+ MSG
579
+ end
580
+
581
+ # FIXME: AR version => table = Utils.extract_schema_qualified_name(table_name.to_s)
582
+ schema, table = extract_schema_and_table(table_name.to_s)
583
+
584
+ result = query(<<-SQL, 'SCHEMA')
585
+ SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
586
+ pg_catalog.obj_description(i.oid, 'pg_class') AS comment,
587
+ (SELECT COUNT(*) FROM pg_opclass o
588
+ JOIN (SELECT unnest(string_to_array(d.indclass::text, ' '))::int oid) c
589
+ ON o.oid = c.oid WHERE o.opcdefault = 'f')
590
+ FROM pg_class t
591
+ INNER JOIN pg_index d ON t.oid = d.indrelid
592
+ INNER JOIN pg_class i ON d.indexrelid = i.oid
593
+ LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
594
+ WHERE i.relkind = 'i'
595
+ AND d.indisprimary = 'f'
596
+ AND t.relname = '#{table}'
597
+ AND n.nspname = #{schema ? "'#{schema}'" : 'ANY (current_schemas(false))'}
598
+ ORDER BY i.relname
1384
599
  SQL
1385
600
 
1386
- result.map! do |row|
601
+ result.map do |row|
1387
602
  index_name = row[0]
603
+ # FIXME: These values [1,2] are returned in a different format than AR expects, maybe we could update it on the Java side to be more accurate
1388
604
  unique = row[1].is_a?(String) ? row[1] == 't' : row[1] # JDBC gets us a boolean
1389
605
  indkey = row[2].is_a?(Java::OrgPostgresqlUtil::PGobject) ? row[2].value : row[2]
1390
- indkey = indkey.split(" ")
606
+ indkey = indkey.split(" ").map(&:to_i)
1391
607
  inddef = row[3]
1392
608
  oid = row[4]
609
+ comment = row[5]
610
+ opclass = row[6]
1393
611
 
1394
- columns = select_rows(<<-SQL, "SCHEMA")
1395
- SELECT a.attnum, a.attname
1396
- FROM pg_attribute a
1397
- WHERE a.attrelid = #{oid}
1398
- AND a.attnum IN (#{indkey.join(",")})
1399
- SQL
612
+ using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/).flatten
1400
613
 
1401
- columns = Hash[ columns.each { |column| column[0] = column[0].to_s } ]
1402
- column_names = columns.values_at(*indkey).compact
614
+ if indkey.include?(0) || opclass > 0
615
+ columns = expressions
616
+ else
617
+ columns = Hash[query(<<-SQL.strip_heredoc, "SCHEMA")].values_at(*indkey).compact
618
+ SELECT a.attnum, a.attname
619
+ FROM pg_attribute a
620
+ WHERE a.attrelid = #{oid}
621
+ AND a.attnum IN (#{indkey.join(",")})
622
+ SQL
1403
623
 
1404
- unless column_names.empty?
1405
624
  # add info on sort order for columns (only desc order is explicitly specified, asc is the default)
1406
- desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
1407
- orders = desc_order_columns.any? ? Hash[ desc_order_columns.map { |column| [column, :desc] } ] : {}
1408
-
1409
- if ::ActiveRecord::VERSION::MAJOR > 3 # AR4 supports `where` and `using` index options
1410
- where = inddef.scan(/WHERE (.+)$/).flatten[0]
1411
- using = inddef.scan(/USING (.+?) /).flatten[0].to_sym
1412
-
1413
- IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where, nil, using)
1414
- else
1415
- new_index_definition(table_name, index_name, unique, column_names, [], orders)
1416
- end
625
+ orders = Hash[
626
+ expressions.scan(/(\w+) DESC/).flatten.map { |order_column| [order_column, :desc] }
627
+ ]
1417
628
  end
1418
- end
1419
- result.compact!
1420
- result
629
+
630
+ IndexDefinition.new(table_name, index_name, unique, columns, [], orders, where, nil, using.to_sym, comment.presence)
631
+ end.compact
1421
632
  end
1422
633
 
1423
634
  # @private
@@ -1428,16 +639,48 @@ module ArJdbc
1428
639
  when 'average' then 'avg'
1429
640
  else operation.downcase
1430
641
  end
1431
- end if AR42
642
+ end
1432
643
 
1433
644
  private
1434
645
 
646
+ # Pulled from ActiveRecord's Postgres adapter and modified to use execute
647
+ def can_perform_case_insensitive_comparison_for?(column)
648
+ @case_insensitive_cache ||= {}
649
+ @case_insensitive_cache[column.sql_type] ||= begin
650
+ sql = <<-end_sql
651
+ SELECT exists(
652
+ SELECT * FROM pg_proc
653
+ WHERE proname = 'lower'
654
+ AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
655
+ ) OR exists(
656
+ SELECT * FROM pg_proc
657
+ INNER JOIN pg_cast
658
+ ON ARRAY[casttarget]::oidvector = proargtypes
659
+ WHERE proname = 'lower'
660
+ AND castsource = #{quote column.sql_type}::regtype
661
+ )
662
+ end_sql
663
+ select_rows(sql, 'SCHEMA').first.first == 't'
664
+ end
665
+ end
666
+
1435
667
  def translate_exception(exception, message)
668
+ # TODO: Can we base these on an error code of some kind?
1436
669
  case exception.message
1437
670
  when /duplicate key value violates unique constraint/
1438
- ::ActiveRecord::RecordNotUnique.new(message, exception)
671
+ ::ActiveRecord::RecordNotUnique.new(message)
672
+ when /violates not-null constraint/
673
+ ::ActiveRecord::NotNullViolation.new(message)
1439
674
  when /violates foreign key constraint/
1440
- ::ActiveRecord::InvalidForeignKey.new(message, exception)
675
+ ::ActiveRecord::InvalidForeignKey.new(message)
676
+ when /value too long/
677
+ ::ActiveRecord::ValueTooLong.new(message)
678
+ when /out of range/
679
+ ::ActiveRecord::RangeError.new(message)
680
+ when /could not serialize/
681
+ ::ActiveRecord::SerializationFailure.new(message)
682
+ when /deadlock detected/
683
+ ::ActiveRecord::Deadlocked.new(message)
1441
684
  else
1442
685
  super
1443
686
  end
@@ -1477,30 +720,37 @@ require 'arjdbc/util/quoted_cache'
1477
720
 
1478
721
  module ActiveRecord::ConnectionAdapters
1479
722
 
1480
- remove_const(:PostgreSQLColumn) if const_defined?(:PostgreSQLColumn)
1481
-
1482
- class PostgreSQLColumn < JdbcColumn
1483
- include ::ArJdbc::PostgreSQL::Column
1484
- end
1485
-
1486
723
  # NOTE: seems needed on 4.x due loading of '.../postgresql/oid' which
1487
724
  # assumes: class PostgreSQLAdapter < AbstractAdapter
1488
725
  remove_const(:PostgreSQLAdapter) if const_defined?(:PostgreSQLAdapter)
1489
726
 
1490
- class PostgreSQLAdapter < JdbcAdapter
1491
- include ::ArJdbc::PostgreSQL
1492
- include ::ArJdbc::PostgreSQL::ExplainSupport
727
+ class PostgreSQLAdapter < AbstractAdapter
1493
728
 
1494
- require 'arjdbc/postgresql/oid_types' if ::ArJdbc::AR40
1495
- include ::ArJdbc::PostgreSQL::OIDTypes if ::ArJdbc::PostgreSQL.const_defined?(:OIDTypes)
729
+ # Try to use as much of the built in postgres logic as possible
730
+ # maybe someday we can extend the actual adapter
731
+ include ActiveRecord::ConnectionAdapters::PostgreSQL::ColumnDumper
732
+ include ActiveRecord::ConnectionAdapters::PostgreSQL::ReferentialIntegrity
733
+ include ActiveRecord::ConnectionAdapters::PostgreSQL::SchemaStatements
734
+ include ActiveRecord::ConnectionAdapters::PostgreSQL::Quoting
1496
735
 
1497
- load 'arjdbc/postgresql/_bc_time_cast_patch.rb' if ::ArJdbc::AR42
736
+ include ArJdbc::Abstract::Core
737
+ include ArJdbc::Abstract::ConnectionManagement
738
+ include ArJdbc::Abstract::DatabaseStatements
739
+ include ArJdbc::Abstract::StatementCache
740
+ include ArJdbc::Abstract::TransactionSupport
741
+ include ArJdbc::PostgreSQL
1498
742
 
1499
- include ::ArJdbc::PostgreSQL::ColumnHelpers if ::ArJdbc::AR42
743
+ require 'arjdbc/postgresql/oid_types'
744
+ include ::ArJdbc::PostgreSQL::OIDTypes
745
+
746
+ include ::ArJdbc::PostgreSQL::ColumnHelpers
1500
747
 
1501
748
  include ::ArJdbc::Util::QuotedCache
1502
749
 
1503
- def initialize(*args)
750
+ # AR expects OID to be available on the adapter
751
+ OID = ActiveRecord::ConnectionAdapters::PostgreSQL::OID
752
+
753
+ def initialize(connection, logger = nil, config = {})
1504
754
  # @local_tz is initialized as nil to avoid warnings when connect tries to use it
1505
755
  @local_tz = nil
1506
756
 
@@ -1508,7 +758,7 @@ module ActiveRecord::ConnectionAdapters
1508
758
 
1509
759
  @table_alias_length = nil
1510
760
 
1511
- initialize_type_map(@type_map = Type::HashLookupTypeMap.new) if ::ArJdbc::AR42
761
+ initialize_type_map(@type_map = Type::HashLookupTypeMap.new)
1512
762
 
1513
763
  @use_insert_returning = @config.key?(:insert_returning) ?
1514
764
  self.class.type_cast_config_to_boolean(@config[:insert_returning]) : nil
@@ -1518,34 +768,49 @@ module ActiveRecord::ConnectionAdapters
1518
768
  Arel::Visitors::PostgreSQL.new(self)
1519
769
  end
1520
770
 
1521
- if ::ArJdbc::AR42
1522
- require 'active_record/connection_adapters/postgresql/schema_definitions'
1523
- else
1524
- require 'arjdbc/postgresql/base/schema_definitions'
1525
- end
1526
-
1527
- ColumnDefinition = ActiveRecord::ConnectionAdapters::PostgreSQL::ColumnDefinition
771
+ require 'active_record/connection_adapters/postgresql/schema_definitions'
1528
772
 
1529
773
  ColumnMethods = ActiveRecord::ConnectionAdapters::PostgreSQL::ColumnMethods
1530
774
  TableDefinition = ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition
775
+ Table = ActiveRecord::ConnectionAdapters::PostgreSQL::Table
1531
776
 
1532
- def table_definition(*args)
1533
- new_table_definition(TableDefinition, *args)
777
+ def create_table_definition(*args) # :nodoc:
778
+ TableDefinition.new(*args)
1534
779
  end
1535
780
 
1536
- Table = ActiveRecord::ConnectionAdapters::PostgreSQL::Table
781
+ def exec_query(sql, name = nil, binds = [], prepare: false)
782
+ super
783
+ rescue ActiveRecord::StatementInvalid => e
784
+ raise unless e.cause.message.include?('cached plan must not change result type'.freeze)
785
+
786
+ if open_transactions > 0
787
+ # In a transaction, have to fail it - See AR code for details
788
+ raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
789
+ else
790
+ # Not in a transaction, clear the prepared statement and try again
791
+ delete_cached_statement(sql)
792
+ retry
793
+ end
794
+ end
795
+
796
+ def schema_creation # :nodoc:
797
+ PostgreSQL::SchemaCreation.new self
798
+ end
1537
799
 
1538
800
  def update_table_definition(table_name, base)
1539
801
  Table.new(table_name, base)
1540
- end if ::ActiveRecord::VERSION::MAJOR > 3
802
+ end
1541
803
 
1542
804
  def jdbc_connection_class(spec)
1543
805
  ::ArJdbc::PostgreSQL.jdbc_connection_class
1544
806
  end
1545
807
 
1546
- if ::ActiveRecord::VERSION::MAJOR < 4 # Rails 3.x compatibility
1547
- PostgreSQLJdbcConnection.raw_array_type = true if PostgreSQLJdbcConnection.raw_array_type? == nil
1548
- PostgreSQLJdbcConnection.raw_hstore_type = true if PostgreSQLJdbcConnection.raw_hstore_type? == nil
808
+ private
809
+
810
+ # Prepared statements aren't schema aware so we need to make sure we
811
+ # store different PreparedStatement objects for different schemas
812
+ def cached_statement_key(sql)
813
+ "#{schema_search_path}-#{sql}"
1549
814
  end
1550
815
 
1551
816
  end