activerecord 4.0.0.beta1 → 4.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +573 -30
  3. data/README.rdoc +3 -3
  4. data/lib/active_record.rb +8 -2
  5. data/lib/active_record/associations.rb +16 -9
  6. data/lib/active_record/associations/association.rb +8 -6
  7. data/lib/active_record/associations/association_scope.rb +2 -1
  8. data/lib/active_record/associations/belongs_to_association.rb +2 -2
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  10. data/lib/active_record/associations/builder/belongs_to.rb +37 -5
  11. data/lib/active_record/associations/collection_association.rb +38 -14
  12. data/lib/active_record/associations/collection_proxy.rb +18 -15
  13. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +3 -3
  14. data/lib/active_record/associations/has_many_association.rb +4 -3
  15. data/lib/active_record/associations/has_many_through_association.rb +1 -1
  16. data/lib/active_record/associations/has_one_association.rb +1 -1
  17. data/lib/active_record/associations/join_dependency.rb +29 -8
  18. data/lib/active_record/associations/join_dependency/join_association.rb +26 -6
  19. data/lib/active_record/associations/join_dependency/join_base.rb +2 -2
  20. data/lib/active_record/associations/join_dependency/join_part.rb +6 -6
  21. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +1 -1
  22. data/lib/active_record/associations/preloader/has_many_through.rb +6 -2
  23. data/lib/active_record/associations/through_association.rb +1 -1
  24. data/lib/active_record/attribute_assignment.rb +5 -5
  25. data/lib/active_record/attribute_methods.rb +20 -5
  26. data/lib/active_record/attribute_methods/dirty.rb +5 -1
  27. data/lib/active_record/attribute_methods/primary_key.rb +1 -1
  28. data/lib/active_record/attribute_methods/serialization.rb +9 -2
  29. data/lib/active_record/autosave_association.rb +19 -5
  30. data/lib/active_record/base.rb +3 -3
  31. data/lib/active_record/callbacks.rb +1 -1
  32. data/lib/active_record/coders/yaml_column.rb +8 -13
  33. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +3 -9
  34. data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -1
  35. data/lib/active_record/connection_adapters/abstract/database_statements.rb +2 -1
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +2 -8
  37. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +60 -61
  38. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +13 -2
  39. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +291 -153
  40. data/lib/active_record/connection_adapters/abstract/transaction.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract_adapter.rb +92 -1
  42. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +55 -29
  43. data/lib/active_record/connection_adapters/column.rb +4 -4
  44. data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
  45. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -3
  46. data/lib/active_record/connection_adapters/mysql_adapter.rb +5 -5
  47. data/lib/active_record/connection_adapters/postgresql/cast.rb +22 -2
  48. data/lib/active_record/connection_adapters/postgresql/oid.rb +25 -6
  49. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -13
  50. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +50 -9
  51. data/lib/active_record/connection_adapters/postgresql_adapter.rb +53 -24
  52. data/lib/active_record/connection_adapters/schema_cache.rb +35 -7
  53. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +13 -5
  54. data/lib/active_record/connection_handling.rb +7 -7
  55. data/lib/active_record/core.rb +43 -8
  56. data/lib/active_record/counter_cache.rb +2 -1
  57. data/lib/active_record/errors.rb +11 -10
  58. data/lib/active_record/explain.rb +9 -7
  59. data/lib/active_record/explain_registry.rb +30 -0
  60. data/lib/active_record/explain_subscriber.rb +3 -2
  61. data/lib/active_record/fixture_set/file.rb +1 -2
  62. data/lib/active_record/fixtures.rb +13 -7
  63. data/lib/active_record/inheritance.rb +12 -4
  64. data/lib/active_record/integration.rb +3 -3
  65. data/lib/active_record/locking/optimistic.rb +2 -2
  66. data/lib/active_record/log_subscriber.rb +2 -2
  67. data/lib/active_record/migration.rb +69 -21
  68. data/lib/active_record/model_schema.rb +1 -1
  69. data/lib/active_record/nested_attributes.rb +98 -46
  70. data/lib/active_record/persistence.rb +3 -3
  71. data/lib/active_record/querying.rb +1 -1
  72. data/lib/active_record/railtie.rb +18 -4
  73. data/lib/active_record/railties/console_sandbox.rb +3 -2
  74. data/lib/active_record/railties/controller_runtime.rb +2 -1
  75. data/lib/active_record/railties/databases.rake +38 -80
  76. data/lib/active_record/reflection.rb +36 -3
  77. data/lib/active_record/relation.rb +18 -8
  78. data/lib/active_record/relation/calculations.rb +10 -5
  79. data/lib/active_record/relation/delegation.rb +3 -5
  80. data/lib/active_record/relation/finder_methods.rb +27 -14
  81. data/lib/active_record/relation/merger.rb +30 -2
  82. data/lib/active_record/relation/predicate_builder.rb +1 -6
  83. data/lib/active_record/relation/query_methods.rb +113 -16
  84. data/lib/active_record/runtime_registry.rb +17 -0
  85. data/lib/active_record/schema_dumper.rb +5 -1
  86. data/lib/active_record/schema_migration.rb +8 -5
  87. data/lib/active_record/scoping.rb +56 -2
  88. data/lib/active_record/scoping/default.rb +12 -11
  89. data/lib/active_record/scoping/named.rb +7 -3
  90. data/lib/active_record/serialization.rb +1 -1
  91. data/lib/active_record/statement_cache.rb +26 -0
  92. data/lib/active_record/tasks/database_tasks.rb +55 -10
  93. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  94. data/lib/active_record/tasks/mysql_database_tasks.rb +7 -2
  95. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  96. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  97. data/lib/active_record/timestamp.rb +6 -0
  98. data/lib/active_record/transactions.rb +7 -3
  99. data/lib/active_record/validations.rb +1 -2
  100. data/lib/active_record/validations/uniqueness.rb +7 -3
  101. data/lib/active_record/version.rb +7 -6
  102. data/lib/rails/generators/active_record/migration/migration_generator.rb +9 -2
  103. data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +4 -0
  104. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  105. data/lib/rails/generators/active_record/model/templates/model.rb +4 -1
  106. metadata +17 -12
  107. data/examples/associations.png +0 -0
