activerecord-jdbc-adapter 1.3.16 → 1.3.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7cf55d725c96e138c32eea755df27374aa764038
4
- data.tar.gz: 9f5e8cf34fc6099b8958ef3de9bacffaf746dc6b
3
+ metadata.gz: 134cda5c81428ddd6306cfbf8de01390845e9332
4
+ data.tar.gz: 5b4c2e9e1f691ab3cd0c8df0ab77a06a933bb433
5
5
  SHA512:
6
- metadata.gz: d75af524f18547b955e6a5b00964536223b382d052b69a7fbce062ba142a5bcb85ccf7ba3918838653756c0e582cbf62852568f3239399b7e00efe749ec7a8b7
7
- data.tar.gz: 4f2e9693c5d1eb81f69741e0b1e8d06911d05273564a67910a77da073141609d5c81d1b751c715c2de7adb79956622b36e216493bcb40649bb2157bebcee3d8a
6
+ metadata.gz: bb750f2f8396290b6212c017350c1b9c2f38792122a2214e1c14a2e83e22f35e6d0dab9c808f384421a64d3c11c691d94e2909e5548ccafbc523c7c066cdd7f1
7
+ data.tar.gz: 3cc35229994ff2f461fe35ff5e381c461d41d91ab80e51a8e89b054494f21283a9b897488cab748a1efe076abc3f86316939644a1dcfa2a22029a49c409fa800
data/.gitignore CHANGED
@@ -29,4 +29,5 @@ jndi_test/jdbc/.bindings
29
29
  .bundle/config
30
30
  gemfiles/.bundle/config
31
31
  Gemfile.lock
