activerecord-postgresql-extensions 0.0.12 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.gitignore +18 -0
  2. data/Gemfile +3 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +7 -2
  5. data/Rakefile +4 -17
  6. data/activerecord-postgresql-extensions.gemspec +13 -57
  7. data/lib/{postgresql_extensions/postgresql_adapter_extensions.rb → active_record/postgresql_extensions/adapter_extensions.rb} +44 -46
  8. data/lib/{postgresql_extensions/postgresql_constraints.rb → active_record/postgresql_extensions/constraints.rb} +121 -10
  9. data/lib/{postgresql_extensions/postgresql_extensions.rb → active_record/postgresql_extensions/extensions.rb} +1 -1
  10. data/lib/{postgresql_extensions → active_record/postgresql_extensions}/foreign_key_associations.rb +9 -1
  11. data/lib/{postgresql_extensions/postgresql_functions.rb → active_record/postgresql_extensions/functions.rb} +9 -3
  12. data/lib/{postgresql_extensions/postgresql_geometry.rb → active_record/postgresql_extensions/geometry.rb} +111 -35
  13. data/lib/{postgresql_extensions/postgresql_indexes.rb → active_record/postgresql_extensions/indexes.rb} +4 -2
  14. data/lib/{postgresql_extensions/postgresql_languages.rb → active_record/postgresql_extensions/languages.rb} +1 -1
  15. data/lib/{postgresql_extensions/postgresql_permissions.rb → active_record/postgresql_extensions/permissions.rb} +3 -3
  16. data/lib/active_record/postgresql_extensions/postgis.rb +53 -0
  17. data/lib/{postgresql_extensions/postgresql_roles.rb → active_record/postgresql_extensions/roles.rb} +1 -1
  18. data/lib/{postgresql_extensions/postgresql_rules.rb → active_record/postgresql_extensions/rules.rb} +3 -3
  19. data/lib/{postgresql_extensions/postgresql_schemas.rb → active_record/postgresql_extensions/schemas.rb} +1 -1
  20. data/lib/{postgresql_extensions/postgresql_sequences.rb → active_record/postgresql_extensions/sequences.rb} +2 -2
  21. data/lib/{postgresql_extensions/postgresql_tables.rb → active_record/postgresql_extensions/tables.rb} +18 -4
  22. data/lib/{postgresql_extensions/postgresql_tablespaces.rb → active_record/postgresql_extensions/tablespaces.rb} +1 -1
  23. data/lib/{postgresql_extensions/postgresql_text_search.rb → active_record/postgresql_extensions/text_search.rb} +3 -3
  24. data/lib/{postgresql_extensions/postgresql_triggers.rb → active_record/postgresql_extensions/triggers.rb} +1 -1
  25. data/lib/{postgresql_extensions/postgresql_types.rb → active_record/postgresql_extensions/types.rb} +1 -1
  26. data/lib/active_record/postgresql_extensions/utils.rb +23 -0
  27. data/lib/active_record/postgresql_extensions/version.rb +7 -0
  28. data/lib/{postgresql_extensions/postgresql_views.rb → active_record/postgresql_extensions/views.rb} +2 -2
  29. data/lib/activerecord-postgresql-extensions.rb +23 -22
  30. data/test/adapter_tests.rb +9 -9
  31. data/test/constraints_tests.rb +155 -0
  32. data/test/database.yml +17 -0
  33. data/test/geometry_tests.rb +224 -52
  34. data/test/index_tests.rb +16 -1
  35. data/test/rules_tests.rb +4 -4
  36. data/test/sequences_tests.rb +0 -22
  37. data/test/tables_tests.rb +28 -31
  38. data/test/test_helper.rb +70 -23
  39. data/test/trigger_tests.rb +5 -5
  40. metadata +112 -25
  41. data/VERSION +0 -1
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.kpf
2
+ *.komodoproject
3
+ *~
4
+ .DS_Store
5
+ *tmp*
6
+ pkg/
7
+ doc/
8
+ *.orig
9
+ *.patch
10
+ .vimrc_local
11
+ debug.log
12
+ *.gem
13
+ *.rbc
14
+ .bundle
15
+ .config
16
+ .yardoc
17
+ Gemfile.lock
18
+ test/local_database.yml
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2011 2167961 Ontario Inc., Zoocasa <code@zoocasa.com>
1
+ Copyright (c) 2012 2167961 Ontario Inc., Zoocasa <code@zoocasa.com>
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person
4
4
  obtaining a copy of this software and associated documentation
