activerecord-postgresql-extensions 0.0.7
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.
- data/MIT-LICENSE +23 -0
- data/README.rdoc +32 -0
- data/Rakefile +42 -0
- data/VERSION +1 -0
- data/lib/activerecord-postgresql-extensions.rb +30 -0
- data/lib/postgresql_extensions/foreign_key_associations.rb +367 -0
- data/lib/postgresql_extensions/postgresql_adapter_extensions.rb +646 -0
- data/lib/postgresql_extensions/postgresql_constraints.rb +579 -0
- data/lib/postgresql_extensions/postgresql_functions.rb +345 -0
- data/lib/postgresql_extensions/postgresql_geometry.rb +212 -0
- data/lib/postgresql_extensions/postgresql_indexes.rb +219 -0
- data/lib/postgresql_extensions/postgresql_languages.rb +80 -0
- data/lib/postgresql_extensions/postgresql_permissions.rb +322 -0
- data/lib/postgresql_extensions/postgresql_rules.rb +112 -0
- data/lib/postgresql_extensions/postgresql_schemas.rb +49 -0
- data/lib/postgresql_extensions/postgresql_sequences.rb +222 -0
- data/lib/postgresql_extensions/postgresql_tables.rb +308 -0
- data/lib/postgresql_extensions/postgresql_triggers.rb +131 -0
- data/lib/postgresql_extensions/postgresql_types.rb +17 -0
- data/lib/postgresql_extensions/postgresql_views.rb +103 -0
- data/postgresql-extensions.gemspec +50 -0
- data/test/adapter_test.rb +45 -0
- data/test/constraints_test.rb +98 -0
- data/test/functions_test.rb +112 -0
- data/test/geometry_test.rb +43 -0
- data/test/index_test.rb +68 -0
- data/test/languages_test.rb +48 -0
- data/test/permissions_test.rb +163 -0
- data/test/rules_test.rb +32 -0
- data/test/schemas_test.rb +43 -0
- data/test/sequences_test.rb +90 -0
- data/test/tables_test.rb +49 -0
- data/test/test_helper.rb +64 -0
- metadata +97 -0
@@ -0,0 +1,131 @@
|
|
1
|
+
|
2
|
+
require 'active_record/connection_adapters/postgresql_adapter'
|
3
|
+
|
4
|
+
module ActiveRecord
|
5
|
+
class InvalidTriggerCallType < ActiveRecordError #:nodoc:
|
6
|
+
def initialize(called)
|
7
|
+
super("Invalid trigger call type - #{called}")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class InvalidTriggerEvents < ActiveRecordError #:nodoc:
|
12
|
+
def initialize(events)
|
13
|
+
super("Invalid trigger event(s) - #{events.inspect}")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module ConnectionAdapters
|
18
|
+
class PostgreSQLAdapter < AbstractAdapter
|
19
|
+
# Creates a PostgreSQL trigger.
|
20
|
+
#
|
21
|
+
# The +called+ argument specifies when the trigger is called and
|
22
|
+
# can be either <tt>:before</tt> or <tt>:after</tt>.
|
23
|
+
#
|
24
|
+
# +events+ can be on or more of <tt>:insert</tt>,
|
25
|
+
# <tt>:update</tt> or <tt>:delete</tt>. There are no
|
26
|
+
# <tt>:select</tt> triggers, as SELECT generally doesn't modify
|
27
|
+
# the database.
|
28
|
+
#
|
29
|
+
# +table+ is obviously the table the trigger will be created on
|
30
|
+
# while +function+ is the name of the procedure to call when the
|
31
|
+
# trigger is fired.
|
32
|
+
#
|
33
|
+
# ==== Options
|
34
|
+
#
|
35
|
+
# * <tt>:for_each</tt> - defines whether the trigger will be fired
|
36
|
+
# on each row in a statement or on the statement itself. Possible
|
37
|
+
# values are <tt>:row</tt> and <tt>:statement</tt>, with
|
38
|
+
# <tt>:statement</tt> being the default.
|
39
|
+
# * <tt>:args</tt> - if the trigger function requires any
|
40
|
+
# arguments then this is the place to let everyone know about it.
|
41
|
+
#
|
42
|
+
# ==== Example
|
43
|
+
#
|
44
|
+
# ### ruby
|
45
|
+
# create_trigger(
|
46
|
+
# 'willie_nelsons_trigger',
|
47
|
+
# :before,
|
48
|
+
# :update,
|
49
|
+
# { :nylon => :guitar },
|
50
|
+
# 'strum_trigger',
|
51
|
+
# :for_each => :row
|
52
|
+
# )
|
53
|
+
# # => CREATE TRIGGER "willie_nelsons_trigger" BEFORE UPDATE
|
54
|
+
# # ON "nylon"."guitar" FOR EACH ROW EXECUTE PROCEDURE "test_trigger"();
|
55
|
+
def create_trigger(name, called, events, table, function, options = {})
|
56
|
+
execute PostgreSQLTriggerDefinition.new(self, name, called, events, table, function, options).to_s
|
57
|
+
end
|
58
|
+
|
59
|
+
# Drops a trigger.
|
60
|
+
#
|
61
|
+
# ==== Options
|
62
|
+
#
|
63
|
+
# * <tt>:if_exists</tt> - adds IF EXISTS.
|
64
|
+
# * <tt>:cascade</tt> - cascades changes down to objects referring
|
65
|
+
# to the trigger.
|
66
|
+
def drop_trigger(name, table, options = {})
|
67
|
+
sql = 'DROP TRIGGER '
|
68
|
+
sql << 'IF EXISTS ' if options[:if_exists]
|
69
|
+
sql << "#{quote_generic(name)} ON #{quote_table_name(table)}"
|
70
|
+
sql << ' CASCADE' if options[:cascade]
|
71
|
+
execute sql
|
72
|
+
end
|
73
|
+
|
74
|
+
# Renames a trigger.
|
75
|
+
def rename_trigger(name, table, new_name, options = {})
|
76
|
+
execute "ALTER TRIGGER #{quote_generic(name)} ON #{quote_table_name(table)} RENAME TO #{quote_generic(new_name)}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Creates a PostgreSQL trigger definition. This class isn't really
|
81
|
+
# meant to be used directly. You'd be better off sticking to
|
82
|
+
# PostgreSQLAdapter#create_trigger. Honestly.
|
83
|
+
class PostgreSQLTriggerDefinition
|
84
|
+
attr_accessor :base, :name, :called, :events, :table, :function, :options
|
85
|
+
|
86
|
+
def initialize(base, name, called, events, table, function, options = {}) #:nodoc:
|
87
|
+
assert_valid_called(called)
|
88
|
+
assert_valid_events(events)
|
89
|
+
assert_valid_for_each(options[:for_each])
|
90
|
+
|
91
|
+
@base, @name, @events, @called, @table, @function, @options =
|
92
|
+
base, name, events, called, table, function, options
|
93
|
+
end
|
94
|
+
|
95
|
+
def to_sql #:nodoc:
|
96
|
+
sql = "CREATE TRIGGER #{base.quote_generic(name)} #{called.to_s.upcase} "
|
97
|
+
sql << Array(events).collect { |e| e.to_s.upcase }.join(' OR ')
|
98
|
+
sql << " OF " << Array(options[:of]).collect { |o| base.quote_generic(o) }.join(', ') if options[:of].present?
|
99
|
+
sql << " ON #{base.quote_table_name(table)}"
|
100
|
+
sql << " FOR EACH #{options[:for_each].to_s.upcase}" if options[:for_each]
|
101
|
+
sql << " EXECUTE PROCEDURE #{base.quote_function(function)}(#{options[:args]})"
|
102
|
+
sql
|
103
|
+
end
|
104
|
+
alias :to_s :to_sql
|
105
|
+
|
106
|
+
private
|
107
|
+
CALLED_TYPES = %w{ before after }.freeze
|
108
|
+
EVENT_TYPES = %w{ insert update delete }.freeze
|
109
|
+
FOR_EACH_TYPES = %w{ row statement }.freeze
|
110
|
+
|
111
|
+
def assert_valid_called(c) #:nodoc:
|
112
|
+
if !CALLED_TYPES.include?(c.to_s.downcase)
|
113
|
+
raise ActiveRecord::InvalidTriggerCallType.new(c)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def assert_valid_events(events) #:nodoc:
|
118
|
+
check_events = Array(events).collect(&:to_s) - EVENT_TYPES
|
119
|
+
if !check_events.empty?
|
120
|
+
raise ActiveRecord::InvalidTriggerEvent.new(check_events)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def assert_valid_for_each(f) #:nodoc:
|
125
|
+
if !FOR_EACH_TYPES.include?(f.to_s.downcase)
|
126
|
+
raise ActiveRecord::InvalidTriggerForEach.new(f)
|
127
|
+
end unless f.nil?
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
require 'active_record/connection_adapters/postgresql_adapter'
|
3
|
+
|
4
|
+
module ActiveRecord
|
5
|
+
module ConnectionAdapters
|
6
|
+
class PostgreSQLAdapter < AbstractAdapter
|
7
|
+
# Returns an Array of available languages.
|
8
|
+
def types(name = nil)
|
9
|
+
query(%{SELECT typname FROM pg_type}, name).map { |row| row[0] }
|
10
|
+
end
|
11
|
+
|
12
|
+
def type_exists?(name)
|
13
|
+
types.include?(name.to_s)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
|
2
|
+
require 'active_record/connection_adapters/postgresql_adapter'
|
3
|
+
|
4
|
+
module ActiveRecord
|
5
|
+
module ConnectionAdapters
|
6
|
+
class PostgreSQLAdapter < AbstractAdapter
|
7
|
+
# Creates a new PostgreSQL view.
|
8
|
+
#
|
9
|
+
# +name+ is the name of the view. View quoting works the same as
|
10
|
+
# table quoting, so you can use PostgreSQLAdapter#with_schema and
|
11
|
+
# friends. See PostgreSQLAdapter#with_schema and
|
12
|
+
# PostgreSQLAdapter#quote_table_name for details.
|
13
|
+
#
|
14
|
+
# +query+ is the SELECT query to use for the view. This is just
|
15
|
+
# a straight-up String, so quoting rules will not apply.
|
16
|
+
#
|
17
|
+
# Note that you can grant privileges on views using the
|
18
|
+
# grant_view_privileges method and revoke them using
|
19
|
+
# revoke_view_privileges.
|
20
|
+
#
|
21
|
+
# ==== Options
|
22
|
+
#
|
23
|
+
# * <tt>:replace</tt> - adds a REPLACE clause, as in "CREATE OR
|
24
|
+
# REPLACE".
|
25
|
+
# * <tt>:temporary</tt> - adds a TEMPORARY clause.
|
26
|
+
# * <tt>:columns</tt> - you can rename the output columns as
|
27
|
+
# necessary. Note that this can be an Array and that it must be
|
28
|
+
# the same length as the number of output columns created by
|
29
|
+
# +query+.
|
30
|
+
#
|
31
|
+
# ==== Examples
|
32
|
+
#
|
33
|
+
# ### ruby
|
34
|
+
# create_view(:foo_view, 'SELECT * FROM bar')
|
35
|
+
# # => CREATE VIEW "foo_view" AS SELECT * FROM bar;
|
36
|
+
#
|
37
|
+
# create_view(
|
38
|
+
# { :geospatial => :foo_view },
|
39
|
+
# 'SELECT * FROM bar',
|
40
|
+
# :columns => [ :id, :name, :the_geom ]
|
41
|
+
# )
|
42
|
+
# # => CREATE VIEW "geospatial"."foo_view" ("id", "name", "the_geom") AS SELECT * FROM bar;
|
43
|
+
def create_view(name, query, options = {})
|
44
|
+
execute PostgreSQLViewDefinition.new(self, name, query, options).to_s
|
45
|
+
end
|
46
|
+
|
47
|
+
# Drops a view.
|
48
|
+
#
|
49
|
+
# ==== Options
|
50
|
+
#
|
51
|
+
# * <tt>:if_exists</tt> - adds IF EXISTS.
|
52
|
+
# * <tt>:cascade</tt> - adds CASCADE.
|
53
|
+
def drop_view(name, options = {})
|
54
|
+
sql = 'DROP VIEW '
|
55
|
+
sql << 'IF EXISTS ' if options[:if_exists]
|
56
|
+
sql << Array(name).collect { |v| quote_view_name(v) }.join(', ')
|
57
|
+
sql << ' CASCADE' if options[:cascade]
|
58
|
+
execute sql
|
59
|
+
end
|
60
|
+
|
61
|
+
# Renames a view.
|
62
|
+
def rename_view(name, new_name, options = {})
|
63
|
+
execute "ALTER TABLE #{quote_view_name(name)} RENAME TO #{quote_generic_ignore_schema(new_name)}"
|
64
|
+
end
|
65
|
+
|
66
|
+
# Change the ownership of a view.
|
67
|
+
def alter_view_owner(name, role, options = {})
|
68
|
+
execute "ALTER TABLE #{quote_view_name(name)} OWNER TO #{quote_role(role)}"
|
69
|
+
end
|
70
|
+
|
71
|
+
# Alter a view's schema.
|
72
|
+
def alter_view_schema(name, schema, options = {})
|
73
|
+
execute "ALTER TABLE #{quote_view_name(name)} SET SCHEMA #{quote_schema(schema)}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Creates a PostgreSQL view definition. This class isn't really meant
|
78
|
+
# to be used directly. Instead, see PostgreSQLAdapter#create_view
|
79
|
+
# for usage.
|
80
|
+
class PostgreSQLViewDefinition
|
81
|
+
attr_accessor :base, :name, :query, :options
|
82
|
+
|
83
|
+
def initialize(base, name, query, options = {}) #:nodoc:
|
84
|
+
@base, @name, @query, @options = base, name, query, options
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_sql #:nodoc:
|
88
|
+
sql = 'CREATE '
|
89
|
+
sql << 'OR REPLACE ' if options[:replace]
|
90
|
+
sql << 'TEMPORARY ' if options[:temporary]
|
91
|
+
sql << "VIEW #{base.quote_view_name(name)} "
|
92
|
+
if options[:columns]
|
93
|
+
sql << '(' << Array(options[:columns]).collect do |c|
|
94
|
+
base.quote_column_name(c)
|
95
|
+
end.join(', ') << ') '
|
96
|
+
end
|
97
|
+
sql << "AS #{query}"
|
98
|
+
sql
|
99
|
+
end
|
100
|
+
alias :to_s :to_sql
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{postgresql-extensions}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["J Smith"]
|
12
|
+
s.date = %q{2010-11-30}
|
13
|
+
s.description = %q{A whole bunch of extensions the Rails PostgreSQL adapter.}
|
14
|
+
s.email = %q{code@zoocasa.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
"README",
|
20
|
+
"Rakefile",
|
21
|
+
"lib/postgresql_adapter_extensions.rb",
|
22
|
+
"lib/postgresql_constraints.rb",
|
23
|
+
"lib/postgresql_functions.rb",
|
24
|
+
"lib/postgresql_geometry.rb",
|
25
|
+
"lib/postgresql_indexes.rb",
|
26
|
+
"lib/postgresql_languages.rb",
|
27
|
+
"lib/postgresql_permissions.rb",
|
28
|
+
"lib/postgresql_rules.rb",
|
29
|
+
"lib/postgresql_schemas.rb",
|
30
|
+
"lib/postgresql_sequences.rb",
|
31
|
+
"lib/postgresql_tables.rb",
|
32
|
+
"lib/postgresql_triggers.rb",
|
33
|
+
"lib/postgresql_views.rb"
|
34
|
+
]
|
35
|
+
s.homepage = %q{http://github.com/zoocasa/postgresql-extensions}
|
36
|
+
s.require_paths = ["lib"]
|
37
|
+
s.rubygems_version = %q{1.3.7}
|
38
|
+
s.summary = %q{A whole bunch of extensions the Rails PostgreSQL adapter.}
|
39
|
+
|
40
|
+
if s.respond_to? :specification_version then
|
41
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
42
|
+
s.specification_version = 3
|
43
|
+
|
44
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
45
|
+
else
|
46
|
+
end
|
47
|
+
else
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
|
2
|
+
$: << File.dirname(__FILE__)
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class AdapterExtensionTests < Test::Unit::TestCase
|
6
|
+
include PostgreSQLExtensionsTestHelper
|
7
|
+
|
8
|
+
def test_quote_table_name_with_schema_string
|
9
|
+
assert_equal(%{"foo"."bar"}, ARBC.quote_table_name('foo.bar'))
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_quote_table_name_with_schema_hash
|
13
|
+
assert_equal(%{"foo"."bar"}, ARBC.quote_table_name(:foo => :bar))
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_quote_table_name_with_current_schema
|
17
|
+
assert_equal(%{"foo"."bar"}, ARBC.with_schema(:foo) {
|
18
|
+
ARBC.quote_table_name(:bar)
|
19
|
+
})
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_quote_table_name_with_current_schema_ignored
|
23
|
+
assert_equal(%{"bar"}, ARBC.with_schema(:foo) {
|
24
|
+
ARBC.ignore_schema {
|
25
|
+
ARBC.quote_table_name(:bar)
|
26
|
+
}
|
27
|
+
})
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_quote_schema
|
31
|
+
assert_equal('PUBLIC', ARBC.quote_schema(:public))
|
32
|
+
assert_equal(%{"foo"}, ARBC.quote_schema(:foo))
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_other_quoting
|
36
|
+
assert_equal(%{"foo"}, ARBC.quote_generic(:foo))
|
37
|
+
assert_equal(%{"foo"}, ARBC.quote_role(:foo))
|
38
|
+
assert_equal(%{"foo"}, ARBC.quote_rule(:foo))
|
39
|
+
assert_equal(%{"foo"}, ARBC.quote_language(:foo))
|
40
|
+
assert_equal(%{"foo"}, ARBC.quote_sequence(:foo))
|
41
|
+
assert_equal(%{"foo"}, ARBC.quote_function(:foo))
|
42
|
+
assert_equal(%{"foo"}, ARBC.quote_view_name(:foo))
|
43
|
+
assert_equal(%{"foo"}, ARBC.quote_tablespace(:foo))
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
|
2
|
+
$: << File.dirname(__FILE__)
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class ConstraintTests < Test::Unit::TestCase
|
6
|
+
include PostgreSQLExtensionsTestHelper
|
7
|
+
|
8
|
+
def setup
|
9
|
+
clear_statements!
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_create_table_with_unique_constraint_on_table
|
13
|
+
Mig.create_table(:foo) do |t|
|
14
|
+
t.integer :bar_id
|
15
|
+
t.text :name
|
16
|
+
t.text :email
|
17
|
+
t.unique_constraint [ :id, :bar_id ]
|
18
|
+
t.unique_constraint [ :name, :email ], :tablespace => 'fubar'
|
19
|
+
end
|
20
|
+
|
21
|
+
assert_equal((<<-EOF).strip, statements[0])
|
22
|
+
CREATE TABLE "foo" (
|
23
|
+
"id" serial primary key,
|
24
|
+
"bar_id" integer,
|
25
|
+
"name" text,
|
26
|
+
"email" text,
|
27
|
+
UNIQUE ("id", "bar_id"),
|
28
|
+
UNIQUE ("name", "email") USING INDEX TABLESPACE "fubar"
|
29
|
+
)
|
30
|
+
EOF
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_create_table_with_unique_constraint_on_column
|
34
|
+
Mig.create_table(:foo) do |t|
|
35
|
+
t.integer :bar_id, :unique => true
|
36
|
+
end
|
37
|
+
|
38
|
+
assert_equal((<<-EOF).strip, statements[0])
|
39
|
+
CREATE TABLE "foo" (
|
40
|
+
"id" serial primary key,
|
41
|
+
"bar_id" integer,
|
42
|
+
UNIQUE ("bar_id")
|
43
|
+
)
|
44
|
+
EOF
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_add_unique_constraint
|
48
|
+
Mig.add_unique_constraint(:foo, :bar_id)
|
49
|
+
Mig.add_unique_constraint(
|
50
|
+
:foo,
|
51
|
+
:bar_id,
|
52
|
+
:tablespace => 'fubar',
|
53
|
+
:storage_parameters => 'FILLFACTOR=10',
|
54
|
+
:name => 'bar_id_unique'
|
55
|
+
)
|
56
|
+
|
57
|
+
assert_equal([
|
58
|
+
"ALTER TABLE \"foo\" ADD UNIQUE (\"bar_id\")",
|
59
|
+
"ALTER TABLE \"foo\" ADD CONSTRAINT \"bar_id_unique\" UNIQUE (\"bar_id\") WITH (FILLFACTOR=10) USING INDEX TABLESPACE \"fubar\""
|
60
|
+
], statements)
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_add_foreign_key
|
64
|
+
Mig.add_foreign_key(:foo, :bar_id, :bar)
|
65
|
+
Mig.add_foreign_key(:foo, :bar_id, :bar, :ogc_fid, :name => 'bar_fk')
|
66
|
+
Mig.add_foreign_key(:foo, [ :one_id, :bar_id ], :bar, [ :one_id, :bar_id ], :match => 'full')
|
67
|
+
Mig.add_foreign_key(:foo, :bar_id, :bar, :on_delete => :cascade, :on_delete => :set_default)
|
68
|
+
Mig.add_foreign_key(:foo, :bar_id, :bar, :deferrable => :immediate)
|
69
|
+
|
70
|
+
assert_equal([
|
71
|
+
"ALTER TABLE \"foo\" ADD FOREIGN KEY (\"bar_id\") REFERENCES \"bar\"",
|
72
|
+
"ALTER TABLE \"foo\" ADD CONSTRAINT \"bar_fk\" FOREIGN KEY (\"bar_id\") REFERENCES \"bar\" (\"ogc_fid\")",
|
73
|
+
"ALTER TABLE \"foo\" ADD FOREIGN KEY (\"one_id\", \"bar_id\") REFERENCES \"bar\" (\"one_id\", \"bar_id\") MATCH FULL",
|
74
|
+
"ALTER TABLE \"foo\" ADD FOREIGN KEY (\"bar_id\") REFERENCES \"bar\" ON DELETE SET DEFAULT",
|
75
|
+
"ALTER TABLE \"foo\" ADD FOREIGN KEY (\"bar_id\") REFERENCES \"bar\" DEFERRABLE INITIALLY IMMEDIATE"
|
76
|
+
], statements)
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_drop_constraint
|
80
|
+
Mig.drop_constraint(:foo, :bar)
|
81
|
+
Mig.drop_constraint(:foo, :bar, :cascade => true)
|
82
|
+
|
83
|
+
assert_equal([
|
84
|
+
"ALTER TABLE \"foo\" DROP CONSTRAINT \"bar\"",
|
85
|
+
"ALTER TABLE \"foo\" DROP CONSTRAINT \"bar\" CASCADE"
|
86
|
+
], statements)
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_add_check_constraint
|
90
|
+
Mig.add_check_constraint(:foo, 'length(name) < 100')
|
91
|
+
Mig.add_check_constraint(:foo, 'length(name) < 100', :name => 'name_length_check')
|
92
|
+
|
93
|
+
assert_equal([
|
94
|
+
"ALTER TABLE \"foo\" ADD CHECK (length(name) < 100)",
|
95
|
+
"ALTER TABLE \"foo\" ADD CONSTRAINT \"name_length_check\" CHECK (length(name) < 100)"
|
96
|
+
], statements)
|
97
|
+
end
|
98
|
+
end
|