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.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/README.markdown +341 -0
  3. data/lib/colorized_text.rb +33 -0
  4. data/lib/core_ext/active_record/connection_adapters/abstract/schema_statements.rb +155 -0
  5. data/lib/core_ext/active_record/connection_adapters/postgresql_adapter.rb +191 -0
  6. data/lib/core_ext/active_record/errors.rb +6 -0
  7. data/lib/core_ext/active_record/schema_dumper.rb +42 -0
  8. data/lib/pg_saurus/connection_adapters/abstract_adapter/comment_methods.rb +80 -0
  9. data/lib/pg_saurus/connection_adapters/abstract_adapter/foreigner_methods.rb +67 -0
  10. data/lib/pg_saurus/connection_adapters/abstract_adapter/index_methods.rb +6 -0
  11. data/lib/pg_saurus/connection_adapters/abstract_adapter/schema_methods.rb +20 -0
  12. data/lib/pg_saurus/connection_adapters/abstract_adapter.rb +20 -0
  13. data/lib/pg_saurus/connection_adapters/foreign_key_definition.rb +5 -0
  14. data/lib/pg_saurus/connection_adapters/index_definition.rb +8 -0
  15. data/lib/pg_saurus/connection_adapters/postgresql_adapter/comment_methods.rb +114 -0
  16. data/lib/pg_saurus/connection_adapters/postgresql_adapter/extension_methods.rb +124 -0
  17. data/lib/pg_saurus/connection_adapters/postgresql_adapter/foreigner_methods.rb +221 -0
  18. data/lib/pg_saurus/connection_adapters/postgresql_adapter/index_methods.rb +42 -0
  19. data/lib/pg_saurus/connection_adapters/postgresql_adapter/schema_methods.rb +58 -0
  20. data/lib/pg_saurus/connection_adapters/postgresql_adapter/translate_exception.rb +20 -0
  21. data/lib/pg_saurus/connection_adapters/postgresql_adapter/view_methods.rb +17 -0
  22. data/lib/pg_saurus/connection_adapters/postgresql_adapter.rb +28 -0
  23. data/lib/pg_saurus/connection_adapters/table/comment_methods.rb +58 -0
  24. data/lib/pg_saurus/connection_adapters/table/foreigner_methods.rb +51 -0
  25. data/lib/pg_saurus/connection_adapters/table.rb +17 -0
  26. data/lib/pg_saurus/connection_adapters.rb +9 -0
  27. data/lib/pg_saurus/create_index_concurrently.rb +227 -0
  28. data/lib/pg_saurus/engine.rb +57 -0
  29. data/lib/pg_saurus/errors.rb +6 -0
  30. data/lib/pg_saurus/migration/command_recorder/comment_methods.rb +68 -0
  31. data/lib/pg_saurus/migration/command_recorder/extension_methods.rb +25 -0
  32. data/lib/pg_saurus/migration/command_recorder/foreigner_methods.rb +31 -0
  33. data/lib/pg_saurus/migration/command_recorder/schema_methods.rb +59 -0
  34. data/lib/pg_saurus/migration/command_recorder/view_methods.rb +31 -0
  35. data/lib/pg_saurus/migration/command_recorder.rb +17 -0
  36. data/lib/pg_saurus/migration.rb +4 -0
  37. data/lib/pg_saurus/schema_dumper/comment_methods.rb +51 -0
  38. data/lib/pg_saurus/schema_dumper/extension_methods.rb +29 -0
  39. data/lib/pg_saurus/schema_dumper/foreigner_methods.rb +63 -0
  40. data/lib/pg_saurus/schema_dumper/schema_methods.rb +27 -0
  41. data/lib/pg_saurus/schema_dumper/view_methods.rb +32 -0
  42. data/lib/pg_saurus/schema_dumper.rb +28 -0
  43. data/lib/pg_saurus/tools.rb +104 -0
  44. data/lib/pg_saurus/version.rb +4 -0
  45. data/lib/pg_saurus.rb +18 -0
  46. data/lib/tasks/pg_saurus_tasks.rake +4 -0
  47. 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,6 @@
1
+ module ActiveRecord
2
+ # Raised when an DB operation cannot be carried out because the current
3
+ # database user lacks the required privileges.
4
+ class InsufficientPrivilege < WrappedDatabaseException
5
+ end
6
+ 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,6 @@
1
+ # Extends ActiveRecord::ConnectionAdapters::AbstractAdapter.
2
+ module PgSaurus::ConnectionAdapters::AbstractAdapter::IndexMethods
3
+ def supports_partial_index?
4
+ false
5
+ end
6
+ 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,5 @@
1
+ module PgSaurus::ConnectionAdapters
2
+ # Structure to store information about foreign keys related to from_table.
3
+ class ForeignKeyDefinition < Struct.new(:from_table, :to_table, :options)
4
+ end
5
+ 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