data/README.rdoc CHANGED
@@ -12,7 +12,7 @@ PostgreSQL adapter including:
12
12
  functions, sequences, views, schemas and triggers.
13
13
 
14
14
  * better support for creating indexes with expressions so you can use functions
15
- and GIST and GIN indexes.
15
+ and GiST and GIN indexes.
16
16
 
17
17
  * support for manipulating role privileges.
18
18
 
@@ -20,10 +20,15 @@ PostgreSQL adapter including:
20
20
  creating tables.
21
21
 
22
22
  * support for automatically detecting foreign key relationships and building
23
- ActiveRecord associations accordingly. This associations can be overriden
23
+ ActiveRecord associations accordingly. This associations can be overridden
24
24
  as necessary and are sane enough that they won't try to overwrite existing
25
25
  associations.
26
26
 
27
+ * support for some PostGIS functionality including some support for using
28
+ PostGIS columns in migrations. For manipulating PostGIS data, see our
29
+ geos-extensions gem at https://github.com/zoocasa/geos-extensions or
30
+ the RGeo project at https://github.com/dazuma/rgeo .
31
+
27
32
  == First Public Release Notice!
28
33
 
29
34
  This is the first public release of this gem, so while it has been kind to us
data/Rakefile CHANGED
@@ -8,6 +8,7 @@ gem 'rdoc', '~> 3.12'
8
8
  require 'rubygems/package_task'
9
9
  require 'rake/testtask'
10
10
  require 'rdoc/task'
11
+ require 'bundler/gem_tasks'
11
12
 
12
13
  if RUBY_VERSION >= '1.9'
13
14
  begin
@@ -19,27 +20,13 @@ end
19
20
 
20
21
  $:.push 'lib'
21
22
 
22
- version = File.read('VERSION') rescue ''
23
-
24
- begin
25
- require 'jeweler'
26
- Jeweler::Tasks.new do |gem|
27
- gem.name = "activerecord-postgresql-extensions"
28
- gem.summary = "A whole bunch of extensions the ActiveRecord PostgreSQL adapter."
29
- gem.description = gem.summary
30
- gem.email = "code@zoocasa.com"
31
- gem.homepage = "http://github.com/zoocasa/activerecord-postgresql-extensions"
32
- gem.authors = [ "J Smith" ]
33
- end
34
- Jeweler::GemcutterTasks.new
35
- rescue LoadError
36
- puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
37
- end
23
+ version = ActiveRecord::PostgreSQLExtensions::VERSION
38
24
 
39
25
  desc 'Test PostgreSQL extensions'
40
26
  Rake::TestTask.new(:test) do |t|
41
27
  t.test_files = FileList['test/**/*_tests.rb']
42
- t.verbose = false
28
+ t.verbose = !!ENV['VERBOSE_TESTS']
29
+ t.warning = !!ENV['WARNINGS']
43
30
  end
44
31
 
45
32
  desc 'Build docs'
@@ -1,76 +1,32 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
1
  # -*- encoding: utf-8 -*-
5
2
 
3
+ require File.expand_path('../lib/active_record/postgresql_extensions/version', __FILE__)
4
+
6
5
  Gem::Specification.new do |s|
7
6
  s.name = "activerecord-postgresql-extensions"