@@ -2,6 +2,17 @@ module ActiveRecord
2
2
  module ConnectionAdapters
3
3
  class PostgreSQLColumn < Column
4
4
  module Cast
5
+ def point_to_string(point)
6
+ "(#{point[0]},#{point[1]})"
7
+ end
8
+
9
+ def string_to_point(string)
10
+ if string[0] == '(' && string[-1] == ')'
11
+ string = string[1...-1]
12
+ end
13
+ string.split(',').map{ |v| Float(v) }
14
+ end
15
+
5
16
  def string_to_time(string)
6
17
  return string unless String === string
7
18
 
@@ -15,6 +26,15 @@ module ActiveRecord
15
26
  end
16
27
  end
17
28
 
29
+ def string_to_bit(value)
30
+ case value
31
+ when /^0x/i
32
+ value[2..-1].hex.to_s(2) # Hexadecimal notation
33
+ else
34
+ value # Bit-string notation
35
+ end
36
+ end
37
+
18
38
  def hstore_to_string(object)
19
39
  if Hash === object
20
40
  object.map { |k,v|
@@ -30,8 +50,8 @@ module ActiveRecord
30
50
  nil
31
51
  elsif String === string
32
52
  Hash[string.scan(HstorePair).map { |k,v|
33
- v = v.upcase == 'NULL' ? nil : v.gsub(/^"(.*)"$/,'\1').gsub(/\\(.)/, '\1')
34
- k = k.gsub(/^"(.*)"$/,'\1').gsub(/\\(.)/, '\1')
53
+ v = v.upcase == 'NULL' ? nil : v.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
54
+ k = k.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
35
55
  [k,v]
36
56
  }]
37
57
  else
@@ -18,8 +18,19 @@ module ActiveRecord
18
18
  end
19
19
  end
20
20
 
21
+ class Bit < Type
22
+ def type_cast(value)
23
+ if String === value
24
+ ConnectionAdapters::PostgreSQLColumn.string_to_bit value
25
+ else
26
+ value
27
+ end
28
+ end
29
+ end
30
+
21
31
  class Bytea < Type
22
32
  def type_cast(value)
33
+ return if value.nil?
23
34
  PGconn.unescape_bytea value
24
35
  end
25
36
  end
@@ -63,6 +74,16 @@ module ActiveRecord
63
74
  end
64
75
  end
65
76
 
77
+ class Point < Type
78
+ def type_cast(value)
79
+ if String === value
80
+ ConnectionAdapters::PostgreSQLColumn.string_to_point value
81
+ else
82
+ value
83
+ end
84
+ end
85
+ end
86
+
66
87
  class Array < Type
67
88
  attr_reader :subtype
68
89
  def initialize(subtype)
@@ -312,27 +333,25 @@ module ActiveRecord
312
333
  # FIXME: why are we keeping these types as strings?
313
334
  alias_type 'tsvector', 'text'
314
335
  alias_type 'interval', 'text'
315
- alias_type 'bit', 'text'
316
- alias_type 'varbit', 'text'
317
336
  alias_type 'macaddr', 'text'
318
337
  alias_type 'uuid', 'text'
319
338
 
320
- # FIXME: I don't think this is correct. We should probably be returning a parsed date,
321
- # but the tests pass with a string returned.
322
- register_type 'timestamptz', OID::Identity.new
323
-
324
339
  register_type 'money', OID::Money.new
325
340
  register_type 'bytea', OID::Bytea.new
326
341
  register_type 'bool', OID::Boolean.new
342
+ register_type 'bit', OID::Bit.new
343
+ register_type 'varbit', OID::Bit.new
327
344
 
328
345
  register_type 'float4', OID::Float.new
329
346
  alias_type 'float8', 'float4'
330
347
 
331
348
  register_type 'timestamp', OID::Timestamp.new
349
+ register_type 'timestamptz', OID::Timestamp.new
332
350
  register_type 'date', OID::Date.new
333
351
  register_type 'time', OID::Time.new
334
352
 
335
353
  register_type 'path', OID::Identity.new
354
+ register_type 'point', OID::Point.new
336
355
  register_type 'polygon', OID::Identity.new
337
356
  register_type 'circle', OID::Identity.new
338
357
  register_type 'hstore', OID::Hstore.new
@@ -18,27 +18,33 @@ module ActiveRecord
18
18
  def quote(value, column = nil) #:nodoc:
19
19
  return super unless column
20
20
 
21
+ sql_type = type_to_sql(column.type, column.limit, column.precision, column.scale)
22
+
21
23
  case value
22
24
  when Range
23
- if /range$/ =~ column.sql_type
24
- "'#{PostgreSQLColumn.range_to_string(value)}'::#{column.sql_type}"
25
+ if /range$/ =~ sql_type
26
+ "'#{PostgreSQLColumn.range_to_string(value)}'::#{sql_type}"
25
27
  else
26
28
  super
27
29
  end
28
30
  when Array
29
- if column.array
30
- "'#{PostgreSQLColumn.array_to_string(value, column, self)}'"
31
+ case sql_type
32
+ when 'point' then super(PostgreSQLColumn.point_to_string(value))
31
33
  else
32
- super
34
+ if column.array
35
+ "'#{PostgreSQLColumn.array_to_string(value, column, self).gsub(/'/, "''")}'"
36
+ else
37
+ super
38
+ end
33
39
  end
34
40
  when Hash
35
- case column.sql_type
41
+ case sql_type
36
42
  when 'hstore' then super(PostgreSQLColumn.hstore_to_string(value), column)
37
43
  when 'json' then super(PostgreSQLColumn.json_to_string(value), column)
38
44
  else super
39
45
  end
40
46
  when IPAddr
41
- case column.sql_type
47
+ case sql_type
42
48
  when 'inet', 'cidr' then super(PostgreSQLColumn.cidr_to_string(value), column)
43
49
  else super
44
50
  end
@@ -51,11 +57,14 @@ module ActiveRecord
51
57
  super
52
58
  end
53
59
  when Numeric
54
- return super unless column.sql_type == 'money'
55
- # Not truly string input, so doesn't require (or allow) escape string syntax.
56
- "'#{value}'"
60
+ if sql_type == 'money' || [:string, :text].include?(column.type)
61
+ # Not truly string input, so doesn't require (or allow) escape string syntax.
62
+ "'#{value}'"
63
+ else
64
+ super
65
+ end
57
66
  when String
58
- case column.sql_type
67
+ case sql_type
59
68
  when 'bytea' then "'#{escape_bytea(value)}'"
60
69
  when 'xml' then "xml '#{quote_string(value)}'"
61
70
  when /^bit/
@@ -87,8 +96,12 @@ module ActiveRecord
87
96
  super(value, column)
88
97
  end
89
98
  when Array
90
- return super(value, column) unless column.array
91
- PostgreSQLColumn.array_to_string(value, column, self)
99
+ case column.sql_type
100
+ when 'point' then PostgreSQLColumn.point_to_string(value)
101
+ else
102
+ return super(value, column) unless column.array
103
+ PostgreSQLColumn.array_to_string(value, column, self)
104
+ end
92
105
  when String
93
106
  return super(value, column) unless 'bytea' == column.sql_type
94
107
  { :value => value, :format => 1 }
@@ -1,6 +1,42 @@
1
1
  module ActiveRecord
2
2
  module ConnectionAdapters
3
3
  class PostgreSQLAdapter < AbstractAdapter
4
+ class SchemaCreation < AbstractAdapter::SchemaCreation
5
+ private
6
+
7
+ def visit_AddColumn(o)
8
+ sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale)
9
+ sql = "ADD COLUMN #{quote_column_name(o.name)} #{sql_type}"
10
+ add_column_options!(sql, column_options(o))
11
+ end
12
+
13
+ def visit_ColumnDefinition(o)
14
+ sql = super
15
+ if o.primary_key? && o.type == :uuid
16
+ sql << " PRIMARY KEY "
17
+ add_column_options!(sql, column_options(o))
18
+ end
19
+ sql
20
+ end
21
+
22
+ def add_column_options!(sql, options)
23
+ if options[:array] || options[:column].try(:array)
24
+ sql << '[]'
25
+ end
26
+
27
+ column = options.fetch(:column) { return super }
28
+ if column.type == :uuid && options[:default] =~ /\(\)/
29
+ sql << " DEFAULT #{options[:default]}"
30
+ else
31
+ super
32
+ end
33
+ end
34
+ end
35
+
36
+ def schema_creation
37
+ SchemaCreation.new self
38
+ end
39
+
4
40
  module SchemaStatements
