pg_saurus 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.markdown +341 -0
- data/lib/colorized_text.rb +33 -0
- data/lib/core_ext/active_record/connection_adapters/abstract/schema_statements.rb +155 -0
- data/lib/core_ext/active_record/connection_adapters/postgresql_adapter.rb +191 -0
- data/lib/core_ext/active_record/errors.rb +6 -0
- data/lib/core_ext/active_record/schema_dumper.rb +42 -0
- data/lib/pg_saurus/connection_adapters/abstract_adapter/comment_methods.rb +80 -0
- data/lib/pg_saurus/connection_adapters/abstract_adapter/foreigner_methods.rb +67 -0
- data/lib/pg_saurus/connection_adapters/abstract_adapter/index_methods.rb +6 -0
- data/lib/pg_saurus/connection_adapters/abstract_adapter/schema_methods.rb +20 -0
- data/lib/pg_saurus/connection_adapters/abstract_adapter.rb +20 -0
- data/lib/pg_saurus/connection_adapters/foreign_key_definition.rb +5 -0
- data/lib/pg_saurus/connection_adapters/index_definition.rb +8 -0
- data/lib/pg_saurus/connection_adapters/postgresql_adapter/comment_methods.rb +114 -0
- data/lib/pg_saurus/connection_adapters/postgresql_adapter/extension_methods.rb +124 -0
- data/lib/pg_saurus/connection_adapters/postgresql_adapter/foreigner_methods.rb +221 -0
- data/lib/pg_saurus/connection_adapters/postgresql_adapter/index_methods.rb +42 -0
- data/lib/pg_saurus/connection_adapters/postgresql_adapter/schema_methods.rb +58 -0
- data/lib/pg_saurus/connection_adapters/postgresql_adapter/translate_exception.rb +20 -0
- data/lib/pg_saurus/connection_adapters/postgresql_adapter/view_methods.rb +17 -0
- data/lib/pg_saurus/connection_adapters/postgresql_adapter.rb +28 -0
- data/lib/pg_saurus/connection_adapters/table/comment_methods.rb +58 -0
- data/lib/pg_saurus/connection_adapters/table/foreigner_methods.rb +51 -0
- data/lib/pg_saurus/connection_adapters/table.rb +17 -0
- data/lib/pg_saurus/connection_adapters.rb +9 -0
- data/lib/pg_saurus/create_index_concurrently.rb +227 -0
- data/lib/pg_saurus/engine.rb +57 -0
- data/lib/pg_saurus/errors.rb +6 -0
- data/lib/pg_saurus/migration/command_recorder/comment_methods.rb +68 -0
- data/lib/pg_saurus/migration/command_recorder/extension_methods.rb +25 -0
- data/lib/pg_saurus/migration/command_recorder/foreigner_methods.rb +31 -0
- data/lib/pg_saurus/migration/command_recorder/schema_methods.rb +59 -0
- data/lib/pg_saurus/migration/command_recorder/view_methods.rb +31 -0
- data/lib/pg_saurus/migration/command_recorder.rb +17 -0
- data/lib/pg_saurus/migration.rb +4 -0
- data/lib/pg_saurus/schema_dumper/comment_methods.rb +51 -0
- data/lib/pg_saurus/schema_dumper/extension_methods.rb +29 -0
- data/lib/pg_saurus/schema_dumper/foreigner_methods.rb +63 -0
- data/lib/pg_saurus/schema_dumper/schema_methods.rb +27 -0
- data/lib/pg_saurus/schema_dumper/view_methods.rb +32 -0
- data/lib/pg_saurus/schema_dumper.rb +28 -0
- data/lib/pg_saurus/tools.rb +104 -0
- data/lib/pg_saurus/version.rb +4 -0
- data/lib/pg_saurus.rb +18 -0
- data/lib/tasks/pg_saurus_tasks.rake +4 -0
- metadata +226 -0
@@ -0,0 +1,124 @@
|
|
1
|
+
# Provides methods to extend {ActiveRecord::ConnectionAdapters::PostgreSQLAdapter}
|
2
|
+
# to support extensions feature.
|
3
|
+
module PgSaurus::ConnectionAdapters::PostgreSQLAdapter::ExtensionMethods
|
4
|
+
# Default options for {#create_extension} method.
|
5
|
+
CREATE_EXTENSION_DEFAULTS = {
|
6
|
+
:if_not_exists => true,
|
7
|
+
:schema_name => nil,
|
8
|
+
:version => nil,
|
9
|
+
:old_version => nil
|
10
|
+
}
|
11
|
+
|
12
|
+
# Default options for {#drop_extension} method.
|
13
|
+
DROP_EXTENSION_DEFAULTS = {
|
14
|
+
:if_exists => true,
|
15
|
+
:mode => :restrict
|
16
|
+
}
|
17
|
+
|
18
|
+
# The modes which determine postgresql behavior on DROP EXTENSION operation.
|
19
|
+
#
|
20
|
+
# *:restrict* refuse to drop the extension if any objects depend on it
|
21
|
+
# *:cascade* automatically drop objects that depend on the extension
|
22
|
+
AVAILABLE_DROP_MODES = {
|
23
|
+
:restrict => 'RESTRICT',
|
24
|
+
:cascade => 'CASCADE'
|
25
|
+
}
|
26
|
+
|
27
|
+
# @return [Boolean] if adapter supports postgresql extension manipulation
|
28
|
+
def supports_extensions?
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
# Execute SQL to load a postgresql extension module into the current database.
|
33
|
+
#
|
34
|
+
# @param [#to_s] extension_name Name of the extension module to load
|
35
|
+
# @param [Hash] options
|
36
|
+
# @option options [Boolean] :if_not_exists should the 'IF NOT EXISTS' clause be added
|
37
|
+
# @option options [#to_s,nil] :schema_name The name of the schema in which to install the extension's objects
|
38
|
+
# @option options [#to_s,nil] :version The version of the extension to install
|
39
|
+
# @option options [#to_s,nil] :old_version Alternative installation script name
|
40
|
+
# that absorbs the existing objects into the extension, instead of creating new objects
|
41
|
+
def create_extension(extension_name, options = {})
|
42
|
+
options = CREATE_EXTENSION_DEFAULTS.merge(options.symbolize_keys)
|
43
|
+
|
44
|
+
sql = ['CREATE EXTENSION']
|
45
|
+
sql << 'IF NOT EXISTS' if options[:if_not_exists]
|
46
|
+
sql << %Q{"#{extension_name.to_s}"}
|
47
|
+
sql << "SCHEMA #{options[:schema_name]}" if options[:schema_name].present?
|
48
|
+
sql << "VERSION '#{options[:version]}'" if options[:version].present?
|
49
|
+
sql << "FROM #{options[:old_version]}" if options[:old_version].present?
|
50
|
+
|
51
|
+
sql = sql.join(' ')
|
52
|
+
execute(sql)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Execute SQL to remove a postgresql extension module from the current database.
|
56
|
+
#
|
57
|
+
# @param [#to_s] extension_name Name of the extension module to unload
|
58
|
+
# @param [Hash] options
|
59
|
+
# @option options [Boolean] :if_exists should the 'IF EXISTS' clause be added
|
60
|
+
# @option options [Symbol] :mode Operation mode. See {AVAILABLE_DROP_MODES} for details
|
61
|
+
def drop_extension(extension_name, options = {})
|
62
|
+
options = DROP_EXTENSION_DEFAULTS.merge(options.symbolize_keys)
|
63
|
+
|
64
|
+
sql = ['DROP EXTENSION']
|
65
|
+
sql << 'IF EXISTS' if options[:if_exists]
|
66
|
+
sql << %Q{"#{extension_name.to_s}"}
|
67
|
+
|
68
|
+
mode = options[:mode]
|
69
|
+
if mode.present?
|
70
|
+
mode = mode.to_sym
|
71
|
+
|
72
|
+
unless AVAILABLE_DROP_MODES.include?(mode)
|
73
|
+
raise ArgumentError,
|
74
|
+
"Expected one of #{AVAILABLE_DROP_MODES.keys.inspect} drop modes, " \
|
75
|
+
"but #{mode} received"
|
76
|
+
end
|
77
|
+
|
78
|
+
sql << AVAILABLE_DROP_MODES[mode]
|
79
|
+
end
|
80
|
+
|
81
|
+
sql = sql.join(' ')
|
82
|
+
execute(sql)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Query the pg_catalog for all extension modules loaded to the current database.
|
86
|
+
#
|
87
|
+
# @note
|
88
|
+
# Since Rails 4 connection has method +extensions+ that returns array of extensions.
|
89
|
+
# This method is slightly different, since it returns also additional options.
|
90
|
+
#
|
91
|
+
# Please note all extensions which belong to pg_catalog schema are omitted
|
92
|
+
# ===Example
|
93
|
+
#
|
94
|
+
# extension # => {
|
95
|
+
# "fuzzystrmatch" => {:schema_name => "public", :version => "1.0" }
|
96
|
+
# }
|
97
|
+
#
|
98
|
+
# @return [Hash{String => Hash{Symbol => String}}] A list of loaded extensions with their options
|
99
|
+
def pg_extensions
|
100
|
+
# Check postgresql version to not break on Postgresql < 9.1 during schema dump
|
101
|
+
pg_version_str = select_value('SELECT version()')
|
102
|
+
return {} unless pg_version_str =~ /^PostgreSQL (\d+\.\d+.\d+)/ && ($1 >= '9.1')
|
103
|
+
|
104
|
+
sql = <<-SQL
|
105
|
+
SELECT pge.extname AS ext_name, pgn.nspname AS schema_name, pge.extversion AS ext_version
|
106
|
+
FROM pg_extension pge
|
107
|
+
INNER JOIN pg_namespace pgn on pge.extnamespace = pgn.oid
|
108
|
+
WHERE pgn.nspname <> 'pg_catalog'
|
109
|
+
SQL
|
110
|
+
|
111
|
+
result = select_all(sql)
|
112
|
+
formatted_result = result.map do |row|
|
113
|
+
[
|
114
|
+
row['ext_name'],
|
115
|
+
{
|
116
|
+
:schema_name => row['schema_name'],
|
117
|
+
:version => row['ext_version']
|
118
|
+
}
|
119
|
+
]
|
120
|
+
end
|
121
|
+
|
122
|
+
Hash[formatted_result]
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,221 @@
|
|
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
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Provides methods to extend {ActiveRecord::ConnectionAdapters::SchemaStatements}
|
2
|
+
# to support index features.
|
3
|
+
module PgSaurus::ConnectionAdapters::PostgreSQLAdapter::IndexMethods
|
4
|
+
def supports_partial_index?
|
5
|
+
true
|
6
|
+
end
|
7
|
+
|
8
|
+
# Overrides ActiveRecord::ConnectionAdapters::SchemaStatements.index_name
|
9
|
+
# to support schema notation. Converts dots in index name to underscores.
|
10
|
+
#
|
11
|
+
# === Example
|
12
|
+
# add_index 'demography.citizens', :country_id
|
13
|
+
# # produces
|
14
|
+
# CREATE INDEX "index_demography_citizens_on_country_id" ON "demography"."citizens" ("country_id")
|
15
|
+
# # instead of
|
16
|
+
# CREATE INDEX "index_demography.citizens_on_country_id" ON "demography"."citizens" ("country_id")
|
17
|
+
#
|
18
|
+
def index_name(table_name, options) #:nodoc:
|
19
|
+
super.gsub('.','_')
|
20
|
+
end
|
21
|
+
|
22
|
+
# Overrides ActiveRecord::ConnectionAdapters::SchemaStatements.index_name_for_remove
|
23
|
+
# to support schema notation. Prepends the schema name to the index name.
|
24
|
+
#
|
25
|
+
# === Example
|
26
|
+
# drop_index 'demography.citizens', :country_id
|
27
|
+
# # produces
|
28
|
+
# DROP INDEX "demography"."index_demography_citizens_on_country_id"
|
29
|
+
# # instead of
|
30
|
+
# DROP INDEX "index_demography_citizens_on_country_id"
|
31
|
+
#
|
32
|
+
def index_name_for_remove(table_name, options = {})
|
33
|
+
index_name = super
|
34
|
+
|
35
|
+
if table_name.include?('.') # schema notation
|
36
|
+
schema = table_name.split('.').first
|
37
|
+
"#{schema}.#{index_name}"
|
38
|
+
else
|
39
|
+
index_name
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# Provides methods to extend {ActiveRecord::ConnectionAdapters::PostgreSQLAdapter}
|
2
|
+
# to support schemas feature.
|
3
|
+
module PgSaurus::ConnectionAdapters::PostgreSQLAdapter::SchemaMethods
|
4
|
+
# Creates new schema in DB.
|
5
|
+
# @param [String] schema_name
|
6
|
+
def create_schema(schema_name)
|
7
|
+
::PgSaurus::Tools.create_schema(schema_name)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Drops schema in DB.
|
11
|
+
# @param [String] schema_name
|
12
|
+
def drop_schema(schema_name)
|
13
|
+
::PgSaurus::Tools.drop_schema(schema_name)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Move table to another schema
|
17
|
+
# @param [String] table table name. Can be with schema prefix e.g. "demography.people"
|
18
|
+
# @param [String] schema schema where table should be moved to.
|
19
|
+
def move_table_to_schema(table, schema)
|
20
|
+
::PgSaurus::Tools.move_table_to_schema(table, schema)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Create schema if it does not exist yet.
|
24
|
+
#
|
25
|
+
# @param schema_name [String]
|
26
|
+
def create_schema_if_not_exists(schema_name)
|
27
|
+
::PgSaurus::Tools.create_schema_if_not_exists(schema_name)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Drop schema if it exists.
|
31
|
+
#
|
32
|
+
# @param schema_name [String]
|
33
|
+
def drop_schema_if_exists(schema_name)
|
34
|
+
::PgSaurus::Tools.drop_schema_if_exists(schema_name)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Make method +tables+ return tables not only from public schema.
|
38
|
+
#
|
39
|
+
# @note
|
40
|
+
# Tables from public schema have no "public." prefix. It's done for
|
41
|
+
# compatibility with other libraries that relies on a table name.
|
42
|
+
# Tables from other schemas has appropriate prefix with schema name.
|
43
|
+
# See: https://github.com/TMXCredit/pg_power/pull/42
|
44
|
+
#
|
45
|
+
# @return [Array<String>] table names
|
46
|
+
def tables_with_non_public_schema_tables(*args)
|
47
|
+
public_tables = tables_without_non_public_schema_tables(*args)
|
48
|
+
|
49
|
+
non_public_tables =
|
50
|
+
query(<<-SQL, 'SCHEMA').map { |row| row[0] }
|
51
|
+
SELECT schemaname || '.' || tablename AS table
|
52
|
+
FROM pg_tables
|
53
|
+
WHERE schemaname NOT IN ('pg_catalog', 'information_schema', 'public')
|
54
|
+
SQL
|
55
|
+
|
56
|
+
public_tables + non_public_tables
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Extend ActiveRecord::ConnectionAdapter::PostgreSQLAdapter logic
|
2
|
+
# to wrap more pg-specific errors into specific exception classes
|
3
|
+
module PgSaurus::ConnectionAdapters::PostgreSQLAdapter::TranslateException
|
4
|
+
# # See http://www.postgresql.org/docs/9.1/static/errcodes-appendix.html
|
5
|
+
INSUFFICIENT_PRIVILEGE = "42501"
|
6
|
+
|
7
|
+
# Intercept insufficient privilege PGError and raise active_record wrapped database exception
|
8
|
+
def translate_exception(exception, message)
|
9
|
+
exception_result = exception.result
|
10
|
+
|
11
|
+
case exception_result.try(:error_field, PGresult::PG_DIAG_SQLSTATE)
|
12
|
+
when INSUFFICIENT_PRIVILEGE
|
13
|
+
exc_message = exception_result.try(:error_field, PGresult::PG_DIAG_MESSAGE_PRIMARY)
|
14
|
+
exc_message ||= message
|
15
|
+
::ActiveRecord::InsufficientPrivilege.new(exc_message, exception)
|
16
|
+
else
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Provides methods to extend {ActiveRecord::ConnectionAdapters::PostgreSQLAdapter}
|
2
|
+
# to support views feature.
|
3
|
+
module PgSaurus::ConnectionAdapters::PostgreSQLAdapter::ViewMethods
|
4
|
+
# Creates new view in DB.
|
5
|
+
# @param [String, Symbol] view_name
|
6
|
+
# @param [String] view_definition
|
7
|
+
def create_view(view_name, view_definition)
|
8
|
+
::PgSaurus::Tools.create_view(view_name, view_definition)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Drops view in DB.
|
12
|
+
# @param [String, Symbol] view_name
|
13
|
+
def drop_view(view_name)
|
14
|
+
::PgSaurus::Tools.drop_view(view_name)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Provides methods to extend {ActiveRecord::ConnectionAdapters::PostgreSQLAdapter}
|
2
|
+
# to support pg_saurus features.
|
3
|
+
module PgSaurus::ConnectionAdapters::PostgreSQLAdapter
|
4
|
+
extend ActiveSupport::Autoload
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
# TODO: Looks like explicit path specification can be omitted -- aignatyev 20120904
|
8
|
+
autoload :ExtensionMethods, 'pg_saurus/connection_adapters/postgresql_adapter/extension_methods'
|
9
|
+
autoload :SchemaMethods, 'pg_saurus/connection_adapters/postgresql_adapter/schema_methods'
|
10
|
+
autoload :CommentMethods, 'pg_saurus/connection_adapters/postgresql_adapter/comment_methods'
|
11
|
+
autoload :ForeignerMethods, 'pg_saurus/connection_adapters/postgresql_adapter/foreigner_methods'
|
12
|
+
autoload :IndexMethods, 'pg_saurus/connection_adapters/postgresql_adapter/index_methods'
|
13
|
+
autoload :TranslateException, 'pg_saurus/connection_adapters/postgresql_adapter/translate_exception'
|
14
|
+
autoload :ViewMethods, 'pg_saurus/connection_adapters/postgresql_adapter/view_methods'
|
15
|
+
|
16
|
+
include ExtensionMethods
|
17
|
+
include SchemaMethods
|
18
|
+
include CommentMethods
|
19
|
+
include ForeignerMethods
|
20
|
+
include IndexMethods
|
21
|
+
include TranslateException
|
22
|
+
include ViewMethods
|
23
|
+
|
24
|
+
included do
|
25
|
+
alias_method_chain :tables, :non_public_schema_tables
|
26
|
+
alias_method_chain :add_index, :concurrently
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# Provides methods to extend ActiveRecord::ConnectionAdapters::Table
|
2
|
+
# to support comments feature.
|
3
|
+
module PgSaurus::ConnectionAdapters::Table::CommentMethods
|
4
|
+
# Set the comment on the table.
|
5
|
+
#
|
6
|
+
# ===== Example
|
7
|
+
# ====== Set comment on table
|
8
|
+
# t.set_table_comment 'This table stores phone numbers that conform to the North American Numbering Plan.'
|
9
|
+
def set_table_comment(comment)
|
10
|
+
@base.set_table_comment(@table_name, comment)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Remove any comment from the table.
|
14
|
+
#
|
15
|
+
# ===== Example
|
16
|
+
# ====== Remove table comment
|
17
|
+
# t.remove_table_comment
|
18
|
+
def remove_table_comment
|
19
|
+
@base.remove_table_comment(@table_name)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Set the comment for a given column.
|
23
|
+
#
|
24
|
+
# ===== Example
|
25
|
+
# ====== Set comment on the npa column
|
26
|
+
# t.set_column_comment :npa, 'Numbering Plan Area Code - Allowed ranges: [2-9] for first digit, [0-9] for second and third digit.'
|
27
|
+
def set_column_comment(column_name, comment)
|
28
|
+
@base.set_column_comment(@table_name, column_name, comment)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Set comments on multiple columns. 'comments' is a hash of column_name => comment pairs.
|
32
|
+
#
|
33
|
+
# ===== Example
|
34
|
+
# ====== Setting comments on the columns of the phone_numbers table
|
35
|
+
# t.set_column_comments :npa => 'Numbering Plan Area Code - Allowed ranges: [2-9] for first digit, [0-9] for second and third digit.',
|
36
|
+
# :nxx => 'Central Office Number'
|
37
|
+
def set_column_comments(comments)
|
38
|
+
@base.set_column_comments(@table_name, comments)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Remove any comment for a given column.
|
42
|
+
#
|
43
|
+
# ===== Example
|
44
|
+
# ====== Remove comment from the npa column
|
45
|
+
# t.remove_column_comment :npa
|
46
|
+
def remove_column_comment(column_name)
|
47
|
+
@base.remove_column_comment(@table_name, column_name)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Remove any comments from the given columns.
|
51
|
+
#
|
52
|
+
# ===== Example
|
53
|
+
# ====== Remove comment from the npa and nxx columns
|
54
|
+
# t.remove_column_comment :npa, :nxx
|
55
|
+
def remove_column_comments(*column_names)
|
56
|
+
@base.remove_column_comments(@table_name, *column_names)
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,51 @@
|
|
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
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Provides methods to extend ActiveRecord::ConnectionAdapters::Table
|
2
|
+
# to support pg_saurus features.
|
3
|
+
module PgSaurus::ConnectionAdapters::Table
|
4
|
+
extend ActiveSupport::Autoload
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
autoload :CommentMethods
|
8
|
+
autoload :ForeignerMethods
|
9
|
+
|
10
|
+
include CommentMethods
|
11
|
+
include ForeignerMethods
|
12
|
+
|
13
|
+
|
14
|
+
included do
|
15
|
+
alias_method_chain :references, :foreign_keys
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module PgSaurus::ConnectionAdapters # :nodoc:
|
2
|
+
extend ActiveSupport::Autoload
|
3
|
+
|
4
|
+
autoload :AbstractAdapter
|
5
|
+
autoload :PostgreSQLAdapter, 'pg_saurus/connection_adapters/postgresql_adapter'
|
6
|
+
autoload :Table
|
7
|
+
autoload :ForeignKeyDefinition
|
8
|
+
autoload :IndexDefinition, 'pg_saurus/connection_adapters/index_definition'
|
9
|
+
end
|