32
- /.idea
32
+ .idea
33
+ .settings
data/History.md CHANGED
@@ -1,3 +1,23 @@
1
+ ## 1.3.17 (06/30/15)
2
+
3
+ - [mysql] bulk compatibility with AR 4.2 - all _sql helper accept an options (hash)
4
+ - [mysql] back-port case sensitive/insensitive comparison
5
+ - [mysql] handle precision for datetimes when doing type_to_sql (AR 4.2 compat)
6
+ - [mysql] foreign key support based on AR 4.2 (#656)
7
+ - [postgres] foreign_key support back-ported from AR 4.2 (#656)
8
+ - moved jndi callbacks to happen outside adapter's `initialize` (#649, #620)
9
+ - [postgres] AR 4.2 compatibility with SchemaCreation
10
+ - [postgres] re-define `lookup_cast_type` (AR 4.2) + `conn.type_map` is public
11
+ - [postgres] override `valid_type?(type)` (AR 4.2 compatibility)
12
+ - [postgres] back-port change_column updates from AR 4.2 for compatibility
13
+ - [postgres] pass down array_member to hstore_to_string (fixes #647 on AR 4.1)
14
+ - [postgres] on AR 4.2 use core's schema_definitions.rb for improved compat (#644)
15
+ - back-port PGconn hack on AR 4.x (from #655 and following commits)
16
+ to fix #651 as an unfortunate work-around for (non-shareable) AR internals (#652)
17
+ - seems that extension methods won't work in 9K the same as in 1.7.x thus work-around
18
+ (originally reported as https://github.com/jruby/jruby/issues/2533)
19
+ - restore 1.8 compatibility in postgres' adapter (regression in 1.3.16 see #641)
20
+
1
21
  ## 1.3.16 (04/13/15)
2
22
 
3
23
  - [h2] allow jdbch2 adapter to use jdbc-h2 1.4 (#639)
data/Rakefile CHANGED
@@ -12,7 +12,6 @@ require 'appraisal'
12
12
 
13
13
  task :default => [:jar, :test]
14
14
 
15
- #ugh, bundler doesn't use tasks, so gotta hook up to both tasks.
16
15
  task :build => :jar
17
16
  task :install => :jar
18
17
 
@@ -21,13 +20,27 @@ DRIVERS = %w[derby h2 hsqldb jtds mysql postgres sqlite3].map { |a| "jdbc-#{a}"
21
20
  TARGETS = ( ADAPTERS + DRIVERS )
22
21
 
23
22
  rake = lambda { |task| ruby "-S", "rake", task }
23
+ current_version = lambda { Bundler.load_gemspec('activerecord-jdbc-adapter.gemspec').version }
24
24
 
25
- TARGETS.each do |target|
25
+ ADAPTERS.each do |target|
26
+ namespace target do
27
+ task :build do
28
+ version = current_version.call
29
+ Dir.chdir(target) { rake.call "build" }
30
+ cp FileList["#{target}/pkg/#{target}-#{version}.gem"], "pkg"
31
+ end
32
+ end
33
+ end
34
+ DRIVERS.each do |target|
26
35
  namespace target do
27
36
  task :build do
28
37
  Dir.chdir(target) { rake.call "build" }
29
38
  cp FileList["#{target}/pkg/#{target}-*.gem"], "pkg"
30
39
  end
40
+ end
41
+ end
42
+ TARGETS.each do |target|
43
+ namespace target do
31
44
  task :install do
32
45
  Dir.chdir(target) { rake.call "install" }
33
46
  end
@@ -66,15 +79,16 @@ task "release:adapters" => [ 'release' ] + ADAPTERS.map { |name| "#{name}:releas
66
79
  task "adapters:release" => 'release:adapters'
67
80
 
68
81
  task 'release:do' => 'build:adapters' do
69
- gemspec = Bundler.load_gemspec('activerecord-jdbc-adapter.gemspec')
70
- version = gemspec.version; version_tag = "v#{version}"
82
+ version = current_version.call; version_tag = "v#{version}"
71
83
 
72
84
  sh("git diff --no-patch --exit-code") { |ok| fail "git working dir is not clean" unless ok }
73
85
  sh("git diff-index --quiet --cached HEAD") { |ok| fail "git index is not clean" unless ok }
74
86
 
75
87
  sh "git tag -a -m \"AR-JDBC #{version}\" #{version_tag}"
88
+ branch = `git rev-parse --abbrev-ref HEAD`.strip
89
+ puts "releasing from (current) branch #{branch.inspect}"
76
90
  sh "for gem in `ls pkg/*-#{version}.gem`; do gem push $gem; done" do |ok|
77
- sh "git push origin master --tags" if ok
91
+ sh "git push origin #{branch} --tags" if ok
78
92
  end
79
93
  end
80
94
 
@@ -2,6 +2,11 @@ require 'active_support/deprecation'
2
2
 
3
3
  module ArJdbc
4
4
 
5
+ # @private
6
+ AR40 = ::ActiveRecord::VERSION::MAJOR > 3
7
+ # @private
8
+ AR42 = ::ActiveRecord::VERSION::STRING >= '4.2'
9
+
5
10
  class << self
6
11
 
7
12
  def warn(message, once = nil)
@@ -40,6 +40,12 @@ module ActiveRecord
40
40
 
41
41
  attr_reader :config
42
42
 
43
+ def self.new(connection, logger = nil, pool = nil)
44
+ adapter = super
45
+ Jdbc::JndiConnectionPoolCallbacks.prepare(adapter, adapter.instance_variable_get(:@connection))
46
+ adapter
47
+ end
48
+
43
49
  # Initializes the (JDBC connection) adapter instance.
44
50
  # The passed configuration Hash's keys are symbolized, thus changes to
45
51
  # the original `config` keys won't be reflected in the adapter.
@@ -50,6 +56,8 @@ module ActiveRecord
50
56
  # @param config the database configuration
51
57
  # @note `initialize(logger, config)` with 2 arguments is supported as well
52
58
  def initialize(connection, logger, config = nil)
59
+ # AR : initialize(connection, logger = nil, pool = nil)
60
+ # AR < 3.2 : initialize(connection, logger = nil)
53
61
  if config.nil? && logger.respond_to?(:key?) # (logger, config)
54
62
  config, logger, connection = logger, connection, nil
55
63
  end
@@ -76,8 +84,6 @@ module ActiveRecord
76
84
  # NOTE: should not be necessary for JNDI due reconnect! on checkout :
77
85
  configure_connection if respond_to?(:configure_connection)
78
86
 
79
- Jdbc::JndiConnectionPoolCallbacks.prepare(self, connection)
80
-
81
87
  @visitor = new_visitor # nil if no AREL (AR-2.3)
82
88
  end
83
89
 
@@ -216,6 +222,11 @@ module ActiveRecord
216
222
  end
217
223
  end
218
224
 
225
+ # @override introduced in AR 4.2
226
+ def valid_type?(type)
227
+ ! native_database_types[type].nil?
228
+ end
229
+
219
230
  # Allows for modification of the detected native types.
220
231
  # @param types the resolved native database types
221
232
  # @see #native_database_types
@@ -6,11 +6,6 @@ require 'active_record/connection_adapters/abstract/schema_definitions'
6
6
  module ArJdbc
7
7
  module MySQL
8
8
 
9
- # @private
10
- AR40 = ::ActiveRecord::VERSION::MAJOR > 3
11
- # @private
12
- AR42 = ::ActiveRecord::VERSION::STRING >= '4.2'
13
-
14
9
  require 'arjdbc/mysql/column'
15
10
  require 'arjdbc/mysql/bulk_change_table'
16
11
  require 'arjdbc/mysql/explain_support'
@@ -154,13 +149,29 @@ module ArJdbc
154
149
 
155
150
  def case_sensitive_modifier(node)
156
151
  Arel::Nodes::Bin.new(node)
157
- end
152
+ end unless AR42
158
153
 
159
154
  def case_sensitive_modifier(node, table_attribute)
160
155
  node = Arel::Nodes.build_quoted node, table_attribute
161
156
  Arel::Nodes::Bin.new(node)
162
157
  end if AR42
163
158
 
159
+ def case_sensitive_comparison(table, attribute, column, value)
160
+ if column.case_sensitive?
161
+ table[attribute].eq(value)
162
+ else
163
+ super
164
+ end
165
+ end if AR42
166
+
167
+ def case_insensitive_comparison(table, attribute, column, value)
168
+ if column.case_sensitive?
169
+ super
170
+ else
171
+ table[attribute].eq(value)
172
+ end
173
+ end if AR42
174
+
164
175
  def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
165
176
  where_sql
166
177
  end
@@ -532,12 +543,52 @@ module ArJdbc
532
543
  # @override
533
544
  def rename_index(table_name, old_name, new_name)
534
545
  if supports_rename_index?
546
+ validate_index_length!(table_name, new_name) if respond_to?(:validate_index_length!)
535
547
  execute "ALTER TABLE #{quote_table_name(table_name)} RENAME INDEX #{quote_table_name(old_name)} TO #{quote_table_name(new_name)}"
536
548
  else
537
549
  super
538
550
  end
539
551
  end
540
552
 
553
+ # @private
554
+ ForeignKeyDefinition = ::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition if ::ActiveRecord::ConnectionAdapters.const_defined? :ForeignKeyDefinition
555
+
556
+ def supports_foreign_keys?; true end
557
+
558
+ def foreign_keys(table_name)
559
+ fk_info = select_all "" <<
560
+ "SELECT fk.referenced_table_name as 'to_table' " <<
561
+ ",fk.referenced_column_name as 'primary_key' " <<
562
+ ",fk.column_name as 'column' " <<
563
+ ",fk.constraint_name as 'name' " <<
564
+ "FROM information_schema.key_column_usage fk " <<
565
+ "WHERE fk.referenced_column_name is not null " <<
566
+ "AND fk.table_schema = '#{current_database}' " <<
567
+ "AND fk.table_name = '#{table_name}'"
568
+
569
+ create_table_info = select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
570
+
571
+ fk_info.map! do |row|
572
+ options = {
573
+ :column => row['column'], :name => row['name'], :primary_key => row['primary_key']
574
+ }
575
+ options[:on_update] = extract_foreign_key_action(create_table_info, row['name'], "UPDATE")
576
+ options[:on_delete] = extract_foreign_key_action(create_table_info, row['name'], "DELETE")
577
+
578
+ ForeignKeyDefinition.new(table_name, row['to_table'], options)
579
+ end
580
+ end if defined? ForeignKeyDefinition
581
+
582
+ def extract_foreign_key_action(structure, name, action)
583
+ if structure =~ /CONSTRAINT #{quote_column_name(name)} FOREIGN KEY .* REFERENCES .* ON #{action} (CASCADE|SET NULL|RESTRICT)/
584
+ case $1
585
+ when 'CASCADE'; :cascade
586
+ when 'SET NULL'; :nullify
587
+ end
588
+ end
589
+ end
590
+ private :extract_foreign_key_action
591
+
541
592
  # @override
542
593
  def add_column(table_name, column_name, type, options = {})
543
594
  add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
@@ -663,31 +714,39 @@ module ArJdbc
663
714
  show_variable("collation_database")
664
715
  end
665
716
 
717
+ # Maps logical Rails types to MySQL-specific data types.
666
718
  def type_to_sql(type, limit = nil, precision = nil, scale = nil)
667
719
  case type.to_s
668
720
  when 'binary'
669
721
  case limit
670
- when 0..0xfff; "varbinary(#{limit})"
671
- when nil; "blob"
722
+ when 0..0xfff; "varbinary(#{limit})"
723
+ when nil; "blob"
672
724
  when 0x1000..0xffffffff; "blob(#{limit})"
673
- else raise ActiveRecordError, "No binary type has character length #{limit}"
725
+ else raise(ActiveRecordError, "No binary type has character length #{limit}")
674
726
  end
675
727
  when 'integer'
676
728
  case limit
677
729
  when 1; 'tinyint'
678
730
  when 2; 'smallint'
679
731
  when 3; 'mediumint'
680
- when nil, 4, 11; 'int(11)' # compatibility with MySQL default
732
+ when nil, 4, 11; 'int(11)' # compatibility with MySQL default
681
733
  when 5..8; 'bigint'
682
- else raise ActiveRecordError, "No integer type has byte size #{limit}"
734
+ else raise(ActiveRecordError, "No integer type has byte size #{limit}")
683
735
  end
684
736
  when 'text'
685
737
  case limit
686
- when 0..0xff; 'tinytext'
687
- when nil, 0x100..0xffff; 'text'
688
- when 0x10000..0xffffff; 'mediumtext'
738
+ when 0..0xff; 'tinytext'
739
+ when nil, 0x100..0xffff; 'text'
740
+ when 0x10000..0xffffff; 'mediumtext'
689
741
  when 0x1000000..0xffffffff; 'longtext'
690
- else raise ActiveRecordError, "No text type has character length #{limit}"
742
+ else raise(ActiveRecordError, "No text type has character length #{limit}")
743
+ end
744
+ when 'datetime'
745
+ return super unless precision
746
+
747
+ case precision
748
+ when 0..6; "datetime(#{precision})"
749
+ else raise(ActiveRecordError, "No datetime type has precision of #{precision}. The allowed range of precision is from 0 to 6.")
691
750
  end
692
751
  else
693
752
  super
@@ -131,7 +131,7 @@ module ArJdbc
131
131
  [add_column_sql(table_name, :created_at, :datetime, options), add_column_sql(table_name, :updated_at, :datetime, options)]
132
132
  end
133
133
 
134
- def remove_timestamps_sql(table_name)
134
+ def remove_timestamps_sql(table_name, options = nil)
135
135
  [remove_column_sql(table_name, :updated_at), remove_column_sql(table_name, :created_at)]
136
136
  end
137
137
 
@@ -8,12 +8,30 @@ module ArJdbc
8
8
  end
9
9
 
10
10
  private
11
+
12
+ def visit_DropForeignKey(name)
13
+ "DROP FOREIGN KEY #{name}"
14
+ end
15
+
16
+ def visit_TableDefinition(o)
17
+ name = o.name
18
+ create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE #{quote_table_name(name)} "
19
+
20
+ statements = o.columns.map { |c| accept c }
21
+ statements.concat(o.indexes.map { |column_name, options| index_in_create(name, column_name, options) })
22
+
23
+ create_sql << "(#{statements.join(', ')}) " if statements.present?
24
+ create_sql << "#{o.options}"
25
+ create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
26
+ create_sql
27
+ end if AR42
28
+
11
29
  def visit_ChangeColumnDefinition(o)
12
30
  column = o.column
13
31
  options = o.options
14
32
  sql_type = type_to_sql(o.type, options[:limit], options[:precision], options[:scale])
15
33
  change_column_sql = "CHANGE #{quote_column_name(column.name)} #{quote_column_name(options[:name])} #{sql_type}"
16
- add_column_options!(change_column_sql, options)
34
+ add_column_options!(change_column_sql, options.merge(:column => column))
17
35
  add_column_position!(change_column_sql, options)
18
36
  end
19
37
 
@@ -25,6 +43,11 @@ module ArJdbc
25
43
  end
26
44
  sql
27
45
  end
46
+
47
+ def index_in_create(table_name, column_name, options)
48
+ index_name, index_type, index_columns, index_options, index_algorithm, index_using = @conn.add_index_options(table_name, column_name, options)
49
+ "#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_options} #{index_algorithm}"
50
+ end
28
51
  end
29
52
  end
30
53
 
@@ -6,21 +6,24 @@ module ArJdbc
6
6
  # Strives to provide Rails built-in PostgreSQL adapter (API) compatibility.
7
7
  module PostgreSQL
8
8
 
9
+ # @deprecated no longer used
9
10
  # @private
10
- AR4_COMPAT = ::ActiveRecord::VERSION::MAJOR > 3 unless const_defined?(:AR4_COMPAT)
11
+ AR4_COMPAT = AR40
12
+ # @deprecated no longer used
11
13
  # @private
12
- AR42_COMPAT = ::ActiveRecord::VERSION::MAJOR > 4 ||
13
- ( ::ActiveRecord::VERSION::MAJOR == 4 && ::ActiveRecord::VERSION::MINOR >= 2 )
14
+ AR42_COMPAT = AR42
14
15
 
15
16
  require 'arjdbc/postgresql/column'
16
17
  require 'arjdbc/postgresql/explain_support'
17
18
  require 'arjdbc/postgresql/schema_creation' # AR 4.x
18
-
19
19
  # @private
20
20
  IndexDefinition = ::ActiveRecord::ConnectionAdapters::IndexDefinition
21
21
 
22
22
  # @private
23
- Type = ::ActiveRecord::Type if AR42_COMPAT
23
+ ForeignKeyDefinition = ::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition if ::ActiveRecord::ConnectionAdapters.const_defined? :ForeignKeyDefinition
24
+
25
+ # @private
26
+ Type = ::ActiveRecord::Type if AR42
24
27
 
25
28
  # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class
26
29
  def self.jdbc_connection_class
@@ -137,22 +140,22 @@ module ArJdbc
137
140
 
138
141
  # Maps logical Rails types to PostgreSQL-specific data types.
139
142
  def type_to_sql(type, limit = nil, precision = nil, scale = nil)
140
- case type.to_sym
141
- when :'binary'
143
+ case type.to_s
144
+ when 'binary'
142
145
  # PostgreSQL doesn't support limits on binary (bytea) columns.
143
146
  # The hard limit is 1Gb, because of a 32-bit size field, and TOAST.
144
147
  case limit
145
- when nil, 0..0x3fffffff; super(type, nil, nil, nil)
148
+ when nil, 0..0x3fffffff; super(type)
146
149
  else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
147
150
  end
148
- when :'text'
151
+ when 'text'
149
152
  # PostgreSQL doesn't support limits on text columns.
150
153
  # The hard limit is 1Gb, according to section 8.3 in the manual.
151
154
  case limit
152
- when nil, 0..0x3fffffff; super(type, nil, nil, nil)
155
+ when nil, 0..0x3fffffff; super(type)
153
156
  else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
154
157
  end
155
- when :'integer'
158
+ when 'integer'
156
159
  return 'integer' unless limit
157
160
 
158
161
  case limit
@@ -161,7 +164,7 @@ module ArJdbc
161
164
  when 5..8; 'bigint'
162
165
  else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
163
166
  end
164
- when :'datetime'
167
+ when 'datetime'
165
168
  return super unless precision
166
169
 
167
170
  case precision
@@ -201,7 +204,7 @@ module ArJdbc
201
204
  when Hash
202
205
  case column.sql_type
203
206
  when 'hstore'
204
- jdbc_column_class.hstore_to_string(value)
207
+ jdbc_column_class.hstore_to_string(value, array_member)
205
208
  when 'json', 'jsonb'
206
209
  jdbc_column_class.json_to_string(value)
207
210
  else super(value, column)
@@ -215,7 +218,7 @@ module ArJdbc
215
218
  else
216
219
  super(value, column)
217
220
  end
218
- end if AR4_COMPAT && ! AR42_COMPAT
221
+ end if AR40 && ! AR42
219
222
 
220
223
  # @private
221
224
  def _type_cast(value)
@@ -224,14 +227,14 @@ module ArJdbc
224
227
  # Return a bind param hash with format as binary.
225
228
  # See http://deveiate.org/code/pg/PGconn.html#method-i-exec_prepared-doc
226
229
  # for more information
227
- { value: value.to_s, format: 1 }
230
+ { :value => value.to_s, :format => 1 }
228
231
  when OID::Xml::Data, OID::Bit::Data
229
232
  value.to_s
230
233
  else
231
234
  super
232
235
  end
233
- end if AR42_COMPAT
234
- private :_type_cast if AR42_COMPAT
236
+ end if AR42
237
+ private :_type_cast if AR42
235
238
 
236
239
  NATIVE_DATABASE_TYPES = {
237
240
  :primary_key => "serial primary key",
@@ -273,14 +276,14 @@ module ArJdbc
273
276
  :tstzrange => { :name => "tstzrange" },
274
277
  :int4range => { :name => "int4range" },
275
278
  :int8range => { :name => "int8range" },
276
- }) if AR4_COMPAT
279
+ }) if AR40
277
280
 
278
281
  NATIVE_DATABASE_TYPES.update(
279
282
  :bigserial => "bigserial",
280
283
  :bigint => { :name => "bigint" },
281
284
  :bit => { :name => "bit" },
282
285
  :bit_varying => { :name => "bit varying" }
283
- ) if AR42_COMPAT
286
+ ) if AR42
284
287
 
285
288
  def native_database_types
286
289
  NATIVE_DATABASE_TYPES
@@ -293,13 +296,13 @@ module ArJdbc
293
296
  spec[:array] = 'true' if column.respond_to?(:array) && column.array
294
297
  spec[:default] = "\"#{column.default_function}\"" if column.default_function
295
298
  spec
296
- end if AR4_COMPAT
299
+ end if AR40
297
300
 
298
301
  # Adds `:array` as a valid migration key.
299
302
  # @override
300
303
  def migration_keys
301
304
  super + [:array]
302
- end if AR4_COMPAT
305
+ end if AR40
303
306
 
304
307
  # Enable standard-conforming strings if available.
305
308
  def set_standard_conforming_strings
@@ -367,12 +370,12 @@ module ArJdbc
367
370
 
368
371
  def supports_index_sort_order?; true end
369
372
 
370
- def supports_partial_index?; true end if AR4_COMPAT
373
+ def supports_partial_index?; true end if AR40
371
374
 
372
375
  # Range data-types weren't introduced until PostgreSQL 9.2.
373
376
  def supports_ranges?
374
377
  postgresql_version >= 90200
375
- end if AR4_COMPAT
378
+ end if AR40
376
379
 
377
380
  def supports_transaction_isolation?(level = nil)
378
381
  true
@@ -518,9 +521,8 @@ module ArJdbc
518
521
  result = select(<<-end_sql, 'SCHEMA').first
519
522
  SELECT attr.attname
520
523
  FROM pg_attribute attr
521
- INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1]
522
- WHERE cons.contype = 'p'
523
- AND cons.conrelid = '#{quote_table_name(table)}'::regclass
524
+ INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = any(cons.conkey)
525
+ WHERE cons.contype = 'p' AND cons.conrelid = '#{quote_table_name(table)}'::regclass
524
526
  end_sql
525
527
 
526
528
  result && result['attname']
@@ -874,7 +876,7 @@ module ArJdbc
874
876
  sql_type = column.respond_to?(:sql_type) && column.sql_type
875
877
  sql_type && sql_type[0, 3] == 'bit' ? quote_bit(value) : super
876
878
  when Array
877
- if AR4_COMPAT && column.array? # will be always falsy in AR < 4.0
879
+ if AR40 && column.array? # will be always falsy in AR < 4.0
878
880
  "'#{jdbc_column_class.array_to_string(value, column, self).gsub(/'/, "''")}'"
879
881
  elsif column.type == :json # only in AR-4.0
880
882
  super(jdbc_column_class.json_to_string(value), column)
@@ -895,7 +897,7 @@ module ArJdbc
895
897
  end
896
898
  when Range
897
899
  sql_type = column.respond_to?(:sql_type) && column.sql_type
898
- if sql_type && sql_type[-5, 5] == 'range' && AR4_COMPAT
900
+ if sql_type && sql_type[-5, 5] == 'range' && AR40
899
901
  escaped = quote_string(jdbc_column_class.range_to_string(value))
900
902
  "'#{escaped}'::#{sql_type}"
901
903
  else super
@@ -908,7 +910,7 @@ module ArJdbc
908
910
  else
909
911
  super
910
912
  end
911
- end unless AR42_COMPAT
913
+ end unless AR42
912
914
 
913
915
  # @private
914
916
  def _quote(value)
@@ -932,8 +934,8 @@ module ArJdbc
932
934
  else
933
935
  super
934
936
  end
935
- end if AR42_COMPAT
936
- private :_quote if AR42_COMPAT
937
+ end if AR42
938
+ private :_quote if AR42
937
939
 
938
940
  # Quotes a string, escaping any ' (single quote) and \ (backslash) chars.
939
941
  # @return [String]
@@ -962,7 +964,7 @@ module ArJdbc
962
964
 
963
965
  def quote_bit(value)
964
966
  "B'#{value}'"
965
- end if AR4_COMPAT
967
+ end if AR40
966
968
 
967
969
  def escape_bytea(string)
968
970
  return unless string
@@ -990,7 +992,7 @@ module ArJdbc
990
992
  # @override
991
993
  def quote_table_name_for_assignment(table, attr)
992
994
  quote_column_name(attr)
993
- end if AR4_COMPAT
995
+ end if AR40
994
996
 
995
997
  # @override
996
998
  def quote_column_name(name)
@@ -1072,35 +1074,50 @@ module ArJdbc
1072
1074
  change_column_null(table_name, column_name, false, default) if notnull
1073
1075
  end if ::ActiveRecord::VERSION::MAJOR < 4
1074
1076
 
1077
+ # @private documented above
1078
+ def add_column(table_name, column_name, type, options = {}); super end if AR42
1079
+
1075
1080
  # Changes the column of a table.
1076
1081
  def change_column(table_name, column_name, type, options = {})
1077
1082
  quoted_table_name = quote_table_name(table_name)
1083
+ quoted_column_name = quote_table_name(column_name)
1078
1084
 
1079
1085
  sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale])
1080
1086
  sql_type << "[]" if options[:array]
1081
1087
 
1088
+ sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}"
1089
+ sql << " USING #{options[:using]}" if options[:using]
1090
+ if options[:cast_as]
1091
+ sql << " USING CAST(#{quoted_column_name} AS #{type_to_sql(options[:cast_as], options[:limit], options[:precision], options[:scale])})"
1092
+ end
1082
1093
  begin
