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,227 @@
|
|
1
|
+
# Adds ability to configure in migration how index will be created.
|
2
|
+
# See more details how to create index concurrently in PostgreSQL at
|
3
|
+
# (see http://www.postgresql.org/docs/9.2/static/sql-createindex.html#SQL-CREATEINDEX-CONCURRENTLY).
|
4
|
+
#
|
5
|
+
# There are several things you should be aware when use option to create index
|
6
|
+
# concurrently.
|
7
|
+
# Index can not be created concurrently inside transaction and such indexes
|
8
|
+
# creation will be postponed till migration transaction will be closed.
|
9
|
+
# In case of migration failure and transaction was rolled back indexes will not
|
10
|
+
# be created concurrently. But if indexes which should be created concurrently
|
11
|
+
# run with errors migration's transaction won't be rolled back. Error in that
|
12
|
+
# case will be raised and migration process will be stoped.
|
13
|
+
#
|
14
|
+
# Migrations can not ensure that all indexes that tend to be created
|
15
|
+
# concurrently were created even if the query for such index creation run
|
16
|
+
# without errors. Such indexes creation are deferred because of its nature.
|
17
|
+
# So, it's up to you to ensure that indexes was really created or remove
|
18
|
+
# partially created invalid indexes.
|
19
|
+
#
|
20
|
+
# :concurrent_index option conflicts with :exclude_index option in method
|
21
|
+
# `add_foreign_key`. So, if you put them together exception will be raised.
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
#
|
25
|
+
# class AddIndexToNameForUsers < ActiveRecord::Migration
|
26
|
+
# def change
|
27
|
+
# add_index :users, :name, :concurrently => true
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# # or with foreign key
|
32
|
+
#
|
33
|
+
# class AddForeignKeyToRoleIdForUsers < ActiveRecord::Migration
|
34
|
+
# def change
|
35
|
+
# add_foreign_key :users, :roles, :concurrent_index => true
|
36
|
+
# end
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
module PgSaurus::CreateIndexConcurrently
|
40
|
+
# Provides ability to postpone index creation queries in migrations.
|
41
|
+
#
|
42
|
+
# Overrides `add_index` and `add_foreign_key` methods for migration to be
|
43
|
+
# able to prevent indexes creation inside scope of transaction if they have to
|
44
|
+
# be created concurrently.
|
45
|
+
# Allows to run creation of postponed indexes.
|
46
|
+
#
|
47
|
+
# This module included into ActiveRecord::Migration class to extend it with
|
48
|
+
# new features.
|
49
|
+
#
|
50
|
+
# All postponed index creation queries are stored inside migration instance.
|
51
|
+
module Migration
|
52
|
+
# @attribute postponed_queries
|
53
|
+
# @return [Array] list of arguments to call `add_index` method.
|
54
|
+
# @private
|
55
|
+
attr_accessor :postponed_queries
|
56
|
+
private :postponed_queries, :postponed_queries=
|
57
|
+
|
58
|
+
|
59
|
+
# Add a new index to the table. +column_name+ can be a single Symbol, or
|
60
|
+
# an Array of Symbols.
|
61
|
+
#
|
62
|
+
# @param [Symbol, String] table_name
|
63
|
+
# @param [Symbol, String, Array<Symbol, String>] column_name
|
64
|
+
# @param [optional, Hash] options
|
65
|
+
# @option options [Boolean] :unique
|
66
|
+
# @option options [Boolean] :concurrently
|
67
|
+
# @option options [String] :where
|
68
|
+
#
|
69
|
+
# @return [nil]
|
70
|
+
#
|
71
|
+
# @see ActiveRecord::ConnectionAdapters::SchemaStatements.add_index in pg_saurus gem
|
72
|
+
def add_index(table_name, column_name, options = {}, &block)
|
73
|
+
table_name = ::ActiveRecord::Migrator.proper_table_name(table_name)
|
74
|
+
# GOTCHA:
|
75
|
+
# checks if index should be created concurretnly then put it into
|
76
|
+
# the queue to wait till queue processing will be called (should be
|
77
|
+
# happended after closing transaction).
|
78
|
+
# Otherwise just delegate call to PgSaurus's `add_index`.
|
79
|
+
# Block is given for future compatibility.
|
80
|
+
# -- zekefast 2012-09-12
|
81
|
+
unless options[:concurrently]
|
82
|
+
return connection.add_index(table_name, column_name, options, &block)
|
83
|
+
end
|
84
|
+
|
85
|
+
enque(table_name, column_name, options, &block)
|
86
|
+
nil
|
87
|
+
end
|
88
|
+
|
89
|
+
# Add a foreign key.
|
90
|
+
#
|
91
|
+
# == Options:
|
92
|
+
# * :column
|
93
|
+
# * :primary_key
|
94
|
+
# * :dependent
|
95
|
+
# * :exclude_index [Boolean]
|
96
|
+
# * :concurrent_index [Boolean]
|
97
|
+
#
|
98
|
+
# @param [String, Symbol] from_table
|
99
|
+
# @param [String, Symbol] to_table
|
100
|
+
# @param [Hash] options
|
101
|
+
# @option options [String, Symbol] :column
|
102
|
+
# @option options [String, Symbol] :primary_key
|
103
|
+
# @option options [Hash] :dependent
|
104
|
+
# @option options [Boolean] :exclude_index
|
105
|
+
# @option options [Boolean] :concurrent_index
|
106
|
+
#
|
107
|
+
# @raise [ArgumentError] in case of conflicted option were set
|
108
|
+
#
|
109
|
+
# @see ::PgSaurus::ConnectionAdapters::PostgreSQLAdapter::ForeignerMethods.add_foreign_key
|
110
|
+
def add_foreign_key(from_table, to_table, options = {}, &block)
|
111
|
+
from_table = ::ActiveRecord::Migrator.proper_table_name(from_table)
|
112
|
+
concurrent_index = options[:concurrent_index]
|
113
|
+
|
114
|
+
if concurrent_index then
|
115
|
+
if options[:exclude_index]
|
116
|
+
raise ArgumentError, 'Conflicted options(exclude_index, concurrent_index) was found, both are set to true.'
|
117
|
+
end
|
118
|
+
|
119
|
+
options[:column] ||= connection.id_column_name_from_table_name(to_table)
|
120
|
+
options = options.merge(:concurrently => concurrent_index)
|
121
|
+
|
122
|
+
index_options = { :concurrently => true }
|
123
|
+
enque(from_table, options[:column], index_options)
|
124
|
+
end
|
125
|
+
|
126
|
+
# GOTCHA:
|
127
|
+
# proceed foreign key creation, but giving :concurrent_index => true
|
128
|
+
# prevent normal index creation in PgSaurus's `add_foreign_key`.
|
129
|
+
# So, postponed creation could be done after transaction.
|
130
|
+
# -- zekefast 2012-09-12
|
131
|
+
connection.add_foreign_key(from_table, to_table, options, &block)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Execute all postponed index creation.
|
135
|
+
#
|
136
|
+
# @return [::PgSaurus::CreateIndexConcurrently::Migration]
|
137
|
+
def process_postponed_queries
|
138
|
+
Array(@postponed_queries).each do |arguments, block|
|
139
|
+
connection.add_index(*arguments, &block)
|
140
|
+
end
|
141
|
+
|
142
|
+
clear_queue
|
143
|
+
|
144
|
+
self
|
145
|
+
end
|
146
|
+
|
147
|
+
# Clean postponed queries queue.
|
148
|
+
#
|
149
|
+
# @return [::PgSaurus::CreateIndexConcurrently::Migration] migration
|
150
|
+
def clear_queue
|
151
|
+
@postponed_queries = []
|
152
|
+
|
153
|
+
self
|
154
|
+
end
|
155
|
+
private :clear_queue
|
156
|
+
|
157
|
+
# Add to the queue add_index call parameters to be able execute call later.
|
158
|
+
#
|
159
|
+
# @param [Array] arguments
|
160
|
+
# @param [Proc] block
|
161
|
+
#
|
162
|
+
# @return [::PgSaurus::CreateIndexConcurrently::Migration]
|
163
|
+
def enque(*arguments, &block)
|
164
|
+
@postponed_queries ||= []
|
165
|
+
@postponed_queries << [arguments, block]
|
166
|
+
|
167
|
+
self
|
168
|
+
end
|
169
|
+
private :enque
|
170
|
+
end
|
171
|
+
|
172
|
+
# Allows `process_postponed_queries` to be called on MigrationProxy instances.
|
173
|
+
# So, (see ::PgSaurus::CreateIndexConcurrently::Migrator) could run index
|
174
|
+
# creation concurrently.
|
175
|
+
#
|
176
|
+
# Default delegation in (see ActiveRecord::MigrationProxy) allows to call
|
177
|
+
# only several methods.
|
178
|
+
module MigrationProxy
|
179
|
+
# :nodoc:
|
180
|
+
def self.included(klass)
|
181
|
+
klass.delegate :process_postponed_queries, :to => :migration
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Run postponed index creation for each migration.
|
186
|
+
#
|
187
|
+
# This module included into (see ::ActiveRecord::Migrator) class to make possible
|
188
|
+
# to execute queries for postponed index creation after closing migration's
|
189
|
+
# transaction.
|
190
|
+
#
|
191
|
+
# @see ::ActiveRecord::Migrator.migrate
|
192
|
+
# @see ::ActiveRecord::Migrator.ddl_transaction
|
193
|
+
module Migrator
|
194
|
+
extend ActiveSupport::Concern
|
195
|
+
|
196
|
+
# :nodoc:
|
197
|
+
def self.included(klass)
|
198
|
+
klass.alias_method_chain :ddl_transaction, :postponed_queries
|
199
|
+
end
|
200
|
+
|
201
|
+
# Override (see ::ActiveRecord::Migrator.ddl_transaction) to call
|
202
|
+
# (see ::PgSaurus::CreateIndexConcurrently::Migration.process_postponed_queries)
|
203
|
+
# immediately after transaction.
|
204
|
+
#
|
205
|
+
# @see ::ActiveRecord::Migrator.ddl_transaction
|
206
|
+
def ddl_transaction_with_postponed_queries(*args, &block)
|
207
|
+
ddl_transaction_without_postponed_queries(*args, &block)
|
208
|
+
|
209
|
+
# GOTCHA:
|
210
|
+
# This might be a bit tricky, but I've decided that this is the best
|
211
|
+
# way to retrieve migration instance after closing transaction.
|
212
|
+
# The problem that (see ::ActiveRecord::Migrator) doesn't provide any
|
213
|
+
# access to recently launched migration. All logic to iterate through
|
214
|
+
# set of migrations incapsulated in (see ::ActiveRecord::Migrator.migrate)
|
215
|
+
# method.
|
216
|
+
# So, to get access to migration you need to override `migrate` method
|
217
|
+
# and duplicated all logic inside it, plus add call to
|
218
|
+
# `process_postponed_queries`.
|
219
|
+
# I've decided this is less forward compatible then retrieving
|
220
|
+
# value of `migration` variable in context where block
|
221
|
+
# given to `ddl_transaction` method was created.
|
222
|
+
# -- zekefast 2012-09-12
|
223
|
+
migration = block.binding.eval('migration')
|
224
|
+
migration.process_postponed_queries
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module PgSaurus
|
2
|
+
# :nodoc:
|
3
|
+
class Engine < Rails::Engine
|
4
|
+
|
5
|
+
initializer 'pg_saurus' do
|
6
|
+
ActiveSupport.on_load(:active_record) do
|
7
|
+
# load monkey patches
|
8
|
+
['schema_dumper',
|
9
|
+
'errors',
|
10
|
+
'connection_adapters/postgresql_adapter',
|
11
|
+
'connection_adapters/abstract/schema_statements'].each do |path|
|
12
|
+
require ::PgSaurus::Engine.root + 'lib/core_ext/active_record/' + path
|
13
|
+
end
|
14
|
+
|
15
|
+
ActiveRecord::SchemaDumper.class_eval { include ::PgSaurus::SchemaDumper }
|
16
|
+
|
17
|
+
if defined?(ActiveRecord::Migration::CommandRecorder)
|
18
|
+
ActiveRecord::Migration::CommandRecorder.class_eval do
|
19
|
+
include ::PgSaurus::Migration::CommandRecorder
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Follow three include statements add support for concurrently
|
24
|
+
# index creation in migrations.
|
25
|
+
ActiveRecord::Migration.class_eval do
|
26
|
+
include ::PgSaurus::CreateIndexConcurrently::Migration
|
27
|
+
end
|
28
|
+
ActiveRecord::Migrator.class_eval do
|
29
|
+
include ::PgSaurus::CreateIndexConcurrently::Migrator
|
30
|
+
end
|
31
|
+
ActiveRecord::MigrationProxy.class_eval do
|
32
|
+
include ::PgSaurus::CreateIndexConcurrently::MigrationProxy
|
33
|
+
end
|
34
|
+
|
35
|
+
ActiveRecord::ConnectionAdapters::Table.module_eval do
|
36
|
+
include ::PgSaurus::ConnectionAdapters::Table
|
37
|
+
end
|
38
|
+
|
39
|
+
ActiveRecord::ConnectionAdapters::AbstractAdapter.module_eval do
|
40
|
+
include ::PgSaurus::ConnectionAdapters::AbstractAdapter
|
41
|
+
end
|
42
|
+
|
43
|
+
if defined?(ActiveRecord::ConnectionAdapters::JdbcAdapter)
|
44
|
+
sql_adapter_class = ActiveRecord::ConnectionAdapters::JdbcAdapter
|
45
|
+
else
|
46
|
+
sql_adapter_class = ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
|
47
|
+
end
|
48
|
+
|
49
|
+
sql_adapter_class.class_eval do
|
50
|
+
include ::PgSaurus::ConnectionAdapters::PostgreSQLAdapter
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# Provides methods to extend ActiveRecord::Migration::CommandRecorder to
|
2
|
+
# support comments feature.
|
3
|
+
module PgSaurus::Migration::CommandRecorder::CommentMethods
|
4
|
+
# :nodoc:
|
5
|
+
def set_table_comment(*args)
|
6
|
+
record(:set_table_comment, args)
|
7
|
+
end
|
8
|
+
|
9
|
+
# :nodoc:
|
10
|
+
def remove_table_comment(*args)
|
11
|
+
record(:remove_table_comment, args)
|
12
|
+
end
|
13
|
+
|
14
|
+
# :nodoc:
|
15
|
+
def set_column_comment(*args)
|
16
|
+
record(:set_column_comment, args)
|
17
|
+
end
|
18
|
+
|
19
|
+
# :nodoc:
|
20
|
+
def set_column_comments(*args)
|
21
|
+
record(:set_column_comments, args)
|
22
|
+
end
|
23
|
+
|
24
|
+
# :nodoc:
|
25
|
+
def remove_column_comment(*args)
|
26
|
+
record(:remove_column_comment, args)
|
27
|
+
end
|
28
|
+
|
29
|
+
# :nodoc:
|
30
|
+
def remove_column_comments(*args)
|
31
|
+
record(:remove_column_comments, args)
|
32
|
+
end
|
33
|
+
|
34
|
+
# :nodoc:
|
35
|
+
def set_index_comment(*args)
|
36
|
+
record(:set_index_comment, args)
|
37
|
+
end
|
38
|
+
|
39
|
+
# :nodoc:
|
40
|
+
def remove_index_comment(*args)
|
41
|
+
record(:remove_index_comment, args)
|
42
|
+
end
|
43
|
+
|
44
|
+
# :nodoc:
|
45
|
+
def invert_set_table_comment(args)
|
46
|
+
table_name = args.first
|
47
|
+
[:remove_table_comment, [table_name]]
|
48
|
+
end
|
49
|
+
|
50
|
+
# :nodoc:
|
51
|
+
def invert_set_column_comment(args)
|
52
|
+
table_name = args[0]
|
53
|
+
column_name = args[1]
|
54
|
+
[:remove_column_comment, [table_name, column_name]]
|
55
|
+
end
|
56
|
+
|
57
|
+
# :nodoc:
|
58
|
+
def invert_set_column_comments(args)
|
59
|
+
i_args = [args[0]] + args[1].collect{|name, value| name }
|
60
|
+
[:remove_column_comments, i_args]
|
61
|
+
end
|
62
|
+
|
63
|
+
# :nodoc:
|
64
|
+
def invert_set_index_comment(args)
|
65
|
+
index_name = args.first
|
66
|
+
[:remove_index_comment, [index_name]]
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Provides methods to extend ActiveRecord::Migration::CommandRecorder to
|
2
|
+
# support extensions feature.
|
3
|
+
module PgSaurus::Migration::CommandRecorder::ExtensionMethods
|
4
|
+
# :nodoc:
|
5
|
+
def create_extension(*args)
|
6
|
+
record(:create_extension, args)
|
7
|
+
end
|
8
|
+
|
9
|
+
# :nodoc:
|
10
|
+
def drop_extension(*args)
|
11
|
+
record(:drop_extension, args)
|
12
|
+
end
|
13
|
+
|
14
|
+
# :nodoc:
|
15
|
+
def invert_create_extension(args)
|
16
|
+
extension_name = args.first
|
17
|
+
[:drop_extension, [extension_name]]
|
18
|
+
end
|
19
|
+
|
20
|
+
# :nodoc:
|
21
|
+
def invert_drop_extension(args)
|
22
|
+
extension_name = args.first
|
23
|
+
[:create_extension, [extension_name]]
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
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
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# Provides methods to extend ActiveRecord::Migration::CommandRecorder to
|
2
|
+
# support multi schemas feature.
|
3
|
+
module PgSaurus::Migration::CommandRecorder::SchemaMethods
|
4
|
+
# :nodoc:
|
5
|
+
def create_schema(*args)
|
6
|
+
record(:create_schema, args)
|
7
|
+
end
|
8
|
+
|
9
|
+
# :nodoc:
|
10
|
+
def drop_schema(*args)
|
11
|
+
record(:drop_schema, args)
|
12
|
+
end
|
13
|
+
|
14
|
+
# :nodoc:
|
15
|
+
def move_table_to_schema(*args)
|
16
|
+
record(:move_table_to_schema, args)
|
17
|
+
end
|
18
|
+
|
19
|
+
# :nodoc:
|
20
|
+
def create_schema_if_not_exists(*args)
|
21
|
+
record(:create_schema_if_not_exists, args)
|
22
|
+
end
|
23
|
+
|
24
|
+
# :nodoc:
|
25
|
+
def drop_schema_if_exists(*args)
|
26
|
+
record(:drop_schema_if_exists, args)
|
27
|
+
end
|
28
|
+
|
29
|
+
# :nodoc:
|
30
|
+
def invert_create_schema(args)
|
31
|
+
[:drop_schema, [args.first]]
|
32
|
+
end
|
33
|
+
|
34
|
+
# :nodoc:
|
35
|
+
def invert_drop_schema(args)
|
36
|
+
[:create_schema, [args.first]]
|
37
|
+
end
|
38
|
+
|
39
|
+
# :nodoc:
|
40
|
+
def invert_move_table_to_schema(args)
|
41
|
+
table_name = args.first
|
42
|
+
current_schema = args.second
|
43
|
+
|
44
|
+
new_schema, table = ::PgSaurus::Tools.to_schema_and_table(table_name)
|
45
|
+
|
46
|
+
invert_args = ["#{current_schema}.#{table}", new_schema]
|
47
|
+
[:move_table_to_schema, invert_args]
|
48
|
+
end
|
49
|
+
|
50
|
+
# :nodoc:
|
51
|
+
def invert_create_schema_if_not_exists(*args)
|
52
|
+
[:drop_schema_if_exists, args]
|
53
|
+
end
|
54
|
+
|
55
|
+
# :nodoc:
|
56
|
+
def invert_drop_schema_if_exists(*args)
|
57
|
+
[:create_schema_if_not_exists, args]
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Provides methods to extend ActiveRecord::Migration::CommandRecorder to
|
2
|
+
# support view feature.
|
3
|
+
module PgSaurus::Migration::CommandRecorder::ViewMethods
|
4
|
+
# Create a PostgreSQL view.
|
5
|
+
#
|
6
|
+
# @param args [Array] view_name and view_definition
|
7
|
+
#
|
8
|
+
# @return [view]
|
9
|
+
def create_view(*args)
|
10
|
+
record(:create_view, args)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Drop a view in the DB.
|
14
|
+
#
|
15
|
+
# @param args [Array] first argument is view_name
|
16
|
+
#
|
17
|
+
# @return [void]
|
18
|
+
def drop_view(*args)
|
19
|
+
record(:drop_view, args)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Invert the creation of a view in the DB.
|
23
|
+
#
|
24
|
+
# @param args [Array] first argument is supposed to be name of view
|
25
|
+
#
|
26
|
+
# @return [void]
|
27
|
+
def invert_create_view(args)
|
28
|
+
[:drop_view, [args.first]]
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Provides methods to extend ActiveRecord::Migration::CommandRecorder to
|
2
|
+
# support pg_saurus features.
|
3
|
+
module PgSaurus::Migration::CommandRecorder
|
4
|
+
extend ActiveSupport::Autoload
|
5
|
+
|
6
|
+
autoload :ExtensionMethods
|
7
|
+
autoload :SchemaMethods
|
8
|
+
autoload :CommentMethods
|
9
|
+
autoload :ForeignerMethods
|
10
|
+
autoload :ViewMethods
|
11
|
+
|
12
|
+
include ExtensionMethods
|
13
|
+
include SchemaMethods
|
14
|
+
include CommentMethods
|
15
|
+
include ForeignerMethods
|
16
|
+
include ViewMethods
|
17
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# Extends ActiveRecord::SchemaDumper class to dump comments on tables and columns.
|
2
|
+
module PgSaurus::SchemaDumper::CommentMethods
|
3
|
+
# Hook ActiveRecord::SchemaDumper#table method to dump comments on
|
4
|
+
# table and columns.
|
5
|
+
def tables_with_comments(stream)
|
6
|
+
tables_without_comments(stream)
|
7
|
+
|
8
|
+
# Dump table and column comments
|
9
|
+
@connection.tables.sort.each do |table_name|
|
10
|
+
dump_comments(table_name, stream)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Now dump index comments
|
14
|
+
unless (index_comments = @connection.index_comments).empty?
|
15
|
+
index_comments.each do |schema_name, table_name, raw_comment|
|
16
|
+
index_name = schema_name == 'public' ? "'#{table_name}'" : "'#{schema_name}.#{table_name}'"
|
17
|
+
comment = format_comment(raw_comment)
|
18
|
+
stream.puts " set_index_comment #{index_name}, '#{comment}'"
|
19
|
+
end
|
20
|
+
stream.puts
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Find all comments related to passed table and write appropriate
|
25
|
+
# statements to stream.
|
26
|
+
def dump_comments(table_name, stream)
|
27
|
+
unless (comments = @connection.comments(table_name)).empty?
|
28
|
+
comment_statements = comments.map do |row|
|
29
|
+
column_name = row[0]
|
30
|
+
comment = format_comment(row[1])
|
31
|
+
|
32
|
+
if column_name
|
33
|
+
" set_column_comment '#{table_name}', '#{column_name}', '#{comment}'"
|
34
|
+
else
|
35
|
+
" set_table_comment '#{table_name}', '#{comment}'"
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
stream.puts comment_statements.join("\n")
|
41
|
+
stream.puts
|
42
|
+
end
|
43
|
+
end
|
44
|
+
private :dump_comments
|
45
|
+
|
46
|
+
# Escape single quotes from comments.
|
47
|
+
def format_comment(comment)
|
48
|
+
comment.gsub(/'/, "\\\\'")
|
49
|
+
end
|
50
|
+
private :format_comment
|
51
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Extends ActiveRecord::SchemaDumper class to dump comments on tables and columns.
|
2
|
+
module PgSaurus::SchemaDumper::ExtensionMethods
|
3
|
+
# Hook ActiveRecord::SchemaDumper#header method to dump extensions in all
|
4
|
+
# schemas except for pg_catalog.
|
5
|
+
def header_with_extensions(stream)
|
6
|
+
header_without_extensions(stream)
|
7
|
+
dump_extensions(stream)
|
8
|
+
stream
|
9
|
+
end
|
10
|
+
|
11
|
+
# Dump current database extensions recreation commands to the given stream.
|
12
|
+
#
|
13
|
+
# @param [#puts] stream Stream to write to
|
14
|
+
def dump_extensions(stream)
|
15
|
+
extensions = @connection.pg_extensions
|
16
|
+
commands = extensions.map do |extension_name, options|
|
17
|
+
result = [%Q|create_extension "#{extension_name}"|]
|
18
|
+
result << %Q|:schema_name => "#{options[:schema_name]}"| unless options[:schema_name] == 'public'
|
19
|
+
result << %Q|:version => "#{options[:version]}"|
|
20
|
+
result.join(', ')
|
21
|
+
end
|
22
|
+
|
23
|
+
commands.each do |command|
|
24
|
+
stream.puts(" #{command}")
|
25
|
+
end
|
26
|
+
|
27
|
+
stream.puts
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,63 @@
|
|
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
|