activerecord-jdbc-adapter 1.3.16 → 1.3.17

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