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