1083
- execute "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{sql_type}"
1094
+ execute sql
1084
1095
  rescue ActiveRecord::StatementInvalid => e
1085
1096
  raise e if postgresql_version > 80000
1086
- # This is PostgreSQL 7.x, so we have to use a more arcane way of doing it.
1087
- begin
1088
- begin_db_transaction
1089
- tmp_column_name = "#{column_name}_ar_tmp"
1090
- add_column(table_name, tmp_column_name, type, options)
1091
- execute "UPDATE #{quoted_table_name} SET #{quote_column_name(tmp_column_name)} = CAST(#{quote_column_name(column_name)} AS #{sql_type})"
1092
- remove_column(table_name, column_name)
1093
- rename_column(table_name, tmp_column_name, column_name)
1094
- commit_db_transaction
1095
- rescue
1096
- rollback_db_transaction
1097
- end
1097
+ change_column_pg7(table_name, column_name, type, options)
1098
1098
  end
1099
1099
 
1100
1100
  change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
1101
1101
  change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
1102
1102
  end # unless const_defined? :SchemaCreation
1103
1103
 
1104
+ def change_column_pg7(table_name, column_name, type, options)
1105
+ quoted_table_name = quote_table_name(table_name)
1106
+ # This is PostgreSQL 7.x, so we have to use a more arcane way of doing it.
1107
+ begin
1108
+ begin_db_transaction
1109
+ tmp_column_name = "#{column_name}_ar_tmp"
1110
+ add_column(table_name, tmp_column_name, type, options)
1111
+ execute "UPDATE #{quoted_table_name} SET #{quote_column_name(tmp_column_name)} = CAST(#{quote_column_name(column_name)} AS #{sql_type})"
1112
+ remove_column(table_name, column_name)
1113
+ rename_column(table_name, tmp_column_name, column_name)
1114
+ commit_db_transaction
1115
+ rescue
1116
+ rollback_db_transaction
1117
+ end
1118
+ end
1119
+ private :change_column_pg7
1120
+
1104
1121
  # Changes the default value of a table column.