5
41
  # Drops the database specified on the +name+ attribute
6
42
  # and creates it again using the provided +options+.
@@ -120,12 +156,15 @@ module ActiveRecord
120
156
 
121
157
  column_names = columns.values_at(*indkey).compact
122
158
 
123
- # add info on sort order for columns (only desc order is explicitly specified, asc is the default)
124
- desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
125
- orders = desc_order_columns.any? ? Hash[desc_order_columns.map {|order_column| [order_column, :desc]}] : {}
126
- where = inddef.scan(/WHERE (.+)$/).flatten[0]
159
+ unless column_names.empty?
160
+ # add info on sort order for columns (only desc order is explicitly specified, asc is the default)
161
+ desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
162
+ orders = desc_order_columns.any? ? Hash[desc_order_columns.map {|order_column| [order_column, :desc]}] : {}
163
+ where = inddef.scan(/WHERE (.+)$/).flatten[0]
164
+ using = inddef.scan(/USING (.+?) /).flatten[0].to_sym
127
165
 
128
- column_names.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where)
166
+ IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where, nil, using)
167
+ end
129
168
  end.compact
130
169
  end
131
170
 
@@ -337,10 +376,7 @@ module ActiveRecord
337
376
  # See TableDefinition#column for details of the options you can use.
338
377
  def add_column(table_name, column_name, type, options = {})
