pg_saurus 2.6.0 → 3.0.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.
- checksums.yaml +4 -4
- data/README.markdown +141 -98
- data/lib/core_ext/active_record/connection_adapters/postgresql_adapter.rb +4 -2
- data/lib/pg_saurus/connection_adapters/abstract_adapter.rb +0 -2
- data/lib/pg_saurus/connection_adapters/postgresql_adapter/foreign_key_methods.rb +105 -0
- data/lib/pg_saurus/connection_adapters/postgresql_adapter/schema_methods.rb +8 -0
- data/lib/pg_saurus/connection_adapters/postgresql_adapter.rb +14 -2
- data/lib/pg_saurus/connection_adapters/table/comment_methods.rb +6 -6
- data/lib/pg_saurus/connection_adapters/table/trigger_methods.rb +2 -2
- data/lib/pg_saurus/connection_adapters/table.rb +0 -6
- data/lib/pg_saurus/connection_adapters.rb +0 -1
- data/lib/pg_saurus/create_index_concurrently.rb +1 -46
- data/lib/pg_saurus/migration/command_recorder.rb +0 -2
- data/lib/pg_saurus/schema_dumper/foreign_key_methods.rb +49 -0
- data/lib/pg_saurus/schema_dumper.rb +4 -3
- data/lib/pg_saurus/version.rb +1 -1
- metadata +10 -78
- data/lib/pg_saurus/connection_adapters/abstract_adapter/foreigner_methods.rb +0 -67
- data/lib/pg_saurus/connection_adapters/postgresql_adapter/foreigner_methods.rb +0 -221
- data/lib/pg_saurus/connection_adapters/table/foreigner_methods.rb +0 -51
- data/lib/pg_saurus/migration/command_recorder/foreigner_methods.rb +0 -31
- data/lib/pg_saurus/schema_dumper/foreigner_methods.rb +0 -63
@@ -1,221 +0,0 @@
|
|
1
|
-
module PgSaurus # :nodoc:
|
2
|
-
# Provides methods to extend {ActiveRecord::ConnectionAdapters::PostgreSQLAdapter}
|
3
|
-
# to support foreign keys feature.
|
4
|
-
module ConnectionAdapters::PostgreSQLAdapter::ForeignerMethods
|
5
|
-
def supports_foreign_keys?
|
6
|
-
true
|
7
|
-
end
|
8
|
-
|
9
|
-
# Fetch information about foreign keys related to the passed table.
|
10
|
-
# @param [String, Symbol] table_name name of table (e.g. "users", "music.bands")
|
11
|
-
# @return [Foreigner::ConnectionAdapters::ForeignKeyDefinition]
|
12
|
-
def foreign_keys(table_name)
|
13
|
-
relation, schema = table_name.to_s.split('.', 2).reverse
|
14
|
-
quoted_schema = schema ? "'#{schema}'" : "ANY (current_schemas(false))"
|
15
|
-
|
16
|
-
fk_info = select_all <<-SQL
|
17
|
-
SELECT nsp.nspname || '.' || t2.relname AS to_table,
|
18
|
-
a1.attname AS column ,
|
19
|
-
a2.attname AS primary_key,
|
20
|
-
c.conname AS name ,
|
21
|
-
c.confdeltype AS dependency
|
22
|
-
FROM pg_constraint c
|
23
|
-
JOIN pg_class t1 ON c.conrelid = t1.oid
|
24
|
-
JOIN pg_class t2 ON c.confrelid = t2.oid
|
25
|
-
JOIN pg_attribute a1 ON a1.attnum = c.conkey[1] AND a1.attrelid = t1.oid
|
26
|
-
JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
|
27
|
-
JOIN pg_namespace t3 ON c.connamespace = t3.oid
|
28
|
-
JOIN pg_namespace nsp ON nsp.oid = t2.relnamespace
|
29
|
-
WHERE c.contype = 'f'
|
30
|
-
AND t1.relname = '#{relation}'
|
31
|
-
AND t3.nspname = #{quoted_schema}
|
32
|
-
ORDER BY c.conname
|
33
|
-
SQL
|
34
|
-
|
35
|
-
fk_info.map do |row|
|
36
|
-
options = { :column => row['column'],
|
37
|
-
:name => row['name'],
|
38
|
-
:primary_key => row['primary_key'] }
|
39
|
-
|
40
|
-
options[:dependent] =
|
41
|
-
case row['dependency']
|
42
|
-
when 'c' then :delete
|
43
|
-
when 'n' then :nullify
|
44
|
-
when 'r' then :restrict
|
45
|
-
end
|
46
|
-
|
47
|
-
PgSaurus::ConnectionAdapters::ForeignKeyDefinition.new(table_name, row['to_table'], options)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
# Drop table and optionally disable triggers.
|
52
|
-
# Changes adapted from https://github.com/matthuhiggins/foreigner/blob/e72ab9c454c156056d3f037d55e3359cd972af32/lib/foreigner/connection_adapters/sql2003.rb
|
53
|
-
# NOTE: Disabling referential integrity requires superuser access in postgres.
|
54
|
-
# Default AR behavior is just to drop_table.
|
55
|
-
#
|
56
|
-
# == Options:
|
57
|
-
# * :force - force disabling of referential integrity
|
58
|
-
#
|
59
|
-
# Note: I don't know a good way to test this -mike 20120420
|
60
|
-
def drop_table(*args)
|
61
|
-
options = args.clone.extract_options!
|
62
|
-
if options[:force]
|
63
|
-
disable_referential_integrity { super }
|
64
|
-
else
|
65
|
-
super
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
# Add foreign key.
|
70
|
-
#
|
71
|
-
# Ensures that an index is created for the foreign key, unless :exclude_index is true.
|
72
|
-
#
|
73
|
-
# == Options:
|
74
|
-
# * :column
|
75
|
-
# * :primary_key
|
76
|
-
# * :dependent
|
77
|
-
# * :exclude_index [Boolean]
|
78
|
-
# * :concurrent_index [Boolean]
|
79
|
-
#
|
80
|
-
# @param [String, Symbol] from_table
|
81
|
-
# @param [String, Symbol] to_table
|
82
|
-
# @param [Hash] options
|
83
|
-
# @option options [String, Symbol] :column
|
84
|
-
# @option options [String, Symbol] :primary_key
|
85
|
-
# @option options [Hash] :dependent
|
86
|
-
# @option options [Boolean] :exclude_index
|
87
|
-
# @option options [Boolean] :concurrent_index
|
88
|
-
#
|
89
|
-
# @raise [PgSaurus::IndexExistsError] when :exclude_index is true, but the index already exists
|
90
|
-
def add_foreign_key(from_table, to_table, options = {})
|
91
|
-
options[:column] ||= id_column_name_from_table_name(to_table)
|
92
|
-
options[:exclude_index] ||= false
|
93
|
-
|
94
|
-
if index_exists?(from_table, options[:column]) && !options[:exclude_index]
|
95
|
-
raise PgSaurus::IndexExistsError,
|
96
|
-
"The index, #{index_name(from_table, options[:column])}, already exists." \
|
97
|
-
" Use :exclude_index => true when adding the foreign key."
|
98
|
-
end
|
99
|
-
|
100
|
-
sql = "ALTER TABLE #{quote_table_name(from_table)} #{add_foreign_key_sql(from_table, to_table, options)}"
|
101
|
-
execute(sql)
|
102
|
-
|
103
|
-
# GOTCHA:
|
104
|
-
# Index can not be created concurrently inside transaction in PostgreSQL.
|
105
|
-
# So, in case of concurrently created index with foreign key only
|
106
|
-
# foreign key will be created inside migration transaction and after
|
107
|
-
# closing transaction queries for index creation will be send to database.
|
108
|
-
# That's why I prevent here normal index creation in case of
|
109
|
-
# `concurrent_index` option is given.
|
110
|
-
# NOTE: Index creation after closing migration transaction could lead
|
111
|
-
# to weird effects when transaction moves smoothly, but index
|
112
|
-
# creation with error. In that case transaction will not be rolled back.
|
113
|
-
# As it was closed before even index was attempted to create.
|
114
|
-
# -- zekefast 2012-09-12
|
115
|
-
unless options[:exclude_index] || options[:concurrent_index]
|
116
|
-
add_index(from_table, options[:column])
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
# Return the SQL code fragment to add the foreign key based on the table names and options.
|
121
|
-
def add_foreign_key_sql(from_table, to_table, options = {})
|
122
|
-
column = options[:column]
|
123
|
-
options_options = options[:options]
|
124
|
-
foreign_key_name = foreign_key_name(from_table, column, options)
|
125
|
-
primary_key = options[:primary_key] || "id"
|
126
|
-
dependency = dependency_sql(options[:dependent])
|
127
|
-
|
128
|
-
sql =
|
129
|
-
"ADD CONSTRAINT #{quote_column_name(foreign_key_name)} " +
|
130
|
-
"FOREIGN KEY (#{quote_column_name(column)}) " +
|
131
|
-
"REFERENCES #{quote_table_name(ActiveRecord::Migrator.proper_table_name(to_table))}(#{primary_key})"
|
132
|
-
sql << " #{dependency}" if dependency.present?
|
133
|
-
sql << " #{options_options}" if options_options
|
134
|
-
|
135
|
-
sql
|
136
|
-
end
|
137
|
-
|
138
|
-
#
|
139
|
-
# TODO Determine if we can refactor the method signature
|
140
|
-
# remove_foreign_key(from_table, to_table_or_options_hash, options={}) => remove_foreign_key(from_table, to_table, options={})
|
141
|
-
#
|
142
|
-
# Remove the foreign key.
|
143
|
-
# @param [String, Symbol] from_table
|
144
|
-
# @param [String, Hash] to_table_or_options_hash
|
145
|
-
#
|
146
|
-
# The flexible method signature allows calls of two principal forms. Examples:
|
147
|
-
#
|
148
|
-
# remove_foreign_key(from_table, options_hash)
|
149
|
-
#
|
150
|
-
# or:
|
151
|
-
#
|
152
|
-
# remove_foreign_key(from_table, to_table, options_hash)
|
153
|
-
#
|
154
|
-
def remove_foreign_key(from_table, to_table_or_options_hash, options={})
|
155
|
-
if Hash === to_table_or_options_hash
|
156
|
-
options = to_table_or_options_hash
|
157
|
-
column = options[:column]
|
158
|
-
foreign_key_name = foreign_key_name(from_table, column, options)
|
159
|
-
column ||= id_column_name_from_foreign_key_metadata(from_table, foreign_key_name)
|
160
|
-
else
|
161
|
-
column = id_column_name_from_table_name(to_table_or_options_hash)
|
162
|
-
foreign_key_name = foreign_key_name(from_table, column)
|
163
|
-
end
|
164
|
-
|
165
|
-
execute "ALTER TABLE #{quote_table_name(from_table)} #{remove_foreign_key_sql(foreign_key_name)}"
|
166
|
-
|
167
|
-
options[:exclude_index] ||= false
|
168
|
-
unless options[:exclude_index] || !index_exists?(from_table, column) then
|
169
|
-
remove_index(from_table, column)
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
# Return the SQL code fragment to remove foreign key based on table name and options.
|
174
|
-
def remove_foreign_key_sql(foreign_key_name)
|
175
|
-
"DROP CONSTRAINT #{quote_column_name(foreign_key_name)}"
|
176
|
-
end
|
177
|
-
|
178
|
-
# Build the foreign key column id from the referenced table.
|
179
|
-
def id_column_name_from_table_name(table)
|
180
|
-
"#{table.to_s.split('.').last.singularize}_id"
|
181
|
-
end
|
182
|
-
|
183
|
-
# Extract the foreign key column id from the foreign key metadata.
|
184
|
-
# @param [String, Symbol] from_table
|
185
|
-
# @param [String] foreign_key_name
|
186
|
-
def id_column_name_from_foreign_key_metadata(from_table, foreign_key_name)
|
187
|
-
keys = foreign_keys(from_table)
|
188
|
-
this_key = keys.find {|key| key.options[:name] == foreign_key_name}
|
189
|
-
this_key.options[:column]
|
190
|
-
end
|
191
|
-
private :id_column_name_from_foreign_key_metadata
|
192
|
-
|
193
|
-
# Build default name for constraint.
|
194
|
-
def foreign_key_name(table, column, options = {})
|
195
|
-
name = options[:name]
|
196
|
-
|
197
|
-
if !name.nil? then
|
198
|
-
name
|
199
|
-
else
|
200
|
-
prefix = table.gsub(".", "_")
|
201
|
-
"#{prefix}_#{column}_fk"
|
202
|
-
end
|
203
|
-
end
|
204
|
-
private :foreign_key_name
|
205
|
-
|
206
|
-
# Get the SQL code fragment that represents dependency for a constraint.
|
207
|
-
#
|
208
|
-
# @param dependency [Symbol] :nullify, :delete or :restrict
|
209
|
-
#
|
210
|
-
# @return [String]
|
211
|
-
def dependency_sql(dependency)
|
212
|
-
case dependency
|
213
|
-
when :nullify then "ON DELETE SET NULL"
|
214
|
-
when :delete then "ON DELETE CASCADE"
|
215
|
-
when :restrict then "ON DELETE RESTRICT"
|
216
|
-
else ""
|
217
|
-
end
|
218
|
-
end
|
219
|
-
private :dependency_sql
|
220
|
-
end
|
221
|
-
end
|
@@ -1,51 +0,0 @@
|
|
1
|
-
# Provides methods to extend ActiveRecord::ConnectionAdapters::Table
|
2
|
-
# to support foreign keys feature.
|
3
|
-
module PgSaurus::ConnectionAdapters::Table::ForeignerMethods
|
4
|
-
# Adds a new foreign key to the table. +to_table+ can be a single Symbol, or
|
5
|
-
# an Array of Symbols. See SchemaStatements#add_foreign_key
|
6
|
-
#
|
7
|
-
# ===== Examples
|
8
|
-
# ====== Creating a simple foreign key
|
9
|
-
# t.foreign_key(:people)
|
10
|
-
# ====== Defining the column
|
11
|
-
# t.foreign_key(:people, :column => :sender_id)
|
12
|
-
# ====== Creating a named foreign key
|
13
|
-
# t.foreign_key(:people, :column => :sender_id, :name => 'sender_foreign_key')
|
14
|
-
# ====== Defining the column of the +to_table+.
|
15
|
-
# t.foreign_key(:people, :column => :sender_id, :primary_key => :person_id)
|
16
|
-
def foreign_key(to_table, options = {})
|
17
|
-
@base.add_foreign_key(@table_name, to_table, options)
|
18
|
-
end
|
19
|
-
|
20
|
-
# Remove the given foreign key from the table.
|
21
|
-
#
|
22
|
-
# ===== Examples
|
23
|
-
# ====== Remove the suppliers_company_id_fk in the suppliers table.
|
24
|
-
# change_table :suppliers do |t|
|
25
|
-
# t.remove_foreign_key :companies
|
26
|
-
# end
|
27
|
-
# ====== Remove the foreign key named accounts_branch_id_fk in the accounts table.
|
28
|
-
# change_table :accounts do |t|
|
29
|
-
# t.remove_foreign_key :column => :branch_id
|
30
|
-
# end
|
31
|
-
# ====== Remove the foreign key named party_foreign_key in the accounts table.
|
32
|
-
# change_table :accounts do |t|
|
33
|
-
# t.remove_index :name => :party_foreign_key
|
34
|
-
# end
|
35
|
-
def remove_foreign_key(options)
|
36
|
-
@base.remove_foreign_key(@table_name, options)
|
37
|
-
end
|
38
|
-
|
39
|
-
# Deprecated
|
40
|
-
def references_with_foreign_keys(*args)
|
41
|
-
options = args.extract_options!
|
42
|
-
|
43
|
-
if options.delete(:foreign_key)
|
44
|
-
p ActiveSupport::Deprecation.send(:deprecation_message, caller,
|
45
|
-
":foreign_key in t.references is deprecated. " \
|
46
|
-
"Use t.foreign_key instead")
|
47
|
-
end
|
48
|
-
|
49
|
-
references_without_foreign_keys(*(args.dup << options))
|
50
|
-
end
|
51
|
-
end
|
@@ -1,31 +0,0 @@
|
|
1
|
-
# Provides methods to extend ActiveRecord::Migration::CommandRecorder to
|
2
|
-
# support foreign keys feature.
|
3
|
-
module PgSaurus::Migration::CommandRecorder::ForeignerMethods
|
4
|
-
# :nodoc:
|
5
|
-
def add_foreign_key(*args)
|
6
|
-
record(:add_foreign_key, args)
|
7
|
-
end
|
8
|
-
|
9
|
-
# :nodoc:
|
10
|
-
def remove_foreign_key(*args)
|
11
|
-
record(:remove_foreign_key, args)
|
12
|
-
end
|
13
|
-
|
14
|
-
# :nodoc:
|
15
|
-
def invert_add_foreign_key(args)
|
16
|
-
from_table, to_table, add_options = *args
|
17
|
-
add_options ||= {}
|
18
|
-
add_name_option = add_options[:name]
|
19
|
-
add_column_option = add_options[:column]
|
20
|
-
|
21
|
-
if add_name_option then
|
22
|
-
options = {:name => add_name_option}
|
23
|
-
elsif add_column_option
|
24
|
-
options = {:column => add_column_option}
|
25
|
-
else
|
26
|
-
options = to_table
|
27
|
-
end
|
28
|
-
|
29
|
-
[:remove_foreign_key, [from_table, options]]
|
30
|
-
end
|
31
|
-
end
|
@@ -1,63 +0,0 @@
|
|
1
|
-
# Provides methods to extend ActiveRecord::SchemaDumper to dump
|
2
|
-
# foreign keys.
|
3
|
-
module PgSaurus::SchemaDumper::ForeignerMethods
|
4
|
-
# Hooks ActiveRecord::SchemaDumper#table method to dump foreign keys.
|
5
|
-
def tables_with_foreign_keys(stream)
|
6
|
-
tables_without_foreign_keys(stream)
|
7
|
-
|
8
|
-
table_names = @connection.tables.sort
|
9
|
-
|
10
|
-
table_names.sort.each do |table|
|
11
|
-
next if ['schema_migrations', ignore_tables].flatten.any? do |ignored|
|
12
|
-
case ignored
|
13
|
-
when String; table == ignored
|
14
|
-
when Regexp; table =~ ignored
|
15
|
-
else
|
16
|
-
raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.'
|
17
|
-
end
|
18
|
-
end
|
19
|
-
foreign_keys(table, stream)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
|
24
|
-
# Find all foreign keys on passed table and writes appropriate
|
25
|
-
# statements to stream.
|
26
|
-
def foreign_keys(table_name, stream)
|
27
|
-
if (foreign_keys = @connection.foreign_keys(table_name)).any?
|
28
|
-
add_foreign_key_statements = foreign_keys.map do |foreign_key|
|
29
|
-
options = foreign_key.options
|
30
|
-
table_from_key = foreign_key.to_table
|
31
|
-
statement_parts = [ ('add_foreign_key ' + foreign_key.from_table.inspect) ]
|
32
|
-
statement_parts << table_from_key.inspect
|
33
|
-
statement_parts << (':name => ' + options[:name].inspect)
|
34
|
-
|
35
|
-
column_from_options = options[:column]
|
36
|
-
primary_key_from_options = options[:primary_key]
|
37
|
-
dependent_from_options = options[:dependent]
|
38
|
-
|
39
|
-
if column_from_options != "#{table_from_key.singularize}_id"
|
40
|
-
statement_parts << (":column => #{column_from_options.inspect}")
|
41
|
-
end
|
42
|
-
if primary_key_from_options != 'id'
|
43
|
-
statement_parts << (":primary_key => #{primary_key_from_options.inspect}")
|
44
|
-
end
|
45
|
-
if dependent_from_options.present?
|
46
|
-
statement_parts << (":dependent => #{dependent_from_options.inspect}")
|
47
|
-
end
|
48
|
-
|
49
|
-
# Always exclude the index
|
50
|
-
# If an index was created in a migration, it will get dumped to the schema
|
51
|
-
# separately from the foreign key. This will raise an exception if
|
52
|
-
# add_foreign_key is run without :exclude_index => true.
|
53
|
-
statement_parts << (':exclude_index => true')
|
54
|
-
|
55
|
-
' ' + statement_parts.join(', ')
|
56
|
-
end
|
57
|
-
|
58
|
-
stream.puts add_foreign_key_statements.sort.join("\n")
|
59
|
-
stream.puts
|
60
|
-
end
|
61
|
-
end
|
62
|
-
private :foreign_keys
|
63
|
-
end
|