pg_saurus 2.6.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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