8
- s.version = "0.0.12"
7
+ s.version = ActiveRecord::PostgreSQLExtensions::VERSION
9
8
 
10
9
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
10
  s.authors = ["J Smith"]
12
- s.date = "2012-03-22"
13
11
  s.description = "A whole bunch of extensions the ActiveRecord PostgreSQL adapter."
12
+ s.summary = s.description
14
13
  s.email = "code@zoocasa.com"
15
14
  s.extra_rdoc_files = [
16
15
  "README.rdoc"
17
16
  ]
18
- s.files = [
19
- "MIT-LICENSE",
20
- "README.rdoc",
21
- "Rakefile",
22
- "VERSION",
23
- "activerecord-postgresql-extensions.gemspec",
24
- "lib/activerecord-postgresql-extensions.rb",
25
- "lib/postgresql_extensions/foreign_key_associations.rb",
26
- "lib/postgresql_extensions/postgresql_adapter_extensions.rb",
27
- "lib/postgresql_extensions/postgresql_constraints.rb",
28
- "lib/postgresql_extensions/postgresql_extensions.rb",
29
- "lib/postgresql_extensions/postgresql_functions.rb",
30
- "lib/postgresql_extensions/postgresql_geometry.rb",
31
- "lib/postgresql_extensions/postgresql_indexes.rb",
32
- "lib/postgresql_extensions/postgresql_languages.rb",
33
- "lib/postgresql_extensions/postgresql_permissions.rb",
34
- "lib/postgresql_extensions/postgresql_roles.rb",
35
- "lib/postgresql_extensions/postgresql_rules.rb",
36
- "lib/postgresql_extensions/postgresql_schemas.rb",
37
- "lib/postgresql_extensions/postgresql_sequences.rb",
38
- "lib/postgresql_extensions/postgresql_tables.rb",
39
- "lib/postgresql_extensions/postgresql_tablespaces.rb",
40
- "lib/postgresql_extensions/postgresql_text_search.rb",
41
- "lib/postgresql_extensions/postgresql_triggers.rb",
42
- "lib/postgresql_extensions/postgresql_types.rb",
43
- "lib/postgresql_extensions/postgresql_views.rb",
44
- "test/adapter_tests.rb",
45
- "test/constraints_tests.rb",
46
- "test/extensions_tests.rb",
47
- "test/functions_tests.rb",
48
- "test/geometry_tests.rb",
49
- "test/index_tests.rb",
50
- "test/languages_tests.rb",
51
- "test/permissions_tests.rb",
52
- "test/roles_tests.rb",
53
- "test/rules_tests.rb",
54
- "test/schemas_tests.rb",
55
- "test/sequences_tests.rb",
56
- "test/tables_tests.rb",
57
- "test/tablespace_tests.rb",
58
- "test/test_helper.rb",
59
- "test/text_search_tests.rb",
60
- "test/trigger_tests.rb"
61
- ]
17
+ s.files = `git ls-files`.split($\)
18
+ s.executables = s.files.grep(%r{^bin/}).map { |f| File.basename(f) }
19
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
62
20
  s.homepage = "http://github.com/zoocasa/activerecord-postgresql-extensions"
63
21
  s.require_paths = ["lib"]
64
- s.rubygems_version = "1.8.15"
65
- s.summary = "A whole bunch of extensions the ActiveRecord PostgreSQL adapter."
66
-
67
- if s.respond_to? :specification_version then
68
- s.specification_version = 3
69
22
 
70
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
71
- else
72
- end
23
+ s.add_dependency("activerecord", [">= 2.3"])
24
+ if RUBY_PLATFORM == "java"
25
+ s.add_dependency("activerecord-jdbcpostgresql-adapter")
73
26
  else
27
+ s.add_dependency("pg")
74
28
  end
29
+ s.add_dependency("rdoc")
30
+ s.add_dependency("rake", ["~> 0.9"])
75
31
  end
76
32
 
@@ -1,12 +1,13 @@
1
1
 
