pg_power 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +212 -0
- data/lib/core_ext/active_record/connection_adapters/abstract/schema_statements.rb +139 -0
- data/lib/core_ext/active_record/connection_adapters/postgresql_adapter.rb +135 -0
- data/lib/core_ext/active_record/schema_dumper.rb +40 -0
- data/lib/pg_power.rb +16 -0
- data/lib/pg_power/connection_adapters.rb +9 -0
- data/lib/pg_power/connection_adapters/abstract_adapter.rb +20 -0
- data/lib/pg_power/connection_adapters/abstract_adapter/comment_methods.rb +62 -0
- data/lib/pg_power/connection_adapters/abstract_adapter/foreigner_methods.rb +67 -0
- data/lib/pg_power/connection_adapters/abstract_adapter/index_methods.rb +6 -0
- data/lib/pg_power/connection_adapters/abstract_adapter/schema_methods.rb +18 -0
- data/lib/pg_power/connection_adapters/foreign_key_definition.rb +5 -0
- data/lib/pg_power/connection_adapters/index_definition.rb +6 -0
- data/lib/pg_power/connection_adapters/postgresql_adapter.rb +16 -0
- data/lib/pg_power/connection_adapters/postgresql_adapter/comment_methods.rb +79 -0
- data/lib/pg_power/connection_adapters/postgresql_adapter/foreigner_methods.rb +190 -0
- data/lib/pg_power/connection_adapters/postgresql_adapter/index_methods.rb +42 -0
- data/lib/pg_power/connection_adapters/postgresql_adapter/schema_methods.rb +22 -0
- data/lib/pg_power/connection_adapters/table.rb +17 -0
- data/lib/pg_power/connection_adapters/table/comment_methods.rb +58 -0
- data/lib/pg_power/connection_adapters/table/foreigner_methods.rb +51 -0
- data/lib/pg_power/engine.rb +46 -0
- data/lib/pg_power/migration.rb +4 -0
- data/lib/pg_power/migration/command_recorder.rb +13 -0
- data/lib/pg_power/migration/command_recorder/comment_methods.rb +52 -0
- data/lib/pg_power/migration/command_recorder/foreigner_methods.rb +29 -0
- data/lib/pg_power/migration/command_recorder/schema_methods.rb +39 -0
- data/lib/pg_power/schema_dumper.rb +21 -0
- data/lib/pg_power/schema_dumper/comment_methods.rb +36 -0
- data/lib/pg_power/schema_dumper/foreigner_methods.rb +58 -0
- data/lib/pg_power/schema_dumper/schema_methods.rb +51 -0
- data/lib/pg_power/tools.rb +56 -0
- data/lib/pg_power/version.rb +4 -0
- data/lib/tasks/pg_power_tasks.rake +4 -0
- metadata +213 -0
data/README.markdown
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
# PgPower
|
2
|
+
|
3
|
+
ActiveRecord extension to get more from PostgreSQL:
|
4
|
+
|
5
|
+
* Create/drop schemas.
|
6
|
+
* Set/remove comments on columns and tables.
|
7
|
+
* Use foreign keys.
|
8
|
+
* Use partial indexes.
|
9
|
+
|
10
|
+
## Environment notes
|
11
|
+
|
12
|
+
It was tested with Rails 3.1.x and 3.2.x, Ruby 1.8.7 REE and 1.9.3.
|
13
|
+
|
14
|
+
|
15
|
+
## Schemas
|
16
|
+
|
17
|
+
### Create schema
|
18
|
+
|
19
|
+
In migrations you can use `create_schema` and `drop_schema` methods like this:
|
20
|
+
|
21
|
+
class ReplaceDemographySchemaWithPolitics < ActiveRecord::Migration
|
22
|
+
def change
|
23
|
+
drop_schema 'demography'
|
24
|
+
create_schema 'politics'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
### Create table
|
29
|
+
|
30
|
+
Use schema `:schema` option to specify schema name:
|
31
|
+
|
32
|
+
create_table "countries", :schema => "demography" do |t|
|
33
|
+
# columns goes here
|
34
|
+
end
|
35
|
+
|
36
|
+
### Move table to another schema
|
37
|
+
|
38
|
+
Move table `countries` from `demography` schema to `public`:
|
39
|
+
|
40
|
+
move_table_to_schema 'demography.countries', :public
|
41
|
+
|
42
|
+
## Table and column comments
|
43
|
+
|
44
|
+
Provides the following methods to manage comments:
|
45
|
+
|
46
|
+
* set\_table\_comment(table\_name, comment)
|
47
|
+
* remove\_table\_comment(table\_name)
|
48
|
+
* set\_column\_comment(table\_name, column\_name, comment)
|
49
|
+
* remove\_column\_comment(table\_name, column\_name, comment)
|
50
|
+
* set\_column\_comments(table\_name, comments)
|
51
|
+
* remove\_column\_comments(table\_name, *comments)
|
52
|
+
|
53
|
+
|
54
|
+
### Examples
|
55
|
+
|
56
|
+
Set a comment on the given table.
|
57
|
+
|
58
|
+
set_table_comment :phone_numbers, 'This table stores phone numbers that conform to the North American Numbering Plan.'
|
59
|
+
|
60
|
+
Sets a comment on a given column of a given table.
|
61
|
+
|
62
|
+
set_column_comment :phone_numbers, :npa, 'Numbering Plan Area Code - Allowed ranges: [2-9] for first digit, [0-9] for second and third digit.'
|
63
|
+
|
64
|
+
Removes any comment from the given table.
|
65
|
+
|
66
|
+
remove_table_comment :phone_numbers
|
67
|
+
|
68
|
+
Removes any comment from the given column of a given table.
|
69
|
+
|
70
|
+
remove_column_comment :phone_numbers, :npa
|
71
|
+
|
72
|
+
Set comments on multiple columns in the table.
|
73
|
+
|
74
|
+
set_column_comments :phone_numbers, :npa => 'Numbering Plan Area Code - Allowed ranges: [2-9] for first digit, [0-9] for second and third digit.',
|
75
|
+
:nxx => 'Central Office Number'
|
76
|
+
|
77
|
+
Remove comments from multiple columns in the table.
|
78
|
+
|
79
|
+
remove_column_comments :phone_numbers, :npa, :nxx
|
80
|
+
|
81
|
+
PgPower also adds extra methods to change_table.
|
82
|
+
|
83
|
+
Set comments:
|
84
|
+
|
85
|
+
change_table :phone_numbers do |t|
|
86
|
+
t.set_table_comment 'This table stores phone numbers that conform to the North American Numbering Plan.'
|
87
|
+
t.set_column_comment :npa, 'Numbering Plan Area Code - Allowed ranges: [2-9] for first digit, [0-9] for second and third digit.'
|
88
|
+
end
|
89
|
+
|
90
|
+
change_table :phone_numbers do |t|
|
91
|
+
t.set_column_comments :npa => 'Numbering Plan Area Code - Allowed ranges: [2-9] for first digit, [0-9] for second and third digit.',
|
92
|
+
:nxx => 'Central Office Number'
|
93
|
+
end
|
94
|
+
|
95
|
+
Remove comments:
|
96
|
+
|
97
|
+
change_table :phone_numbers do |t|
|
98
|
+
t.remove_table_comment
|
99
|
+
t.remove_column_comment :npa
|
100
|
+
end
|
101
|
+
|
102
|
+
change_table :phone_numbers do |t|
|
103
|
+
t.remove_column_comments :npa, :nxx
|
104
|
+
end
|
105
|
+
|
106
|
+
## Foreign keys
|
107
|
+
|
108
|
+
We imported some code of [foreigner](https://github.com/matthuhiggins/foreigner)
|
109
|
+
gem and patched it to be schema-aware. We also added support for index auto-generation.
|
110
|
+
|
111
|
+
You should disable `foreigner` in your Gemfile if you want to use `pg_power`.
|
112
|
+
|
113
|
+
If you do not want to generate an index, pass the :exclude_index => true option.
|
114
|
+
|
115
|
+
The syntax is compatible with `foreigner`:
|
116
|
+
|
117
|
+
|
118
|
+
Add foreign key from `comments` to `posts` using `post_id` column as key by default:
|
119
|
+
add_foreign_key(:comments, :posts)
|
120
|
+
|
121
|
+
Specify key explicitly:
|
122
|
+
add_foreign_key(:comments, :posts, :column => :blog_post_id)
|
123
|
+
|
124
|
+
Specify name of foreign key constraint:
|
125
|
+
add_foreign_key(:comments, :posts, :name => "comments_posts_fk")
|
126
|
+
|
127
|
+
It works with schemas as expected:
|
128
|
+
add_foreign_key('blog.comments', 'blog.posts')
|
129
|
+
|
130
|
+
Adds the index 'index_comments_on_post_id':
|
131
|
+
add_foreign_key(:comments, :posts)
|
132
|
+
|
133
|
+
Does not add an index:
|
134
|
+
add_foreign_key(:comments, :posts, :exclude_index => true)
|
135
|
+
|
136
|
+
## Partial Indexes
|
137
|
+
|
138
|
+
We used a Rails 4.x [pull request](https://github.com/rails/rails/pull/4956) as a
|
139
|
+
starting point, backported to Rails 3.1.x and patched it to be schema-aware.
|
140
|
+
|
141
|
+
### Examples
|
142
|
+
|
143
|
+
Add a partial index to a table
|
144
|
+
|
145
|
+
add_index(:comments, [:country_id, :user_id], :where => 'active')
|
146
|
+
|
147
|
+
Add a partial index to a schema table
|
148
|
+
|
149
|
+
add_index('blog.comments', :user_id, :where => 'active')
|
150
|
+
|
151
|
+
## Indexes on Expressions
|
152
|
+
|
153
|
+
PostgreSQL supports indexes on expressions. Right now, only basic functional
|
154
|
+
expressions are supported.
|
155
|
+
|
156
|
+
### Examples
|
157
|
+
|
158
|
+
Add an index to a column with a function
|
159
|
+
|
160
|
+
add_index(:comments, "lower(text)")
|
161
|
+
|
162
|
+
## Tools
|
163
|
+
|
164
|
+
PgPower::Tools provides number of useful methods:
|
165
|
+
|
166
|
+
PgPower::Tools.create_schema "services" # => create new PG schema "services"
|
167
|
+
PgPower::Tools.create_schema "nets" # => create new PG schema "nets"
|
168
|
+
PgPower::Tools.drop_schema "services" # => remove the PG schema "services"
|
169
|
+
PgPower::Tools.schemas # => ["public", "information_schema", "nets"]
|
170
|
+
PgPower::Tools.index_exists?(table, columns, options) # => returns true if an index exists for the given params
|
171
|
+
|
172
|
+
## Running tests:
|
173
|
+
|
174
|
+
* Configure `spec/dummy/config/database.yml` for development and test environments.
|
175
|
+
* Run `rake spec`.
|
176
|
+
* Make sure migrations don't raise exceptions and all specs pass.
|
177
|
+
|
178
|
+
## TODO:
|
179
|
+
|
180
|
+
Add next syntax to create table:
|
181
|
+
|
182
|
+
create_table "table_name", :schema => "schema_name" do |t|
|
183
|
+
# columns goes here
|
184
|
+
end
|
185
|
+
|
186
|
+
Support for JRuby:
|
187
|
+
|
188
|
+
* Jdbc driver provides its own `create_schema(schema, user)` method - solve conflicts.
|
189
|
+
|
190
|
+
## Credits
|
191
|
+
|
192
|
+
* [Potapov Sergey](https://github.com/greyblake) - schema support
|
193
|
+
* [Arthur Shagall](https://github.com/albertosaurus) - thanks for [pg_comment](https://github.com/albertosaurus/pg_comment)
|
194
|
+
* [Matthew Higgins](https://github.com/matthuhiggins) - thanks for [foreigner](https://github.com/matthuhiggins/foreigner), which was used as a base for the foreign key support
|
195
|
+
* [Marcelo Silveira](https://github.com/mhfs) - thanks for rails partial index support that was backported into this gem
|
196
|
+
|
197
|
+
## Copyright and License
|
198
|
+
|
199
|
+
Copyright (c) 2012 TMX Credit.
|
200
|
+
Initial foreign key code taken from foreigner, Copyright (c) 2009 Matthew Higgins
|
201
|
+
pg_comment Copyright (c) 2011 Arthur Shagall
|
202
|
+
Partial index Copyright (c) 2012 Marcelo Silveira
|
203
|
+
|
204
|
+
Released under the MIT License. See the MIT-LICENSE file for more details.
|
205
|
+
|
206
|
+
## Contributing
|
207
|
+
|
208
|
+
Contributions are welcome. However, before issuing a pull request, please make sure of the following:
|
209
|
+
|
210
|
+
* All specs are passing (under both ree and 1.9.3)
|
211
|
+
* Any new features have test coverage.
|
212
|
+
* Anything that breaks backward compatibility has a very good reason for doing so.
|
@@ -0,0 +1,139 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters # :nodoc:
|
3
|
+
module SchemaStatements # :nodoc:
|
4
|
+
# Regexp used to find the function name and function argument of a
|
5
|
+
# function call
|
6
|
+
FUNCTIONAL_INDEX_REGEXP = /(\w+)\((\w+)\)/
|
7
|
+
|
8
|
+
# Adds a new index to the table. +column_name+ can be a single Symbol, or
|
9
|
+
# an Array of Symbols.
|
10
|
+
#
|
11
|
+
# ====== Creating a partial index
|
12
|
+
# add_index(:accounts, [:branch_id, :party_id], :unique => true, :where => "active")
|
13
|
+
# generates
|
14
|
+
# CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
|
15
|
+
#
|
16
|
+
def add_index(table_name, column_name, options = {})
|
17
|
+
index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
|
18
|
+
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
|
19
|
+
end
|
20
|
+
|
21
|
+
# Checks to see if an index exists on a table for a given index definition.
|
22
|
+
#
|
23
|
+
# === Examples
|
24
|
+
# # Check that a partial index exists
|
25
|
+
# index_exists?(:suppliers, :company_id, :where => 'active')
|
26
|
+
#
|
27
|
+
# # GIVEN: "index_suppliers_on_company_id" UNIQUE, btree (company_id) WHERE active
|
28
|
+
# index_exists?(:suppliers, :company_id, :unique => true, :where => 'active') => true
|
29
|
+
# index_exists?(:suppliers, :company_id, :unique => true) => false
|
30
|
+
#
|
31
|
+
def index_exists?(table_name, column_name, options = {})
|
32
|
+
column_names = Array.wrap(column_name)
|
33
|
+
index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, :column => column_names)
|
34
|
+
|
35
|
+
# Always compare the index name
|
36
|
+
default_comparator = lambda { |index| index.name == index_name }
|
37
|
+
comparators = [default_comparator]
|
38
|
+
|
39
|
+
# Add a comparator for each index option that is part of the query
|
40
|
+
index_options = [:unique, :where]
|
41
|
+
index_options.each do |index_option|
|
42
|
+
comparators << if options.key?(index_option)
|
43
|
+
lambda do |index|
|
44
|
+
pg_where_clause = index.send(index_option)
|
45
|
+
# pg does nothing to boolean clauses, e.g. 'where active' => 'where active'
|
46
|
+
if pg_where_clause.is_a?(TrueClass) or pg_where_clause.is_a?(FalseClass)
|
47
|
+
pg_where_clause == options[index_option]
|
48
|
+
else
|
49
|
+
# pg adds parentheses around non-boolean clauses, e.g. 'where color IS NULL' => 'where (color is NULL)'
|
50
|
+
pg_where_clause.gsub!(/[()]/,'')
|
51
|
+
# pg casts string comparison ::text. e.g. "where color = 'black'" => "where ((color)::text = 'black'::text)"
|
52
|
+
pg_where_clause.gsub!(/::text/,'')
|
53
|
+
# prevent case from impacting the comparison
|
54
|
+
pg_where_clause.downcase == options[index_option].downcase
|
55
|
+
end
|
56
|
+
end
|
57
|
+
else
|
58
|
+
# If the given index_option is not an argument to the index_exists? query,
|
59
|
+
# select only those pg indexes that do not have the component
|
60
|
+
lambda { |index| index.send(index_option).blank? }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Search all indexes for any that match all comparators
|
65
|
+
indexes(table_name).any? do |index|
|
66
|
+
comparators.inject(true) { |ret, comparator| ret && comparator.call(index) }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Derives the name of the index from the given table name and options hash.
|
71
|
+
def index_name(table_name, options) #:nodoc:
|
72
|
+
if Hash === options # legacy support
|
73
|
+
if options[:column]
|
74
|
+
column_names = Array.wrap(options[:column]).map {|c| expression_index_name(c)}
|
75
|
+
"index_#{table_name}_on_#{column_names * '_and_'}"
|
76
|
+
elsif options[:name]
|
77
|
+
options[:name]
|
78
|
+
else
|
79
|
+
raise ArgumentError, "You must specify the index name"
|
80
|
+
end
|
81
|
+
else
|
82
|
+
index_name(table_name, :column => options)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Returns options used to build out index SQL
|
87
|
+
#
|
88
|
+
# Added support for partial indexes implemented using the :where option
|
89
|
+
#
|
90
|
+
def add_index_options(table_name, column_name, options = {})
|
91
|
+
column_names = Array(column_name)
|
92
|
+
index_name = index_name(table_name, :column => column_names)
|
93
|
+
|
94
|
+
if Hash === options # legacy support, since this param was a string
|
95
|
+
index_type = options[:unique] ? "UNIQUE" : ""
|
96
|
+
index_name = options[:name].to_s if options.key?(:name)
|
97
|
+
if supports_partial_index?
|
98
|
+
index_options = options[:where] ? " WHERE #{options[:where]}" : ""
|
99
|
+
end
|
100
|
+
else
|
101
|
+
index_type = options
|
102
|
+
end
|
103
|
+
|
104
|
+
if index_name.length > index_name_length
|
105
|
+
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters"
|
106
|
+
end
|
107
|
+
if index_name_exists?(table_name, index_name, false)
|
108
|
+
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
|
109
|
+
end
|
110
|
+
index_columns = quoted_columns_for_index(column_names, options).join(", ")
|
111
|
+
|
112
|
+
[index_name, index_type, index_columns, index_options]
|
113
|
+
end
|
114
|
+
protected :add_index_options
|
115
|
+
|
116
|
+
# Override super method to provide support for expression column names
|
117
|
+
def quoted_columns_for_index(column_names, options = {})
|
118
|
+
column_names.map do |name|
|
119
|
+
if name =~ FUNCTIONAL_INDEX_REGEXP
|
120
|
+
"#{$1}(#{quote_column_name($2)})"
|
121
|
+
else
|
122
|
+
quote_column_name(name)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
protected :quoted_columns_for_index
|
127
|
+
|
128
|
+
# Map an expression to a name appropriate for an index
|
129
|
+
def expression_index_name(column_name)
|
130
|
+
if column_name =~ FUNCTIONAL_INDEX_REGEXP
|
131
|
+
"#{$1.downcase}_#{$2}"
|
132
|
+
else
|
133
|
+
column_name
|
134
|
+
end
|
135
|
+
end
|
136
|
+
private :expression_index_name
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module ActiveRecord # :nodoc:
|
2
|
+
module ConnectionAdapters # :nodoc:
|
3
|
+
# Patched version: 3.1.3
|
4
|
+
# Patched methods::
|
5
|
+
# * indexes
|
6
|
+
class PostgreSQLAdapter
|
7
|
+
# In Rails3.2 method #extract_schema_and_table is moved into Utils module.
|
8
|
+
# In Rails3.1 it's implemented right in PostgreSQLAdapter class.
|
9
|
+
# So it's Rails3.2 we include the module into PostgreSQLAdapter in order to make
|
10
|
+
# it compatible to Rails3.1
|
11
|
+
# -- sergey.potapov 2012-06-25
|
12
|
+
if ActiveRecord::VERSION::STRING =~ /^3\.2/
|
13
|
+
include self::Utils
|
14
|
+
end
|
15
|
+
|
16
|
+
# Regex to find columns used in index statements
|
17
|
+
INDEX_COLUMN_EXPRESSION = /ON \w+(?: USING \w+ )?\((.+)\)/
|
18
|
+
# Regex to find where clause in index statements
|
19
|
+
INDEX_WHERE_EXPRESION = /WHERE (.+)$/
|
20
|
+
|
21
|
+
# Returns an array of indexes for the given table.
|
22
|
+
#
|
23
|
+
# == Patch 1 reason:
|
24
|
+
# Since {ActiveRecord::SchemaDumper#tables} is patched to process tables
|
25
|
+
# with a schema prefix, the {#indexes} method receives table_name as
|
26
|
+
# "<schema>.<table>". This patch allows it to handle table names with
|
27
|
+
# a schema prefix.
|
28
|
+
#
|
29
|
+
# == Patch 1:
|
30
|
+
# Search using provided schema if table_name includes schema name.
|
31
|
+
#
|
32
|
+
# == Patch 2 reason:
|
33
|
+
# {ActiveRecord::ConnectionAdapters::PostgreSQLAdapter#indexes} is patched
|
34
|
+
# to support partial indexes using :where clause.
|
35
|
+
#
|
36
|
+
# == Patch 2:
|
37
|
+
# Search the postgres indexdef for the where clause and pass the output to
|
38
|
+
# the custom {PgPower::ConnectionAdapters::IndexDefinition}
|
39
|
+
#
|
40
|
+
def indexes(table_name, name = nil)
|
41
|
+
schema, table = extract_schema_and_table(table_name)
|
42
|
+
schemas = schema ? "ARRAY['#{schema}']" : 'current_schemas(false)'
|
43
|
+
|
44
|
+
result = query(<<-SQL, name)
|
45
|
+
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
|
46
|
+
FROM pg_class t
|
47
|
+
INNER JOIN pg_index d ON t.oid = d.indrelid
|
48
|
+
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
49
|
+
WHERE i.relkind = 'i'
|
50
|
+
AND d.indisprimary = 'f'
|
51
|
+
AND t.relname = '#{table}'
|
52
|
+
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (#{schemas}) )
|
53
|
+
ORDER BY i.relname
|
54
|
+
SQL
|
55
|
+
|
56
|
+
result.map do |row|
|
57
|
+
index = {
|
58
|
+
:name => row[0],
|
59
|
+
:unique => row[1] == 't',
|
60
|
+
:keys => row[2].split(" "),
|
61
|
+
:definition => row[3],
|
62
|
+
:id => row[4]
|
63
|
+
}
|
64
|
+
|
65
|
+
column_names = find_column_names(table_name, index)
|
66
|
+
|
67
|
+
unless column_names.empty?
|
68
|
+
where = find_where_statement(index)
|
69
|
+
lengths = find_lengths(index)
|
70
|
+
|
71
|
+
PgPower::ConnectionAdapters::IndexDefinition.new(table_name, index[:name], index[:unique], column_names, lengths, where)
|
72
|
+
end
|
73
|
+
end.compact
|
74
|
+
end
|
75
|
+
|
76
|
+
# Find column names from index attributes. If the columns are virtual (ie
|
77
|
+
# this is an expression index) then it will try to return the functions
|
78
|
+
# that represent each column
|
79
|
+
#
|
80
|
+
# @param [String] table_name the name of the table
|
81
|
+
# @param [Hash] index index attributes
|
82
|
+
# @return [Array]
|
83
|
+
def find_column_names(table_name, index)
|
84
|
+
columns = Hash[query(<<-SQL, "Columns for index #{index[:name]} on #{table_name}")]
|
85
|
+
SELECT a.attnum, a.attname
|
86
|
+
FROM pg_attribute a
|
87
|
+
WHERE a.attrelid = #{index[:id]}
|
88
|
+
AND a.attnum IN (#{index[:keys].join(",")})
|
89
|
+
SQL
|
90
|
+
|
91
|
+
column_names = columns.values_at(*index[:keys]).compact
|
92
|
+
|
93
|
+
if column_names.empty?
|
94
|
+
definition = index[:definition].sub(INDEX_WHERE_EXPRESION, '')
|
95
|
+
if column_expression = definition.match(INDEX_COLUMN_EXPRESSION)[1]
|
96
|
+
column_names = column_expression.split(',').map do |functional_name|
|
97
|
+
remove_type(functional_name)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
column_names
|
103
|
+
end
|
104
|
+
|
105
|
+
# Find where statement from index definition
|
106
|
+
#
|
107
|
+
# @param [Hash] index index attributes
|
108
|
+
# @return [String] where statement
|
109
|
+
def find_where_statement(index)
|
110
|
+
index[:definition].scan(INDEX_WHERE_EXPRESION).flatten[0]
|
111
|
+
end
|
112
|
+
|
113
|
+
# Find length of index
|
114
|
+
# TODO Update lengths once we merge in ActiveRecord code that supports it. -dresselm 20120305
|
115
|
+
#
|
116
|
+
# @param [Hash] index index attributes
|
117
|
+
# @return [Array]
|
118
|
+
def find_lengths(index)
|
119
|
+
[]
|
120
|
+
end
|
121
|
+
|
122
|
+
# Remove type specification from stored Postgres index definitions
|
123
|
+
#
|
124
|
+
# @param [String] column_with_type the name of the column with type
|
125
|
+
# @return [String]
|
126
|
+
#
|
127
|
+
# @example
|
128
|
+
# remove_type("((col)::text")
|
129
|
+
# => "col"
|
130
|
+
def remove_type(column_with_type)
|
131
|
+
column_with_type.sub(/\((\w+)\)::\w+/, '\1')
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|