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,191 @@
|
|
1
|
+
module ActiveRecord # :nodoc:
|
2
|
+
module ConnectionAdapters # :nodoc:
|
3
|
+
# Patched version: 3.1.3
|
4
|
+
# Patched methods::
|
5
|
+
# * indexes
|
6
|
+
class PostgreSQLAdapter
|
7
|
+
# Regex to find columns used in index statements
|
8
|
+
INDEX_COLUMN_EXPRESSION = /ON \w+(?: USING \w+ )?\((.+)\)/
|
9
|
+
# Regex to find where clause in index statements
|
10
|
+
INDEX_WHERE_EXPRESION = /WHERE (.+)$/
|
11
|
+
|
12
|
+
# Returns the list of all tables in the schema search path or a specified schema.
|
13
|
+
#
|
14
|
+
# == Patch:
|
15
|
+
# If current user is not `postgres` original method return all tables from all schemas
|
16
|
+
# without schema prefix. This disables such behavior by querying only default schema.
|
17
|
+
# Tables with schemas will be queried later.
|
18
|
+
#
|
19
|
+
def tables(name = nil)
|
20
|
+
query(<<-SQL, 'SCHEMA').map { |row| row[0] }
|
21
|
+
SELECT tablename
|
22
|
+
FROM pg_tables
|
23
|
+
WHERE schemaname = ANY (ARRAY['public'])
|
24
|
+
SQL
|
25
|
+
end
|
26
|
+
|
27
|
+
# Checks if index exists for given table.
|
28
|
+
#
|
29
|
+
# == Patch:
|
30
|
+
# Search using provided schema if table_name includes schema name.
|
31
|
+
#
|
32
|
+
def index_name_exists?(table_name, index_name, default)
|
33
|
+
schema, table = Utils.extract_schema_and_table(table_name)
|
34
|
+
schemas = schema ? "ARRAY['#{schema}']" : 'current_schemas(false)'
|
35
|
+
|
36
|
+
exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
|
37
|
+
SELECT COUNT(*)
|
38
|
+
FROM pg_class t
|
39
|
+
INNER JOIN pg_index d ON t.oid = d.indrelid
|
40
|
+
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
41
|
+
WHERE i.relkind = 'i'
|
42
|
+
AND i.relname = '#{index_name}'
|
43
|
+
AND t.relname = '#{table}'
|
44
|
+
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (#{schemas}) )
|
45
|
+
SQL
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns an array of indexes for the given table.
|
49
|
+
#
|
50
|
+
# == Patch 1 reason:
|
51
|
+
# Since {ActiveRecord::SchemaDumper#tables} is patched to process tables
|
52
|
+
# with a schema prefix, the {#indexes} method receives table_name as
|
53
|
+
# "<schema>.<table>". This patch allows it to handle table names with
|
54
|
+
# a schema prefix.
|
55
|
+
#
|
56
|
+
# == Patch 1:
|
57
|
+
# Search using provided schema if table_name includes schema name.
|
58
|
+
#
|
59
|
+
# == Patch 2 reason:
|
60
|
+
# {ActiveRecord::ConnectionAdapters::PostgreSQLAdapter#indexes} is patched
|
61
|
+
# to support partial indexes using :where clause.
|
62
|
+
#
|
63
|
+
# == Patch 2:
|
64
|
+
# Search the postgres indexdef for the where clause and pass the output to
|
65
|
+
# the custom {PgSaurus::ConnectionAdapters::IndexDefinition}
|
66
|
+
#
|
67
|
+
def indexes(table_name, name = nil)
|
68
|
+
schema, table = Utils.extract_schema_and_table(table_name)
|
69
|
+
schemas = schema ? "ARRAY['#{schema}']" : 'current_schemas(false)'
|
70
|
+
|
71
|
+
result = query(<<-SQL, name)
|
72
|
+
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid, am.amname
|
73
|
+
FROM pg_class t
|
74
|
+
INNER JOIN pg_index d ON t.oid = d.indrelid
|
75
|
+
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
76
|
+
INNER JOIN pg_am am ON i.relam = am.oid
|
77
|
+
WHERE i.relkind = 'i'
|
78
|
+
AND d.indisprimary = 'f'
|
79
|
+
AND t.relname = '#{table}'
|
80
|
+
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (#{schemas}) )
|
81
|
+
ORDER BY i.relname
|
82
|
+
SQL
|
83
|
+
|
84
|
+
result.map do |row|
|
85
|
+
index = {
|
86
|
+
:name => row[0],
|
87
|
+
:unique => row[1] == 't',
|
88
|
+
:keys => row[2].split(" "),
|
89
|
+
:definition => row[3],
|
90
|
+
:id => row[4],
|
91
|
+
:access_method => row[5]
|
92
|
+
}
|
93
|
+
|
94
|
+
column_names = find_column_names(table_name, index)
|
95
|
+
|
96
|
+
unless column_names.empty?
|
97
|
+
where = find_where_statement(index)
|
98
|
+
lengths = find_lengths(index)
|
99
|
+
|
100
|
+
PgSaurus::ConnectionAdapters::IndexDefinition.new(table_name, index[:name], index[:unique], column_names, lengths, where, index[:access_method])
|
101
|
+
end
|
102
|
+
end.compact
|
103
|
+
end
|
104
|
+
|
105
|
+
# Find column names from index attributes. If the columns are virtual (ie
|
106
|
+
# this is an expression index) then it will try to return the functions
|
107
|
+
# that represent each column
|
108
|
+
#
|
109
|
+
# @param [String] table_name the name of the table
|
110
|
+
# @param [Hash] index index attributes
|
111
|
+
# @return [Array]
|
112
|
+
def find_column_names(table_name, index)
|
113
|
+
columns = Hash[query(<<-SQL, "Columns for index #{index[:name]} on #{table_name}")]
|
114
|
+
SELECT a.attnum, a.attname
|
115
|
+
FROM pg_attribute a
|
116
|
+
WHERE a.attrelid = #{index[:id]}
|
117
|
+
AND a.attnum IN (#{index[:keys].join(",")})
|
118
|
+
SQL
|
119
|
+
|
120
|
+
column_names = columns.values_at(*index[:keys]).compact
|
121
|
+
|
122
|
+
if column_names.empty?
|
123
|
+
definition = index[:definition].sub(INDEX_WHERE_EXPRESION, '')
|
124
|
+
if column_expression = definition.match(INDEX_COLUMN_EXPRESSION)[1]
|
125
|
+
column_names = split_expression(column_expression).map do |functional_name|
|
126
|
+
remove_type(functional_name)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
column_names
|
132
|
+
end
|
133
|
+
|
134
|
+
# Splits only on commas outside of parens
|
135
|
+
def split_expression(expression)
|
136
|
+
result = []
|
137
|
+
parens = 0
|
138
|
+
buffer = ""
|
139
|
+
|
140
|
+
expression.chars do |char|
|
141
|
+
case char
|
142
|
+
when ','
|
143
|
+
if parens == 0
|
144
|
+
result.push(buffer)
|
145
|
+
buffer = ""
|
146
|
+
next
|
147
|
+
end
|
148
|
+
when '('
|
149
|
+
parens += 1
|
150
|
+
when ')'
|
151
|
+
parens -= 1
|
152
|
+
end
|
153
|
+
|
154
|
+
buffer << char
|
155
|
+
end
|
156
|
+
|
157
|
+
result << buffer unless buffer.empty?
|
158
|
+
result
|
159
|
+
end
|
160
|
+
|
161
|
+
# Find where statement from index definition
|
162
|
+
#
|
163
|
+
# @param [Hash] index index attributes
|
164
|
+
# @return [String] where statement
|
165
|
+
def find_where_statement(index)
|
166
|
+
index[:definition].scan(INDEX_WHERE_EXPRESION).flatten[0]
|
167
|
+
end
|
168
|
+
|
169
|
+
# Find length of index
|
170
|
+
# TODO Update lengths once we merge in ActiveRecord code that supports it. -dresselm 20120305
|
171
|
+
#
|
172
|
+
# @param [Hash] index index attributes
|
173
|
+
# @return [Array]
|
174
|
+
def find_lengths(index)
|
175
|
+
[]
|
176
|
+
end
|
177
|
+
|
178
|
+
# Remove type specification from stored Postgres index definitions
|
179
|
+
#
|
180
|
+
# @param [String] column_with_type the name of the column with type
|
181
|
+
# @return [String]
|
182
|
+
#
|
183
|
+
# @example
|
184
|
+
# remove_type("((col)::text")
|
185
|
+
# => "col"
|
186
|
+
def remove_type(column_with_type)
|
187
|
+
column_with_type.sub(/\((\w+)\)::\w+/, '\1')
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module ActiveRecord #:nodoc:
|
2
|
+
# Patched version: 3.1.3
|
3
|
+
# Patched methods::
|
4
|
+
# * indexes
|
5
|
+
class SchemaDumper #:nodoc:
|
6
|
+
# Writes out index-related details to the schema stream
|
7
|
+
#
|
8
|
+
# == Patch reason:
|
9
|
+
# {ActiveRecord::SchemaDumper#indexes} does not support writing out
|
10
|
+
# details related to partial indexes.
|
11
|
+
#
|
12
|
+
# == Patch:
|
13
|
+
# Append :where clause if there's a partial index
|
14
|
+
#
|
15
|
+
def indexes(table, stream)
|
16
|
+
if (indexes = @connection.indexes(table)).any?
|
17
|
+
add_index_statements = indexes.map do |index|
|
18
|
+
statement_parts = [
|
19
|
+
('add_index ' + index.table.inspect),
|
20
|
+
index.columns.inspect,
|
21
|
+
(':name => ' + index.name.inspect),
|
22
|
+
]
|
23
|
+
statement_parts << ':unique => true' if index.unique
|
24
|
+
|
25
|
+
index_lengths = (index.lengths || []).compact
|
26
|
+
statement_parts << (':length => ' + Hash[index.columns.zip(index.lengths)].inspect) unless index_lengths.empty?
|
27
|
+
|
28
|
+
# Patch
|
29
|
+
# Append :where clause if a partial index
|
30
|
+
statement_parts << (':where => ' + index.where.inspect) if index.where
|
31
|
+
|
32
|
+
statement_parts << (':using => ' + index.access_method.inspect) unless index.access_method.downcase == 'btree'
|
33
|
+
|
34
|
+
' ' + statement_parts.join(', ')
|
35
|
+
end
|
36
|
+
|
37
|
+
stream.puts add_index_statements.sort.join("\n")
|
38
|
+
stream.puts
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# Extends ActiveRecord::ConnectionAdapters::AbstractAdapter with
|
2
|
+
# empty methods for comments feature.
|
3
|
+
module PgSaurus::ConnectionAdapters::AbstractAdapter::CommentMethods
|
4
|
+
def supports_comments?
|
5
|
+
false
|
6
|
+
end
|
7
|
+
|
8
|
+
# Sets a comment on the given table.
|
9
|
+
#
|
10
|
+
# ===== Example
|
11
|
+
# ====== Creating a comment on phone_numbers table
|
12
|
+
# set_table_comment :phone_numbers, 'This table stores phone numbers that conform to the North American Numbering Plan.'
|
13
|
+
def set_table_comment(table_name, comment)
|
14
|
+
# Does nothing
|
15
|
+
end
|
16
|
+
|
17
|
+
# Sets a comment on a given column of a given table.
|
18
|
+
#
|
19
|
+
# ===== Example
|
20
|
+
# ====== Creating a comment on npa column of table phone_numbers
|
21
|
+
# set_column_comment :phone_numbers, :npa, 'Numbering Plan Area Code - Allowed ranges: [2-9] for first digit, [0-9] for second and third digit.'
|
22
|
+
def set_column_comment(table_name, column_name, comment)
|
23
|
+
# Does nothing
|
24
|
+
end
|
25
|
+
|
26
|
+
# Sets comments on multiple columns. 'comments' is a hash of column_name => comment pairs.
|
27
|
+
#
|
28
|
+
# ===== Example
|
29
|
+
# ====== Setting comments on the columns of the phone_numbers table
|
30
|
+
# set_column_comments :phone_numbers, :npa => 'Numbering Plan Area Code - Allowed ranges: [2-9] for first digit, [0-9] for second and third digit.',
|
31
|
+
# :nxx => 'Central Office Number'
|
32
|
+
def set_column_comments(table_name, comments)
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
# Sets the comment on the given index
|
37
|
+
#
|
38
|
+
# ===== Example
|
39
|
+
# ====== Setting comment on the index_pets_on_breed_id index
|
40
|
+
# set_index_comment 'index_pets_on_breed_id', 'Index on breed_id'
|
41
|
+
def set_index_comment(index_name, comment)
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
# Removes any comment from the given table.
|
46
|
+
#
|
47
|
+
# ===== Example
|
48
|
+
# ====== Removing comment from phone numbers table
|
49
|
+
# remove_table_comment :phone_numbers
|
50
|
+
def remove_table_comment(table_name)
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
# Removes any comment from the given column of a given table.
|
55
|
+
#
|
56
|
+
# ===== Example
|
57
|
+
# ====== Removing comment from the npa column of table phone_numbers
|
58
|
+
# remove_column_comment :phone_numbers, :npa
|
59
|
+
def remove_column_comment(table_name, column_name)
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
# Removes any comment from the given columns of a given table.
|
64
|
+
#
|
65
|
+
# ===== Example
|
66
|
+
# ====== Removing comment from the npa and nxx columns of table phone_numbers
|
67
|
+
# remove_column_comments :phone_numbers, :npa, :nxx
|
68
|
+
def remove_column_comments(table_name, *column_names)
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
# Removes the comment from the given index
|
73
|
+
#
|
74
|
+
# ===== Example
|
75
|
+
# ====== Removing comment from the index_pets_on_breed_id index
|
76
|
+
# remove_index_comment :index_pets_on_breed_id
|
77
|
+
def remove_index_comment(index_name)
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# Extends ActiveRecord::ConnectionAdapters::AbstractAdapter with
|
2
|
+
# empty methods for foreign keys feature.
|
3
|
+
module PgSaurus::ConnectionAdapters::AbstractAdapter::ForeignerMethods
|
4
|
+
def supports_foreign_keys?
|
5
|
+
false
|
6
|
+
end
|
7
|
+
|
8
|
+
# Adds a new foreign key to the +from_table+, referencing the primary key of +to_table+
|
9
|
+
#
|
10
|
+
# The foreign key will be named after the from and to tables unless you pass
|
11
|
+
# <tt>:name</tt> as an option.
|
12
|
+
#
|
13
|
+
# ===== Examples
|
14
|
+
# ====== Creating a foreign key
|
15
|
+
# add_foreign_key(:comments, :posts)
|
16
|
+
# generates
|
17
|
+
# ALTER TABLE `comments` ADD CONSTRAINT
|
18
|
+
# `comments_post_id_fk` FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`)
|
19
|
+
#
|
20
|
+
# ====== Creating a named foreign key
|
21
|
+
# add_foreign_key(:comments, :posts, :name => 'comments_belongs_to_posts')
|
22
|
+
# generates
|
23
|
+
# ALTER TABLE `comments` ADD CONSTRAINT
|
24
|
+
# `comments_belongs_to_posts` FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`)
|
25
|
+
#
|
26
|
+
# ====== Creating a cascading foreign_key on a custom column
|
27
|
+
# add_foreign_key(:people, :people, :column => 'best_friend_id', :dependent => :nullify)
|
28
|
+
# generates
|
29
|
+
# ALTER TABLE `people` ADD CONSTRAINT
|
30
|
+
# `people_best_friend_id_fk` FOREIGN KEY (`best_friend_id`) REFERENCES `people` (`id`)
|
31
|
+
# ON DELETE SET NULL
|
32
|
+
#
|
33
|
+
# === Supported options
|
34
|
+
# [:column]
|
35
|
+
# Specify the column name on the from_table that references the to_table. By default this is guessed
|
36
|
+
# to be the singular name of the to_table with "_id" suffixed. So a to_table of :posts will use "post_id"
|
37
|
+
# as the default <tt>:column</tt>.
|
38
|
+
# [:primary_key]
|
39
|
+
# Specify the column name on the to_table that is referenced by this foreign key. By default this is
|
40
|
+
# assumed to be "id".
|
41
|
+
# [:name]
|
42
|
+
# Specify the name of the foreign key constraint. This defaults to use from_table and foreign key column.
|
43
|
+
# [:dependent]
|
44
|
+
# If set to <tt>:delete</tt>, the associated records in from_table are deleted when records in to_table table are deleted.
|
45
|
+
# If set to <tt>:nullify</tt>, the foreign key column is set to +NULL+.
|
46
|
+
# [:options]
|
47
|
+
# Any extra options you want appended to the foreign key definition.
|
48
|
+
def add_foreign_key(from_table, to_table, options = {})
|
49
|
+
end
|
50
|
+
|
51
|
+
# Remove the given foreign key from the table.
|
52
|
+
#
|
53
|
+
# ===== Examples
|
54
|
+
# ====== Remove the suppliers_company_id_fk in the suppliers table.
|
55
|
+
# remove_foreign_key :suppliers, :companies
|
56
|
+
# ====== Remove the foreign key named accounts_branch_id_fk in the accounts table.
|
57
|
+
# remove_foreign_key :accounts, :column => :branch_id
|
58
|
+
# ====== Remove the foreign key named party_foreign_key in the accounts table.
|
59
|
+
# remove_foreign_key :accounts, :name => :party_foreign_key
|
60
|
+
def remove_foreign_key(from_table, options)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Return the foreign keys for the schema_dumper
|
64
|
+
def foreign_keys(table_name)
|
65
|
+
[]
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Extends ActiveRecord::ConnectionAdapters::AbstractAdapter
|
2
|
+
# with methods for multi-schema support.
|
3
|
+
module PgSaurus::ConnectionAdapters::AbstractAdapter::SchemaMethods
|
4
|
+
|
5
|
+
# Provide :schema option to +create_table+ method.
|
6
|
+
def create_table_with_schema_option(table_name, options = {}, &block)
|
7
|
+
options = options.dup
|
8
|
+
schema_name = options.delete(:schema)
|
9
|
+
table_name = "#{schema_name}.#{table_name}" if schema_name
|
10
|
+
create_table_without_schema_option(table_name, options, &block)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Provide :schema option to +drop_table+ method.
|
14
|
+
def drop_table_with_schema_option(table_name, options = {})
|
15
|
+
options = options.dup
|
16
|
+
schema_name = options.delete(:schema)
|
17
|
+
table_name = "#{schema_name}.#{table_name}" if schema_name
|
18
|
+
drop_table_without_schema_option(table_name, options)
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Extends ActiveRecord::ConnectionAdapters::AbstractAdapter class.
|
2
|
+
module PgSaurus::ConnectionAdapters::AbstractAdapter
|
3
|
+
extend ActiveSupport::Autoload
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
autoload :CommentMethods
|
7
|
+
autoload :ForeignerMethods
|
8
|
+
autoload :SchemaMethods
|
9
|
+
autoload :IndexMethods
|
10
|
+
|
11
|
+
include CommentMethods
|
12
|
+
include ForeignerMethods
|
13
|
+
include SchemaMethods
|
14
|
+
include IndexMethods
|
15
|
+
|
16
|
+
included do
|
17
|
+
alias_method_chain :create_table, :schema_option
|
18
|
+
alias_method_chain :drop_table , :schema_option
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
module PgSaurus::ConnectionAdapters
|
2
|
+
# Structure to store index parameters
|
3
|
+
# Overrides ActiveRecord::ConnectionAdapters::IndexDefinition
|
4
|
+
# with the additional :where parameter.
|
5
|
+
class IndexDefinition < Struct.new( :table, :name, :unique, :columns,
|
6
|
+
:lengths, :where, :access_method )
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# Provides methods to extend {ActiveRecord::ConnectionAdapters::PostgreSQLAdapter}
|
2
|
+
# to support comments feature.
|
3
|
+
module PgSaurus::ConnectionAdapters::PostgreSQLAdapter::CommentMethods
|
4
|
+
def supports_comments?
|
5
|
+
true
|
6
|
+
end
|
7
|
+
|
8
|
+
# Executes SQL to set comment on table
|
9
|
+
# @param [String, Symbol] table_name name of table to set a comment on
|
10
|
+
# @param [String] comment
|
11
|
+
def set_table_comment(table_name, comment)
|
12
|
+
sql = "COMMENT ON TABLE #{quote_table_name(table_name)} IS $$#{comment}$$;"
|
13
|
+
execute sql
|
14
|
+
end
|
15
|
+
|
16
|
+
# Executes SQL to set comment on column.
|
17
|
+
# @param [String, Symbol] table_name
|
18
|
+
# @param [String, Symbol] column_name
|
19
|
+
# @param [String] comment
|
20
|
+
def set_column_comment(table_name, column_name, comment)
|
21
|
+
sql = "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS $$#{comment}$$;"
|
22
|
+
execute sql
|
23
|
+
end
|
24
|
+
|
25
|
+
# Sets comments on columns of passed table.
|
26
|
+
# @param [String, Symbol] table_name
|
27
|
+
# @param [Hash] comments every key is a column name and value is a comment.
|
28
|
+
def set_column_comments(table_name, comments)
|
29
|
+
comments.each_pair do |column_name, comment|
|
30
|
+
set_column_comment table_name, column_name, comment
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Sets the given comment on the given index
|
35
|
+
# @param [String, Symbol] index_name The name of the index
|
36
|
+
# @param [String, Symbol] comment The comment to set on the index
|
37
|
+
def set_index_comment(index_name, comment)
|
38
|
+
sql = "COMMENT ON INDEX #{quote_string(index_name)} IS $$#{comment}$$;"
|
39
|
+
execute sql
|
40
|
+
end
|
41
|
+
|
42
|
+
# Executes SQL to remove comment on passed table.
|
43
|
+
# @param [String, Symbol] table_name
|
44
|
+
def remove_table_comment(table_name)
|
45
|
+
sql = "COMMENT ON TABLE #{quote_table_name(table_name)} IS NULL;"
|
46
|
+
execute sql
|
47
|
+
end
|
48
|
+
|
49
|
+
# Executes SQL to remove comment on column.
|
50
|
+
# @param [String, Symbol] table_name
|
51
|
+
# @param [String, Symbol] column_name
|
52
|
+
def remove_column_comment(table_name, column_name)
|
53
|
+
sql = "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS NULL;"
|
54
|
+
execute sql
|
55
|
+
end
|
56
|
+
|
57
|
+
# Remove comments on passed table columns.
|
58
|
+
def remove_column_comments(table_name, *column_names)
|
59
|
+
column_names.each do |column_name|
|
60
|
+
remove_column_comment table_name, column_name
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Removes any comment from the given index
|
65
|
+
# @param [String, Symbol] index_name The name of the index
|
66
|
+
def remove_index_comment(index_name)
|
67
|
+
sql = "COMMENT ON INDEX #{quote_string(index_name)} IS NULL;"
|
68
|
+
execute sql
|
69
|
+
end
|
70
|
+
|
71
|
+
# Fetches all comments related to passed table.
|
72
|
+
# I returns table comment and column comments as well.
|
73
|
+
# ===Example
|
74
|
+
# comments("users") # => [[ "" , "Comment on table" ],
|
75
|
+
# ["id" , "Comment on id column" ],
|
76
|
+
# ["email", "Comment on email column"]]
|
77
|
+
def comments(table_name)
|
78
|
+
relation_name, schema_name = table_name.split(".", 2).reverse
|
79
|
+
schema_name ||= :public
|
80
|
+
|
81
|
+
com = select_all <<-SQL
|
82
|
+
SELECT a.attname AS column_name, d.description AS comment
|
83
|
+
FROM pg_description d
|
84
|
+
JOIN pg_class c on c.oid = d.objoid
|
85
|
+
LEFT OUTER JOIN pg_attribute a ON c.oid = a.attrelid AND a.attnum = d.objsubid
|
86
|
+
JOIN pg_namespace ON c.relnamespace = pg_namespace.oid
|
87
|
+
WHERE c.relkind = 'r' AND c.relname = '#{relation_name}' AND
|
88
|
+
pg_namespace.nspname = '#{schema_name}'
|
89
|
+
SQL
|
90
|
+
com.map do |row|
|
91
|
+
[ row['column_name'], row['comment'] ]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Fetches index comments
|
96
|
+
# returns an Array of Arrays, each element representing a single index with comment as
|
97
|
+
# [ 'schema_name', 'index_name', 'comment' ]
|
98
|
+
def index_comments
|
99
|
+
query = <<-SQL
|
100
|
+
SELECT c.relname AS index_name, d.description AS comment, pg_namespace.nspname AS schema_name
|
101
|
+
FROM pg_description d
|
102
|
+
JOIN pg_class c ON c.oid = d.objoid
|
103
|
+
JOIN pg_namespace ON c.relnamespace = pg_namespace.oid
|
104
|
+
WHERE c.relkind = 'i'
|
105
|
+
ORDER BY schema_name, index_name
|
106
|
+
SQL
|
107
|
+
|
108
|
+
com = select_all(query)
|
109
|
+
|
110
|
+
com.map do |row|
|
111
|
+
[ row['schema_name'], row['index_name'], row['comment'] ]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|