339
378
  clear_cache!
340
- add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
341
- add_column_options!(add_column_sql, options)
342
-
343
- execute add_column_sql
379
+ super
344
380
  end
345
381
 
346
382
  # Changes the column of a table.
@@ -375,6 +411,11 @@ module ActiveRecord
375
411
  rename_column_indexes(table_name, column_name, new_column_name)
376
412
  end
377
413
 
414
+ def add_index(table_name, column_name, options = {}) #:nodoc:
415
+ index_name, index_type, index_columns, index_options, index_algorithm, index_using = add_index_options(table_name, column_name, options)
416
+ execute "CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns})#{index_options}"
417
+ end
418
+
378
419
  def remove_index!(table_name, index_name) #:nodoc:
379
420
  execute "DROP INDEX #{quote_table_name(index_name)}"
380
421
  end
@@ -16,7 +16,7 @@ require 'pg'
16
16
  require 'ipaddr'
17
17
 
18
18
  module ActiveRecord
19
- module ConnectionHandling
19
+ module ConnectionHandling # :nodoc:
20
20
  VALID_CONN_PARAMS = [:host, :hostaddr, :port, :dbname, :user, :password, :connect_timeout,
21
21
  :client_encoding, :options, :application_name, :fallback_application_name,
22
22
  :keepalives, :keepalives_idle, :keepalives_interval, :keepalives_count,
@@ -24,7 +24,7 @@ module ActiveRecord
24
24
  :requirepeer, :krbsrvname, :gsslib, :service]
