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.
@@ -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