1105
1122
  def change_column_default(table_name, column_name, default)
1106
1123
  if column = column_for(table_name, column_name) # (backwards) compatible with AR 3.x - 4.x
@@ -1108,8 +1125,23 @@ module ArJdbc
1108
1125
  else
1109
1126
  execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}"
1110
1127
  end
1111
- end # unless const_defined? :SchemaCreation
1128
+ end unless AR42 # unless const_defined? :SchemaCreation
1129
+
1130
+ # @private documented above
1131
+ def change_column_default(table_name, column_name, default)
1132
+ return unless column = column_for(table_name, column_name)
1133
+
1134
+ alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s"
1135
+ if default.nil?
1136
+ # <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
1137
+ # cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
1138
+ execute alter_column_query % "DROP DEFAULT"
1139
+ else
1140
+ execute alter_column_query % "SET DEFAULT #{quote_default_value(default, column)}"
1141
+ end
1142
+ end if AR42
1112
1143
 
1144
+ # @private
1113
1145
  def change_column_null(table_name, column_name, null, default = nil)
1114
1146
  unless null || default.nil?
1115
1147
  if column = column_for(table_name, column_name) # (backwards) compatible with AR 3.x - 4.x
@@ -1119,7 +1151,16 @@ module ArJdbc
1119
1151
  end