2
2
  module ActiveRecord
3
3
  module ConnectionAdapters
4
- class PostgreSQLAdapter < AbstractAdapter
4
+ class PostgreSQLAdapter
5
5
  if defined?(Rails)
6
6
  LOGGER_REGEXP = /^#{Rails.root}(?!\/vendor\/rails)/
7
7
 
8
- def query_with_extra_logging(sql, name = nil) #:nodoc:
8
+ def query_with_extra_logging(*args) #:nodoc:
9
9
  if ActiveRecord::Base.enable_extended_logging && Rails.logger && Rails.logger.level == Logger::DEBUG
10
+ sql = args.first
10
11
  unless (sql =~ /(pg_get_constraintdef|pg_attribute|pg_class)/)
11
12
  Rails.logger.debug
12
13
  Rails.logger.debug(caller.select { |x|
@@ -14,12 +15,13 @@ module ActiveRecord
14
15
  }.join("\n"))
15
16
  end
16
17
  end
17
- query_without_extra_logging(sql, name)
18
+ query_without_extra_logging(*args)
18
19
  end
19
20
  alias_method_chain :query, :extra_logging
20
21
 
21
- def execute_with_extra_logging(sql, name = nil) #:nodoc:
22
+ def execute_with_extra_logging(*args) #:nodoc:
22
23
  if ActiveRecord::Base.enable_extended_logging && Rails.logger && Rails.logger.level == Logger::DEBUG
24
+ sql = args.first
23
25
  unless (sql =~ /(pg_get_constraintdef|pg_attribute|pg_class)/)
24
26
  Rails.logger.debug
25
27
  Rails.logger.debug(caller.select { |x|
@@ -27,29 +29,11 @@ module ActiveRecord
27
29
  }.join("\n"))
28
30
  end
29
31
  end
30
- execute_without_extra_logging(sql, name)
32
+ execute_without_extra_logging(*args)
31
33
  end
32
34
  alias_method_chain :execute, :extra_logging
33
35
  end
34
36
 
35
- # There seems to be a bug in ActiveRecord where it isn't setting
36
- # the schema search path properly because it's using ',' as a
37
- # separator rather than /,\s+/.
38
- def schema_search_path_with_fix_csv=(schema_csv)
39
- if schema_csv
40
- csv = schema_csv.gsub(/,\s+/, ',')
41
- execute "SET search_path TO #{csv}"
42
- @schema_search_path = csv
43
- end
44
- end
45
- alias_method_chain :schema_search_path=, :fix_csv
46
-
47
- # Fix ActiveRecord bug when grabbing the current search_path.
48
- def schema_search_path_with_fix_csv
49
- @schema_search_path ||= query('SHOW search_path')[0][0].gsub(/,\s+/, ',')
50
- end
51
- alias_method_chain :schema_search_path, :fix_csv
52
-
53
37
  # with_schema is kind of like with_scope. It wraps various
54
38
  # object names in SQL statements into a PostgreSQL schema. You
55
39
  # can have multiple with_schemas wrapped around each other, and
@@ -82,7 +66,7 @@ module ActiveRecord
82
66
  #
83
67
  # with_schema :geospatial do
84
68
  # create_table(:test) do |t|
85
- # ignore_schema do
69
+ # ignore_scoped_schema do
86
70
  # t.integer(
87
71
  # :ref_id,
88
72
  # :references => {
@@ -106,11 +90,11 @@ module ActiveRecord
106
90
  # Here we see that we used the geospatial schema when naming the
107
91
  # test table and dropped back to not specifying a schema when
108
92
  # setting up the foreign key to the refs table. If we had not
109
- # used ignore_schema, the foreign key would have been defined
93
+ # used ignore_scoped_schema, the foreign key would have been defined
110
94
  # thusly:
111
95
  #
112
96
  # FOREIGN KEY ("ref_id") REFERENCES "geospatial"."refs" ("id")
