activerecord-jdbc-adapter 5.0.pre1 → 51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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