1120
1152
  end
1121
1153
  execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
1122
- end # unless const_defined? :SchemaCreation
1154
+ end unless AR42 # unless const_defined? :SchemaCreation
1155
+
1156
+ # @private
1157
+ def change_column_null(table_name, column_name, null, default = nil)
1158
+ unless null || default.nil?
1159
+ column = column_for(table_name, column_name)
1160
+ 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
1161
+ end
1162
+ execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
1163
+ end if AR42
1123
1164
 
1124
1165
  def rename_column(table_name, column_name, new_column_name)
1125
1166
  execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
@@ -1129,16 +1170,55 @@ module ArJdbc
1129
1170
  def add_index(table_name, column_name, options = {})
1130
1171
  index_name, index_type, index_columns, index_options, index_algorithm, index_using = add_index_options(table_name, column_name, options)
1131
1172
  execute "CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns})#{index_options}"
1132
- end if AR4_COMPAT
1173
+ end if AR40
1133
1174
 
1134
1175
  def remove_index!(table_name, index_name)
1135
1176
  execute "DROP INDEX #{quote_table_name(index_name)}"
1136
1177
  end
1137
1178
 
1138
1179
  def rename_index(table_name, old_name, new_name)
1180
+ validate_index_length!(table_name, new_name) if respond_to?(:validate_index_length!)
1181
+
1139
1182
  execute "ALTER INDEX #{quote_column_name(old_name)} RENAME TO #{quote_table_name(new_name)}"
