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,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
|