25
25
 
26
26
  # Establishes a connection to the database that's used by all Active Record objects
27
- def postgresql_connection(config) # :nodoc:
27
+ def postgresql_connection(config)
28
28
  conn_params = config.symbolize_keys
29
29
 
30
30
  conn_params.delete_if { |_, v| v.nil? }
@@ -80,7 +80,7 @@ module ActiveRecord
80
80
  when /\A'(.*)'::(num|date|tstz|ts|int4|int8)range\z/m
81
81
  $1
82
82
  # Numeric types
83
- when /\A\(?(-?\d+(\.\d*)?\)?)\z/
83
+ when /\A\(?(-?\d+(\.\d*)?\)?(::bigint)?)\z/
84
84
  $1
85
85
  # Character types
86
86
  when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m
@@ -263,7 +263,7 @@ module ActiveRecord
263
263
  attr_accessor :array
264
264
  end
265
265
 
266
- class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
266
+ module ColumnMethods
267
267
  def xml(*args)
268
268
  options = args.extract_options!
269
269
  column(args[0], 'xml', options)
@@ -325,6 +325,17 @@ module ActiveRecord
325
325
  def json(name, options = {})
326
326
  column(name, 'json', options)
327
327
  end
328
+ end
329
+
330
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
331
+ include ColumnMethods
332
+
333
+ def primary_key(name, type = :primary_key, options = {})
334
+ return super unless type == :uuid
335
+ options[:default] ||= 'uuid_generate_v4()'
336
+ options[:primary_key] = true
337
+ column name, type, options
338
+ end
328
339
 
329
340
  def column(name, type = nil, options = {})
330
341
  super
@@ -334,16 +345,21 @@ module ActiveRecord
334
345
  self
335
346
  end
336
347
 
348
+ def xml(options = {})
349
+ column(args[0], :text, options)
350
+ end
351
+
337
352
  private
338
353
 
339
- def new_column_definition(base, name, type)
340
- definition = ColumnDefinition.new base, name, type
341
- @columns << definition
342
- @columns_hash[name] = definition
343
- definition
354
+ def create_column_definition(name, type)
355
+ ColumnDefinition.new name, type
344
356
  end
345
357
  end
346
358
 
359
+ class Table < ActiveRecord::ConnectionAdapters::Table
360
+ include ColumnMethods
361
+ end
362
+
347
363
  ADAPTER_NAME = 'PostgreSQL'
348
364
 