1140
1183
  end
1141
1184
 
1185
+ def supports_foreign_keys?; true end
1186
+
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
+
1142
1222
  def index_name_length
1143
1223
  63
1144
1224
  end
@@ -1180,12 +1260,12 @@ module ArJdbc
1180
1260
 
1181
1261
  column.new(name, default_value, oid_type, type, ! notnull, default_function, oid, self)
1182
1262
  end
1183
- end if AR42_COMPAT
1263
+ end if AR42
1184
1264
 
1185
1265
  # @private only for API compatibility
1186
1266
  def new_column(name, default, cast_type, sql_type = nil, null = true, default_function = nil)
1187
1267
  jdbc_column_class.new(name, default, cast_type, sql_type, null, default_function)
1188
- end if AR42_COMPAT
1268
+ end if AR42
1189
1269
 
1190
1270
  # @private
1191
1271
  def column_for(table_name, column_name)
@@ -1227,7 +1307,7 @@ module ArJdbc
1227
1307
  # @private
1228
1308
  TABLE_EXISTS_SQL_PREFIX = 'SELECT COUNT(*) as table_count FROM pg_class c'
1229
1309
  TABLE_EXISTS_SQL_PREFIX << ' LEFT JOIN pg_namespace n ON n.oid = c.relnamespace'