113
- def ignore_schema
97
+ def ignore_scoped_schema
114
98
  with_schema nil do
115
99
  yield
116
100
  end
@@ -124,18 +108,24 @@ module ActiveRecord
124
108
  end
125
109
 
126
110
  # Get the current scoped schema.
127
- def current_schema
111
+ def current_scoped_schema
128
112
  scoped_schemas.last
129
113
  end
130
114
 
131
115
  # A generic quoting method for PostgreSQL.
132
- def quote_generic(g)
133
- PGconn.quote_ident(g.to_s)
116
+ if RUBY_PLATFORM == 'java'
117
+ def quote_generic(g)
118
+ quote_column_name(g)
119
+ end
120
+ else
121
+ def quote_generic(g)
122
+ PGconn.quote_ident(g.to_s)
123
+ end
134
124
  end
135
125
 
136
126
  # A generic quoting method for PostgreSQL that specifically ignores
137
127
  # any and all schemas.
138
- def quote_generic_ignore_schema(g)
128
+ def quote_generic_ignore_scoped_schema(g)
139
129
  if g.is_a?(Hash)
140
130
  quote_generic g.values.first
141
131
  else
@@ -149,8 +139,8 @@ module ActiveRecord
149
139
  if g.is_a?(Hash)
150
140
  "#{quote_schema(g.keys.first)}.#{quote_generic(g.values.first)}"
151
141
  else
152
- if current_schema
153
- quote_schema(current_schema) << '.'
142
+ if current_scoped_schema
143
+ quote_schema(current_scoped_schema) << '.'
154
144
  end.to_s << quote_generic(g)
155
145
  end
156
146
  end
@@ -215,13 +205,13 @@ module ActiveRecord
215
205
  # # => "geospatial"."epois"
216
206
  #
217
207
  # with_schema(:geospatial) do
218
- # ignore_schema do
208
+ # ignore_scoped_schema do
219
209
  # quote_table_name(:epois)
220
210
  # end
221
211
  # end
222
212
  # # => "epois"
223
213
  def quote_table_name_with_schemas(name)
224
- if current_schema || name.is_a?(Hash)
214
+ if current_scoped_schema || name.is_a?(Hash)
225
215
  quote_generic_with_schema(name)
226
216
  else
227
217
  quote_table_name_without_schemas(name)
@@ -472,15 +462,21 @@ module ActiveRecord
472
462
  end
473
463
  alias_method_chain :tables, :views
474
464
 
475
- def schema_search_path_with_csv_fix=(schema_csv) #:nodoc:
476
- self.schema_search_path_without_csv_fix = schema_csv.gsub(/, /, ',') if schema_csv
477
- end
478
- alias_method_chain :schema_search_path=, :csv_fix
465
+ unless RUBY_PLATFORM == "java"
466
+ # There seems to be a bug in ActiveRecord where it isn't setting
467
+ # the schema search path properly because it's using ',' as a
468
+ # separator rather than /,\s+/.
469
+ def schema_search_path_with_csv_fix=(schema_csv) #:nodoc:
470
+ self.schema_search_path_without_csv_fix = schema_csv.gsub(/,\s+/, ',') if schema_csv
471
+ end
472
+ alias_method_chain :schema_search_path=, :csv_fix
479
473
 
480
- def schema_search_path_with_csv_fix #:nodoc:
481
- @schema_search_path ||= query('SHOW search_path;')[0][0].gsub(/, /, ',')
474
+ # Fix ActiveRecord bug when grabbing the current search_path.
475
+ def schema_search_path_with_csv_fix
476
+ @schema_search_path ||= query('SHOW search_path;')[0][0].gsub(/,\s+/, ',')
477
+ end
478
+ alias_method_chain :schema_search_path, :csv_fix
482
479
  end
483
- alias_method_chain :schema_search_path, :csv_fix
484
480
 
485
481
  def disable_referential_integrity_with_views #:nodoc:
486
482
  if supports_disable_referential_integrity? then
@@ -682,11 +678,13 @@ module ActiveRecord
682
678
  alias_method_chain :change_column_default, :expression
683
679
  end
684
680
 
685
- class PostgreSQLColumn < Column
681
+ class PostgreSQLColumn
686
682
  def simplified_type_with_additional_types(field_type)
687
683
  case field_type
688
684
  when 'geometry'
689
685
  :geometry
686
+ when 'geography'
687
+ :geography
690
688
  when 'tsvector'
691
689
  :tsvector
692
690
  else
@@ -707,8 +705,8 @@ module ActiveRecord
707
705
  }
708
706
  end
709
707
 
710
- def ignore_schema
711
- self.connection.ignore_schema { |*block_args|
708
+ def ignore_scoped_schema
709
+ self.connection.ignore_scoped_schema { |*block_args|
712
710
  yield(*block_args)
713
711
  }
714
712
  end
@@ -717,8 +715,8 @@ module ActiveRecord
717
715
  self.connection.scope_schemas
718
716
  end
719
717
 
720
- def current_schema
721
- self.connection.current_schema
718
+ def current_scoped_schema
719
+ self.connection.current_scoped_schema
722
720
  end
723
721
 
724
722
  def sequence_exists?
@@ -1,4 +1,7 @@
1
1
 
2
+ require 'active_record/connection_adapters/postgresql_adapter'
3
+ require 'active_record/postgresql_extensions/utils'
4
+
2
5
  module ActiveRecord
3
6
  class InvalidForeignKeyAction < ActiveRecordError #:nodoc:
4
7
  def initialize(action)
@@ -25,7 +28,7 @@ module ActiveRecord
25
28
  end
26
29
 
27
30
  module ConnectionAdapters
28
- class PostgreSQLAdapter < AbstractAdapter
31
+ class PostgreSQLAdapter
29
32
  # Adds a CHECK constraint to the table. See
30
33
  # PostgreSQLCheckConstraint for usage.
31
34
  def add_check_constraint(table, expression, options = {})
@@ -50,8 +53,16 @@ module ActiveRecord
50
53
  execute("#{sql};")
51
54
  end
52
55
 
56
+ # Adds an EXCLUDE constraint to the table. See
57
+ # PostgreSQLExcludeConstraint for details.
58
+ def add_exclude_constraint(table, excludes, options = {})
59
+ sql = "ALTER TABLE #{quote_table_name(table)} ADD "
60
+ sql << PostgreSQLExcludeConstraint.new(self, table, excludes, options).to_s
61
+ execute("#{sql};")
62
+ end
63
+
53
64
  # Drops a constraint from the table. Use this to drop CHECK,
54
- # UNIQUE and FOREIGN KEY constraints from a table.
65
+ # UNIQUE, EXCLUDE and FOREIGN KEY constraints from a table.
55
66
  #
56
67
  # Options:
57
68
  #
@@ -304,7 +315,7 @@ module ActiveRecord
304
315
  # PostgreSQLAdapter#create_index method similarly. The create_index
305
316
  # method adds a couple of PostgreSQL-specific options if you need them.
306
317
  #
307
- # ==== Examples:
318
+ # ==== Examples
308
319
  #
309
320
  # # using the constraint method:
310
321
  # add_unique_constraint(:foo, [ :fancy_id, :another_fancy_id ])
@@ -335,7 +346,8 @@ module ActiveRecord
335
346
  # couple of additional parameters to indexes to govern disk usage and
336
347
  # such. This option is a simple String that lets you insert these
337
348
  # options as necessary. See the PostgreSQL documentation on index
338
- # storage parameters for details.
349
+ # storage parameters for details. <tt>:index_parameters</tt> can also
350
+ # be used.
339
351
  # * <tt>:tablespace</tt> - allows you to specify a tablespace for the
340
352
  # unique index being created. See the PostgreSQL documentation on
341
353
  # tablespaces for details.
@@ -363,7 +375,7 @@ module ActiveRecord
363
375
  sql = "#{constraint_name}UNIQUE ("
364
376
  sql << Array(columns).collect { |c| base.quote_column_name(c) }.join(', ')
365
377
  sql << ")"
366
- sql << " WITH (#{options[:storage_parameters]})" if options[:storage_parameters]
378
+ sql << " WITH (#{options[:storage_parameters] || options[:index_parameters]})" if options[:storage_parameters] || options[:index_parameters]
367
379
  sql << " USING INDEX TABLESPACE #{base.quote_tablespace(options[:tablespace])}" if options[:tablespace]
368
380
  sql
369
381
  end
@@ -470,13 +482,13 @@ module ActiveRecord
470
482
  # You can also create new FOREIGN KEY constraints outside of a table
471
483
  # definition using PostgreSQLAdapter#add_foreign_key.
472
484
  #
473
- # ==== Examples:
485
+ # ==== Examples
474
486
  #
475
487
  # add_foreign_key(:foo, :bar_id, :bar)
476
- # # => ALTER TABLE "funk" ADD FOREIGN KEY ("bar_id") REFERENCES "bar";
488
+ # # => ALTER TABLE "foo" ADD FOREIGN KEY ("bar_id") REFERENCES "bar";
477
489
  #
478
490
  # add_foreign_key(:foo, :bar_id, :bar, :id)
479
- # # => ALTER TABLE "funk" ADD FOREIGN KEY ("bar_id") REFERENCES "bar"("id");
491
+ # # => ALTER TABLE "foo" ADD FOREIGN KEY ("bar_id") REFERENCES "bar"("id");
480
492
  #
481
493
  # add_foreign_key(:foo, [ :bar_id, :blort_id ], :bar, [ :id, :blort_id ],
482
494
  # :name => 'my_fk', :match => :simple
@@ -505,7 +517,7 @@ module ActiveRecord
505
517
  # the <tt>:deferrable</tt>, <tt>:match</tt>, <tt>:on_delete</tt>
506
518
  # and <tt>:on_update</tt> options.
507
519
  #
508
- # === Dropping CHECK Constraints
520
+ # === Dropping FOREIGN KEY Constraints
509
521
  #
510
522
  # Like all PostgreSQL constraints, you can use
511
523
  # PostgreSQLAdapter#drop_constraint to remove a constraint from a
@@ -522,7 +534,7 @@ module ActiveRecord
522
534
  assert_valid_action(options[:on_update]) if options[:on_update]
523
535
  assert_valid_deferrable_option(options[:deferrable])
524
536
  @columns, @ref_table, @ref_columns = columns, ref_table, ref_columns
525
- @schema = base.current_schema
537
+ @schema = base.current_scoped_schema
526
538
  super(base, options)
527
539
  end
528
540
 
@@ -564,5 +576,104 @@ module ActiveRecord
564
576
  end
565
577
  end
566
578
  end
579
+
580
+ # Creates EXCLUDE constraints for PostgreSQL tables and columns.
581
+ #
582
+ # This class is meant to be used by PostgreSQL column and table
583
+ # definition and manipulation methods. There are two ways to create
584
+ # a EXCLUDE constraint:
585
+ #
586
+ # * on a table definition
587
+ # * when altering a table
588
+ #
589
+ # In both cases, a Hash or an Array of Hashes should be used to set the
590
+ # EXCLUDE constraint checks. The Hash(es) should be in the format
591
+ # <tt>{ :element => ..., :with => ... }</tt>, where <tt>:element</tt> is
592
+ # a column name or expression and <tt>:with</tt> is the operator to
593
+ # compare against. The key <tt>:operator</tt> is an alias for <tt>:where</tt>.
594
+ #
595
+ # === Table Definition
596
+ #
597
+ # EXCLUDE constraints can also be applied to the table directly
598
+ # rather than on a column definition.
599
+ #
600
+ # ==== Example:
601
+ #
602
+ # The following example produces the same result as above:
603
+ #
604
+ # create_table('foo') do |t|
605
+ # t.integer :blort
606
+ # t.exclude({
607
+ # :element => 'length(blort)',
608
+ # :with => '='
609
+ # }, {
610
+ # :name => 'exclude_blort_length'
611
+ # })
612
+ # end
613
+ #
614
+ # # Produces:
615
+ # #
616
+ # # CREATE TABLE "foo" (
617
+ # # "id" serial primary key,
618
+ # # "blort" text,
619
+ # # CONSTRAINT "exclude_blort_length" EXCLUDE (length(blort) WITH =)
620
+ # # );
621
+ #
622
+ # === Table Manipulation
623
+ #
624
+ # You can also create new EXCLUDE constraints outside of a table
625
+ # definition using PostgreSQLAdapter#add_exclude_constraint.
626
+ #
627
+ # ==== Examples
628
+ #
629
+ # add_exclude_constraint(:foo, { :element => :bar_id, :with => '=' })
630
+ # # => ALTER TABLE "foo" ADD EXCLUDE ("bar_id" WITH =);
631
+ #
632
+ # === Options for EXCLUDE Constraints
633
+ #
634
+ # * <tt>:using</tt> - sets the index type to be used. Usually this will
635
+ # <tt>:gist</tt>, but the default is left blank to allow for the PostgreSQL
636
+ # default which is <tt>:btree</tt>. See the PostgreSQL docs for details.
637
+ # * <tt>:storage_parameters</tt> - PostgreSQL allows you to add a
638
+ # couple of additional parameters to indexes to govern disk usage and
639
+ # such. This option is a simple String that lets you insert these
640
+ # options as necessary. See the PostgreSQL documentation on index
641
+ # storage parameters for details. <tt>:index_parameters</tt> can also
642
+ # be used.
643
+ # * <tt>:tablespace</tt> - allows you to specify a tablespace for the
644
+ # index being created. See the PostgreSQL documentation on
645
+ # tablespaces for details.
646
+ # * <tt>:conditions</tt> - sets the WHERE conditions for the EXCLUDE
647
+ # constraint. You can also use the <tt>:where</tt> option.
648
+ #
649
+ # === Dropping EXCLUDE Constraints
650
+ #
651
+ # Like all PostgreSQL constraints, you can use
652
+ # PostgreSQLAdapter#drop_constraint to remove a constraint from a
653
+ # table.
654
+ class PostgreSQLExcludeConstraint < PostgreSQLConstraint
655
+ attr_accessor :excludes
656
+
657
+ def initialize(base, table, excludes, options = {}) #:nodoc:
658
+ @excludes = ActiveRecord::PostgreSQLExtensions::Utils.hash_or_array_of_hashes(excludes)
659
+
660
+ super(base, options)
661
+ end
662
+
663
+ def to_sql #:nodoc:
664
+ sql = String.new
665
+ sql << "#{constraint_name}EXCLUDE "
666
+ sql << "USING #{base.quote_column_name(options[:using])} " if options[:using]
667
+ sql << "(" << excludes.collect { |e|
668
+ "#{e[:element]} WITH #{e[:with] || e[:operator]}"
669
+ }.join(', ')
670
+ sql << ")"
671
+ sql << " WITH (#{options[:index_parameters] || options[:storage_parameters]})" if options[:index_parameters] || options[:storage_parameters]
672
+ sql << " USING INDEX TABLESPACE #{base.quote_tablespace(options[:tablespace])}" if options[:tablespace]
673
+ sql << " WHERE (#{options[:conditions] || options[:where]})" if options[:conditions] || options[:where]
674
+ sql
675
+ end
676
+ alias :to_s :to_sql
677
+ end
567
678
  end
568
679
  end