349
365
  NATIVE_DATABASE_TYPES = {
@@ -417,6 +433,10 @@ module ActiveRecord
417
433
  true
418
434
  end
419
435
 
436
+ def index_algorithms
437
+ { concurrently: 'CONCURRENTLY' }
438
+ end
439
+
420
440
  class StatementPool < ConnectionAdapters::StatementPool
421
441
  def initialize(connection, max)
422
442
  super
@@ -481,7 +501,7 @@ module ActiveRecord
481
501
  if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
482
502
  @visitor = Arel::Visitors::PostgreSQL.new self
483
503
  else
484
- @visitor = BindSubstitution.new self
504
+ @visitor = unprepared_visitor
485
505
  end
486
506
 
487
507
  @connection_parameters, @config = connection_parameters, config
@@ -510,8 +530,7 @@ module ActiveRecord
510
530
 
511
531
  # Is this connection alive and ready for queries?
512
532
  def active?
513
- @connection.query 'SELECT 1'
514
- true
533
+ @connection.connect_poll != PG::PGRES_POLLING_FAILED
515
534
  rescue PGError
516
535
  false
517
536
  end
@@ -586,13 +605,13 @@ module ActiveRecord
586
605
  end
587
606
 
588
607
  def enable_extension(name)
589
- exec_query("CREATE EXTENSION IF NOT EXISTS #{name}").tap {
608
+ exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
590
609
  reload_type_map
591
610
  }
592
611
  end
593
612
 
594
613
  def disable_extension(name)
595
- exec_query("DROP EXTENSION IF EXISTS #{name} CASCADE").tap {
614
+ exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
596
615
  reload_type_map
597
616
  }
598
617
  end
@@ -619,13 +638,6 @@ module ActiveRecord
619
638
  @table_alias_length ||= query('SHOW max_identifier_length', 'SCHEMA')[0][0].to_i
620
639
  end
621
640
 
622
- def add_column_options!(sql, options)
623
- if options[:array] || options[:column].try(:array)
624
- sql << '[]'
625
- end
626
- super
627
- end
628
-
629
641
  # Set the authorized user for this session
630
642
  def session_auth=(user)
631
643
  clear_cache!
@@ -655,6 +667,10 @@ module ActiveRecord
655
667
  @use_insert_returning
656
668
  end
657
669
 
670
+ def valid_type?(type)
671
+ !native_database_types[type].nil?
672
+ end
673
+
658
674
  protected
659
675
 
660
676
  # Returns the version of the connected PostgreSQL server.
@@ -667,6 +683,8 @@ module ActiveRecord
667
683
  UNIQUE_VIOLATION = "23505"
668
684
 
669
685
  def translate_exception(exception, message)
686
+ return exception unless exception.respond_to?(:result)
687
+
670
688
  case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE)
671
689
  when UNIQUE_VIOLATION
672
690
  RecordNotUnique.new(message, exception)
@@ -697,7 +715,14 @@ module ActiveRecord
697
715
 
698
716
  # populate composite types
699
717
  nodes.find_all { |row| OID::TYPE_MAP.key? row['typelem'].to_i }.each do |row|
700
- vector = OID::Vector.new row['typdelim'], OID::TYPE_MAP[row['typelem'].to_i]
718
+ if OID.registered_type? row['typname']
719
+ # this composite type is explicitly registered
720
+ vector = OID::NAMES[row['typname']]
721
+ else
722
+ # use the default for composite types
723
+ vector = OID::Vector.new row['typdelim'], OID::TYPE_MAP[row['typelem'].to_i]
724
+ end
725
+
701
726
  OID::TYPE_MAP[row['oid'].to_i] = vector
702
727
  end
703
728
 
@@ -884,8 +909,12 @@ module ActiveRecord
884
909
  $1.strip if $1
885
910
  end
886
911
 
887
- def table_definition
888
- TableDefinition.new(self)
912
+ def create_table_definition(name, temporary, options)
913
+ TableDefinition.new native_database_types, name, temporary, options
914
+ end
915
+
916
+ def update_table_definition(table_name, base)
917
+ Table.new(table_name, base)
889
918
  end
890
919
  end
891
920
  end