1230
- if AR42_COMPAT # -- (r)elation/table, (v)iew, (m)aterialized view
1310
+ if AR42 # -- (r)elation/table, (v)iew, (m)aterialized view
1231
1311
  TABLE_EXISTS_SQL_PREFIX << " WHERE c.relkind IN ('r','v','m')"
1232
1312
  else
1233
1313
  TABLE_EXISTS_SQL_PREFIX << " WHERE c.relkind IN ('r','v')"
@@ -1251,6 +1331,10 @@ module ArJdbc
1251
1331
  end
1252
1332
  end
1253
1333
 
1334
+ def drop_table(table_name, options = {})
1335
+ execute "DROP TABLE #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
1336
+ end
1337
+
1254
1338
  def truncate(table_name, name = nil)
1255
1339
  execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
1256
1340
  end
@@ -1266,7 +1350,7 @@ module ArJdbc
1266
1350
  AND t.relname = '#{table_name}'
1267
1351
  AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
1268
1352
  SQL
1269
- end if AR42_COMPAT
1353
+ end if AR42
1270
1354
 
1271
1355
  # Returns an array of indexes for the given table.
1272
1356
  def indexes(table_name, name = nil)
@@ -1328,7 +1412,7 @@ module ArJdbc
1328
1412
  when 'average' then 'avg'
1329
1413
  else operation.downcase
1330
1414
  end
1331
- end if AR42_COMPAT
1415
+ end if AR42
1332
1416
 
1333
1417
  private
1334
1418
 
@@ -1391,9 +1475,9 @@ module ActiveRecord::ConnectionAdapters
1391
1475
  include ::ArJdbc::PostgreSQL
1392
1476
  include ::ArJdbc::PostgreSQL::ExplainSupport
1393
1477
 
1394
- require 'arjdbc/postgresql/oid_types' if AR4_COMPAT
1478
+ require 'arjdbc/postgresql/oid_types' if ::ArJdbc::AR40
1395
1479
  include ::ArJdbc::PostgreSQL::OIDTypes if ::ArJdbc::PostgreSQL.const_defined?(:OIDTypes)
1396
- include ::ArJdbc::PostgreSQL::ColumnHelpers if AR42_COMPAT
1480
+ include ::ArJdbc::PostgreSQL::ColumnHelpers if ::ArJdbc::AR42
1397
1481
 
1398
1482
  include ::ArJdbc::Util::QuotedCache
1399
1483
 
@@ -1405,141 +1489,28 @@ module ActiveRecord::ConnectionAdapters
1405
1489
 
1406
1490
  @table_alias_length = nil
