pg_saurus 2.1.1
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 +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
|