activerecord-postgresql-extensions 0.0.12 → 0.1.0

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