1407
1491
 
1408
- initialize_type_map(@type_map = Type::HashLookupTypeMap.new) if AR42_COMPAT
1492
+ initialize_type_map(@type_map = Type::HashLookupTypeMap.new) if ::ArJdbc::AR42
1409
1493
 
1410
1494
  @use_insert_returning = @config.key?(:insert_returning) ?
1411
1495
  self.class.type_cast_config_to_boolean(@config[:insert_returning]) : nil
1412
1496
  end
1413
1497
 
1414
- class ColumnDefinition < ActiveRecord::ConnectionAdapters::ColumnDefinition
1415
- attr_accessor :array
1416
- def array?; !!@array; end
1417
- end
1418
-
1419
- module ColumnMethods
1420
- def xml(*args)
1421
- options = args.extract_options!
1422
- column(args[0], 'xml', options)
1423
- end
1424
-
1425
- def tsvector(*args)
1426
- options = args.extract_options!
1427
- column(args[0], 'tsvector', options)
1428
- end
1429
-
1430
- def int4range(name, options = {})
1431
- column(name, 'int4range', options)
1432
- end
1433
-
1434
- def int8range(name, options = {})
1435
- column(name, 'int8range', options)
1436
- end
1437
-
1438
- def tsrange(name, options = {})
1439
- column(name, 'tsrange', options)
1440
- end
1441
-
1442
- def tstzrange(name, options = {})
1443
- column(name, 'tstzrange', options)
1444
- end
1445
-
1446
- def numrange(name, options = {})
1447
- column(name, 'numrange', options)
1448
- end
1449
-
1450
- def daterange(name, options = {})
1451
- column(name, 'daterange', options)
1452
- end
1453
-
1454
- def hstore(name, options = {})
1455
- column(name, 'hstore', options)
1456
- end
1457
-
1458
- def ltree(name, options = {})
1459
- column(name, 'ltree', options)
1460
- end
1461
-
1462
- def inet(name, options = {})
1463
- column(name, 'inet', options)
1464
- end
1465
-
1466
- def cidr(name, options = {})
1467
- column(name, 'cidr', options)
1468
- end
1469
-
1470
- def macaddr(name, options = {})
1471
- column(name, 'macaddr', options)
1472
- end
1473
-
1474
- def uuid(name, options = {})
1475
- column(name, 'uuid', options)
1476
- end
1477
-
1478
- def json(name, options = {})
1479
- column(name, 'json', options)
1480
- end
1481
-
1482
- def jsonb(name, options = {})
1483
- column(name, :jsonb, options)
1484
- end
1485
-
1486
- def bit(name, options)
1487
- column(name, 'bit', options)
1488
- end
1489
-
1490
- def bit_varying(name, options)
1491
- column(name, 'bit varying', options)
1492
- end
1498
+ if ::ArJdbc::AR42
1499
+ require 'active_record/connection_adapters/postgresql/schema_definitions'
1500
+ else
1501
+ require 'arjdbc/postgresql/base/schema_definitions'
1493
1502
  end
1494
1503
 
1495
- class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
1496
- include ColumnMethods
1497
-
1498
- def primary_key(name, type = :primary_key, options = {})
1499
- return super unless type == :uuid
1500
- options[:default] = options.fetch(:default, 'uuid_generate_v4()')
1501
- options[:primary_key] = true
1502
- column name, type, options
1503
- end if ::ActiveRecord::VERSION::MAJOR > 3 # 3.2 super expects (name)
1504
-
1505
- def column(name, type = nil, options = {})
1506
- super
1507
- column = self[name]
1508
- # NOTE: <= 3.1 no #new_column_definition hard-coded ColumnDef.new :
1509
- # column = self[name] || ColumnDefinition.new(@base, name, type)
1510
- # thus we simply do not support array column definitions on <= 3.1
1511
- column.array = options[:array] if column.is_a?(ColumnDefinition)
1512
- self
1513
- end
1504
+ ColumnDefinition = ActiveRecord::ConnectionAdapters::PostgreSQL::ColumnDefinition
1514
1505
 
1515
- private
1516
-
1517
- if ::ActiveRecord::VERSION::MAJOR > 3
1518
-
1519
- def create_column_definition(name, type)
1520
- ColumnDefinition.new name, type
1521
- end
1522
-
1523
- else # no #create_column_definition on 3.2
1524
-
1525
- def new_column_definition(base, name, type)
1526
- definition = ColumnDefinition.new base, name, type
1527
- @columns << definition
1528
- @columns_hash[name] = definition
1529
- definition
1530
- end
1531
-
1532
- end
1533
-
1534
- end
1506
+ ColumnMethods = ActiveRecord::ConnectionAdapters::PostgreSQL::ColumnMethods
1507
+ TableDefinition = ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition
1535
1508
 
1536
1509
  def table_definition(*args)
1537
1510
  new_table_definition(TableDefinition, *args)
1538
1511
  end
1539
1512
 
1540
- class Table < ActiveRecord::ConnectionAdapters::Table
1541
- include ColumnMethods
1542
- end
1513
+ Table = ActiveRecord::ConnectionAdapters::PostgreSQL::Table
1543
1514
 
1544
1515
  def update_table_definition(table_name, base)
1545
1516
  Table.new(table_name, base)