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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a10802e45cc1cb9e289081e96b420ddbcf4459c1
|
4
|
+
data.tar.gz: 5f570fb003f9551eb793213c99586a5b6cf8597d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 22b9bcda65cdbec23a53f07f41fd6a37b808f605e3ba65a972f6ba0c19b17f966e9092107b807208ece28ff318fbe3082c6a5cc78ba69ac9e69fad80c4eb5e28
|
7
|
+
data.tar.gz: 8c57bb5f82920f46ee793236cf031dfa1b2e758a1bc97f24c683ec42c483d661e3bab117bb888c210619a8036e31741751447419e35109cc093f02c2d3e0bd55
|
data/README.markdown
ADDED
@@ -0,0 +1,341 @@
|
|
1
|
+
# PgSaurus
|
2
|
+
|
3
|
+
[![Build Status](https://secure.travis-ci.org/HornsAndHooves/pg_saurus.png)](http://travis-ci.org/HornsAndHooves/pg_saurus)
|
4
|
+
[![Dependency Status](https://gemnasium.com/HornsAndHooves/pg_saurus.png)](https://gemnasium.com/HornsAndHooves/pg_saurus)
|
5
|
+
[![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/HornsAndHooves/pg_saurus)
|
6
|
+
|
7
|
+
ActiveRecord extension to get more from PostgreSQL:
|
8
|
+
|
9
|
+
* Create/drop schemas.
|
10
|
+
* Set/remove comments on columns and tables.
|
11
|
+
* Use foreign keys.
|
12
|
+
* Use partial indexes.
|
13
|
+
* Run index creation concurrently.
|
14
|
+
|
15
|
+
PgSaurus is a fork of PgPower.
|
16
|
+
|
17
|
+
## Environment notes
|
18
|
+
|
19
|
+
PgSaurus was tested with Rails 4.0.x, Ruby 2.1.2.
|
20
|
+
|
21
|
+
NOTE: JRuby is not yet supported. The current ActiveRecord JDBC
|
22
|
+
adapter has its own Rails4-compatible method named "create_schema" which
|
23
|
+
conflicts with this gem.
|
24
|
+
|
25
|
+
## Schemas
|
26
|
+
|
27
|
+
### Create schema
|
28
|
+
|
29
|
+
In migrations you can use `create_schema` and `drop_schema` methods like this:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
class ReplaceDemographySchemaWithPolitics < ActiveRecord::Migration
|
33
|
+
def change
|
34
|
+
drop_schema 'demography'
|
35
|
+
create_schema 'politics'
|
36
|
+
|
37
|
+
drop_schema_if_exists('demography')
|
38
|
+
create_schema_if_not_exists('politics')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
```
|
42
|
+
### Create table
|
43
|
+
|
44
|
+
Use schema `:schema` option to specify schema name:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
create_table "countries", :schema => "demography" do |t|
|
48
|
+
# columns goes here
|
49
|
+
end
|
50
|
+
```
|
51
|
+
### Move table to another schema
|
52
|
+
|
53
|
+
Move table `countries` from `demography` schema to `public`:
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
move_table_to_schema 'demography.countries', :public
|
57
|
+
```
|
58
|
+
## Table and column comments
|
59
|
+
|
60
|
+
Provides the following methods to manage comments:
|
61
|
+
|
62
|
+
* `set_table_comment(table_name, comment)`
|
63
|
+
* `remove_table_comment(table_name)`
|
64
|
+
* `set_column_comment(table_name, column_name, comment)`
|
65
|
+
* `remove_column_comment(table_name, column_name, comment)`
|
66
|
+
* `set_column_comments(table_name, comments)`
|
67
|
+
* `remove_column_comments(table_name, *comments)`
|
68
|
+
|
69
|
+
|
70
|
+
### Examples
|
71
|
+
|
72
|
+
Set a comment on the given table.
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
set_table_comment :phone_numbers, 'This table stores phone numbers that conform to the North American Numbering Plan.'
|
76
|
+
```
|
77
|
+
Sets a comment on a given column of a given table.
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
set_column_comment :phone_numbers, :npa, 'Numbering Plan Area Code - Allowed ranges: [2-9] for first digit, [0-9] for second and third digit.'
|
81
|
+
```
|
82
|
+
Removes any comment from the given table.
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
remove_table_comment :phone_numbers
|
86
|
+
```
|
87
|
+
Removes any comment from the given column of a given table.
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
remove_column_comment :phone_numbers, :npa
|
91
|
+
```
|
92
|
+
Set comments on multiple columns in the table.
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
set_column_comments :phone_numbers, :npa => 'Numbering Plan Area Code - Allowed ranges: [2-9] for first digit, [0-9] for second and third digit.',
|
96
|
+
:nxx => 'Central Office Number'
|
97
|
+
```
|
98
|
+
Remove comments from multiple columns in the table.
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
remove_column_comments :phone_numbers, :npa, :nxx
|
102
|
+
```
|
103
|
+
PgSaurus also adds extra methods to change_table.
|
104
|
+
|
105
|
+
Set comments:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
change_table :phone_numbers do |t|
|
109
|
+
t.set_table_comment 'This table stores phone numbers that conform to the North American Numbering Plan.'
|
110
|
+
t.set_column_comment :npa, 'Numbering Plan Area Code - Allowed ranges: [2-9] for first digit, [0-9] for second and third digit.'
|
111
|
+
end
|
112
|
+
|
113
|
+
change_table :phone_numbers do |t|
|
114
|
+
t.set_column_comments :npa => 'Numbering Plan Area Code - Allowed ranges: [2-9] for first digit, [0-9] for second and third digit.',
|
115
|
+
:nxx => 'Central Office Number'
|
116
|
+
end
|
117
|
+
```
|
118
|
+
Remove comments:
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
change_table :phone_numbers do |t|
|
122
|
+
t.remove_table_comment
|
123
|
+
t.remove_column_comment :npa
|
124
|
+
end
|
125
|
+
|
126
|
+
change_table :phone_numbers do |t|
|
127
|
+
t.remove_column_comments :npa, :nxx
|
128
|
+
end
|
129
|
+
```
|
130
|
+
## Foreign keys
|
131
|
+
|
132
|
+
PgPower imported some code of [foreigner](https://github.com/matthuhiggins/foreigner)
|
133
|
+
gem and patched it to be schema-aware. Support was also added for index auto-generation.
|
134
|
+
|
135
|
+
You should disable `foreigner` in your Gemfile if you want to use `pg_saurus`.
|
136
|
+
|
137
|
+
If you do not want to generate an index, pass the :exclude_index => true option.
|
138
|
+
|
139
|
+
The syntax is compatible with `foreigner`:
|
140
|
+
|
141
|
+
|
142
|
+
Add foreign key from `comments` to `posts` using `post_id` column as key by default:
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
add_foreign_key(:comments, :posts)
|
146
|
+
```
|
147
|
+
Specify key explicitly:
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
add_foreign_key(:comments, :posts, :column => :blog_post_id)
|
151
|
+
```
|
152
|
+
Specify name of foreign key constraint:
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
add_foreign_key(:comments, :posts, :name => "comments_posts_fk")
|
156
|
+
```
|
157
|
+
It works with schemas as expected:
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
add_foreign_key('blog.comments', 'blog.posts')
|
161
|
+
```
|
162
|
+
Adds the index 'index_comments_on_post_id':
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
add_foreign_key(:comments, :posts)
|
166
|
+
```
|
167
|
+
Does not add an index:
|
168
|
+
|
169
|
+
```ruby
|
170
|
+
add_foreign_key(:comments, :posts, :exclude_index => true)
|
171
|
+
```
|
172
|
+
## Partial Indexes
|
173
|
+
|
174
|
+
Rails 4.x [pull request](https://github.com/rails/rails/pull/4956) was used as a
|
175
|
+
starting point to patch it to be schema-aware.
|
176
|
+
|
177
|
+
### Examples
|
178
|
+
|
179
|
+
Add a partial index to a table
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
add_index(:comments, [:country_id, :user_id], :where => 'active')
|
183
|
+
```
|
184
|
+
Add a partial index to a schema table
|
185
|
+
|
186
|
+
```ruby
|
187
|
+
add_index('blog.comments', :user_id, :where => 'active')
|
188
|
+
```
|
189
|
+
## Indexes on Expressions
|
190
|
+
|
191
|
+
PostgreSQL supports indexes on expressions. Right now, only basic functional
|
192
|
+
expressions are supported.
|
193
|
+
|
194
|
+
### Examples
|
195
|
+
|
196
|
+
Add an index to a column with a function
|
197
|
+
|
198
|
+
```ruby
|
199
|
+
add_index(:comments, "lower(text)")
|
200
|
+
```
|
201
|
+
|
202
|
+
You can also specify index access method
|
203
|
+
|
204
|
+
```ruby
|
205
|
+
create_extension 'btree_gist'
|
206
|
+
create_extension 'fuzzystrmatch'
|
207
|
+
add_index(:comments, 'dmetaphone(author)', :using => 'gist')
|
208
|
+
```
|
209
|
+
|
210
|
+
## Concurrent index creation
|
211
|
+
|
212
|
+
PostgreSQL supports concurent index creation. We added that feature to migration
|
213
|
+
DSL on index and foreign keys creation.
|
214
|
+
|
215
|
+
### Examples
|
216
|
+
|
217
|
+
Add an index concurrently to a table
|
218
|
+
|
219
|
+
```ruby
|
220
|
+
add_index :table, :column_id, :concurrently => true
|
221
|
+
```
|
222
|
+
|
223
|
+
Add an index concurrently along with foreign key
|
224
|
+
|
225
|
+
```ruby
|
226
|
+
add_foreign_key :table1, :table2, :column => :column_id, :concurrent_index => true
|
227
|
+
```
|
228
|
+
|
229
|
+
## Loading/Unloading postgresql extension modules
|
230
|
+
|
231
|
+
Postgresql is shipped with a number of [extension modules](http://www.postgresql.org/docs/9.1/static/contrib.html).
|
232
|
+
PgSaurus provides some tools
|
233
|
+
to [load](http://www.postgresql.org/docs/9.1/static/sql-createextension.html)/[unload](http://www.postgresql.org/docs/9.1/static/sql-dropextension.html)
|
234
|
+
such modules by the means of migrations.
|
235
|
+
|
236
|
+
Please note. CREATE/DROP EXTENSION command has been introduced in postgresql 9.1 only.
|
237
|
+
So this functionality will not be available for the previous versions.
|
238
|
+
|
239
|
+
### Examples
|
240
|
+
|
241
|
+
Load [fuzzystrmatch](http://www.postgresql.org/docs/9.1/static/fuzzystrmatch.html) extension module
|
242
|
+
and create its objects in schema *public*:
|
243
|
+
|
244
|
+
```ruby
|
245
|
+
create_extension "fuzzystrmatch"
|
246
|
+
```
|
247
|
+
|
248
|
+
|
249
|
+
Load version *1.0* of the [btree_gist](http://www.postgresql.org/docs/9.1/static/btree-gist.html) extension module
|
250
|
+
and create its objects in schema *demography*.
|
251
|
+
|
252
|
+
```ruby
|
253
|
+
create_extension "btree_gist", :schema_name => "demography", :version => "1.0"
|
254
|
+
```
|
255
|
+
|
256
|
+
Unload extension module:
|
257
|
+
|
258
|
+
```ruby
|
259
|
+
drop_extension "fuzzystrmatch"
|
260
|
+
```
|
261
|
+
|
262
|
+
## Views
|
263
|
+
|
264
|
+
Version 1.6.0 introduces experimental support for creating views. This API should only be used
|
265
|
+
with the understanding that it is preliminary 'alpha' at best.
|
266
|
+
|
267
|
+
### Example
|
268
|
+
|
269
|
+
```ruby
|
270
|
+
create_view "demography.citizens_view", "select * from demography.citizens"
|
271
|
+
```
|
272
|
+
|
273
|
+
## Tools
|
274
|
+
|
275
|
+
PgSaurus::Tools provides number of useful methods:
|
276
|
+
|
277
|
+
```ruby
|
278
|
+
PgSaurus::Tools.create_schema "services" # => create new PG schema "services"
|
279
|
+
PgSaurus::Tools.create_schema "nets" # => create new PG schema "nets"
|
280
|
+
PgSaurus::Tools.drop_schema "services" # => remove the PG schema "services"
|
281
|
+
PgSaurus::Tools.create_schema_if_not_exists "nets" # => Does nothing -- schema "nets" already exists
|
282
|
+
PgSaurus::Tools.drop_schema_if_exists "services" # => Does nothing -- schema "services" doesn't exist
|
283
|
+
PgSaurus::Tools.schemas # => ["public", "information_schema", "nets"]
|
284
|
+
PgSaurus::Tools.index_exists?(table, columns, options) # => returns true if an index exists for the given params
|
285
|
+
```
|
286
|
+
|
287
|
+
## Rails 3
|
288
|
+
|
289
|
+
PgSaurus does not support Rails 3.
|
290
|
+
|
291
|
+
## Running tests:
|
292
|
+
|
293
|
+
* Ensure your postgresql has postgres-contrib (Ubuntu) package installed. Tests depend on btree_gist and fuzzystrmatch extensions
|
294
|
+
* If on Mac, see below for installing contrib packages
|
295
|
+
* Configure `spec/dummy/config/database.yml` for development and test environments.
|
296
|
+
* Run `rake spec`.
|
297
|
+
* Make sure migrations don't raise exceptions and all specs pass.
|
298
|
+
|
299
|
+
### Installing contrib packages on Mac OS X:
|
300
|
+
* This assumes you are using MacPorts to install Postgres. If using homebrew or the Postgres App, you will need to adjust the instructions accordingly (please add to this README when you do)
|
301
|
+
* Assuming you installed with default options (including auto-clean), you will need to rebuild the postgresql port and keep the build files
|
302
|
+
* `sudo port -k -s build postgresql91`
|
303
|
+
* (adjust the version number above appropriately)
|
304
|
+
* Now you can make and install the btree_gist and any other contrib modules
|
305
|
+
* `cd ```port work postgresql91```/postgresql-9.1.7/contrib/btree_gist`
|
306
|
+
* (again, you may need to adjust the version number to your specific version)
|
307
|
+
* `sudo make all`
|
308
|
+
* `sudo make install`
|
309
|
+
* Done!
|
310
|
+
|
311
|
+
## TODO:
|
312
|
+
|
313
|
+
Support for JRuby:
|
314
|
+
|
315
|
+
* Jdbc driver provides its own `create_schema(schema, user)` method - solve conflicts.
|
316
|
+
|
317
|
+
## Credits
|
318
|
+
|
319
|
+
* [Potapov Sergey](https://github.com/greyblake) - schema support
|
320
|
+
* [Arthur Shagall](https://github.com/albertosaurus) - thanks for [pg_comment](https://github.com/albertosaurus/pg_comment)
|
321
|
+
* [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
|
322
|
+
* [Artem Ignatyev](https://github.com/cryo28) - extension modules load/unload support
|
323
|
+
* [Marcelo Silveira](https://github.com/mhfs) - thanks for rails partial index support that was backported into this gem
|
324
|
+
|
325
|
+
## Copyright and License
|
326
|
+
|
327
|
+
* Copyright (c) 2014 HornsAndHooves.
|
328
|
+
* Initial foreign key code taken from foreigner, Copyright (c) 2009 Matthew Higgins
|
329
|
+
* pg_comment Copyright (c) 2011 Arthur Shagall
|
330
|
+
* Partial index Copyright (c) 2012 Marcelo Silveira
|
331
|
+
* PgPower Copyright (c) 2012 TMX Credit.
|
332
|
+
|
333
|
+
Released under the MIT License. See the MIT-LICENSE file for more details.
|
334
|
+
|
335
|
+
## Contributing
|
336
|
+
|
337
|
+
Contributions are welcome. However, before issuing a pull request, please make sure of the following:
|
338
|
+
|
339
|
+
* All specs are passing (under ruby 1.9.3+)
|
340
|
+
* Any new features have test coverage.
|
341
|
+
* Anything that breaks backward compatibility has a very good reason for doing so.
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Colorizes text with ASCII colors.
|
2
|
+
# == Usage:
|
3
|
+
# include ColorizedText
|
4
|
+
#
|
5
|
+
# puts green "OK" # => green output
|
6
|
+
# puts bold "Running... # => bold output
|
7
|
+
# puts bold green "OK!!!" # => bold green output
|
8
|
+
module ColorizedText
|
9
|
+
# Colorize text using ASCII color code
|
10
|
+
def colorize(text, code)
|
11
|
+
"\033[#{code}m#{text}\033[0m"
|
12
|
+
end
|
13
|
+
|
14
|
+
# :nodoc:
|
15
|
+
def yellow(text)
|
16
|
+
colorize(text, 33)
|
17
|
+
end
|
18
|
+
|
19
|
+
# :nodoc:
|
20
|
+
def green(text)
|
21
|
+
colorize(text, 32)
|
22
|
+
end
|
23
|
+
|
24
|
+
# :nodoc:
|
25
|
+
def red(text)
|
26
|
+
colorize(text, 31)
|
27
|
+
end
|
28
|
+
|
29
|
+
# :nodoc:
|
30
|
+
def bold(text)
|
31
|
+
colorize(text, 1)
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,155 @@
|
|
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+)?, *)*)(\w+)\)/
|
7
|
+
|
8
|
+
|
9
|
+
# Redefine original add_index method to handle :concurrently option.
|
10
|
+
#
|
11
|
+
# Adds a new index to the table. +column_name+ can be a single Symbol, or
|
12
|
+
# an Array of Symbols.
|
13
|
+
#
|
14
|
+
# ====== Creating a partial index
|
15
|
+
# add_index(:accounts, [:branch_id, :party_id], :using => 'BTree'
|
16
|
+
# :unique => true, :concurrently => true, :where => 'active')
|
17
|
+
# generates
|
18
|
+
# CREATE UNIQUE INDEX CONCURRENTLY
|
19
|
+
# index_accounts_on_branch_id_and_party_id
|
20
|
+
# ON
|
21
|
+
# accounts(branch_id, party_id)
|
22
|
+
# WHERE
|
23
|
+
# active
|
24
|
+
#
|
25
|
+
def add_index_with_concurrently(table_name, column_name, options = {})
|
26
|
+
creation_method = options.delete(:concurrently) ? 'CONCURRENTLY' : nil
|
27
|
+
|
28
|
+
index_name,
|
29
|
+
index_type,
|
30
|
+
index_columns,
|
31
|
+
index_options,
|
32
|
+
index_algorithm,
|
33
|
+
index_using = add_index_options(table_name, column_name, options)
|
34
|
+
|
35
|
+
# GOTCHA:
|
36
|
+
# It ensures that there is no existing index only for the case when the index
|
37
|
+
# is created concurrently to avoid changing the error behavior for default
|
38
|
+
# index creation.
|
39
|
+
# -- zekefast 2012-09-25
|
40
|
+
# GOTCHA:
|
41
|
+
# This check prevents invalid index creation, so after migration failed
|
42
|
+
# here there is no need to go to database and clean it from invalid
|
43
|
+
# indexes. But note that this handles only one of the cases when index
|
44
|
+
# creation can fail!!! All other case should be procesed manually.
|
45
|
+
# -- zekefast 2012-09-25
|
46
|
+
if creation_method.present? && index_exists?(table_name, column_name, options)
|
47
|
+
raise ::PgSaurus::IndexExistsError,
|
48
|
+
"Index #{index_name} for `#{table_name}.#{column_name}` " \
|
49
|
+
"column can not be created concurrently, because such index already exists."
|
50
|
+
end
|
51
|
+
|
52
|
+
statements = []
|
53
|
+
statements << "CREATE #{index_type} INDEX"
|
54
|
+
statements << creation_method if creation_method.present?
|
55
|
+
statements << index_algorithm if index_algorithm.present?
|
56
|
+
statements << quote_column_name(index_name)
|
57
|
+
statements << "ON"
|
58
|
+
statements << quote_table_name(table_name)
|
59
|
+
statements << index_using if index_using.present?
|
60
|
+
statements << "(#{index_columns})" if index_columns.present?
|
61
|
+
statements << index_options if index_options.present?
|
62
|
+
|
63
|
+
sql = statements.join(' ')
|
64
|
+
execute(sql)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Check to see if an index exists on a table for a given index definition.
|
68
|
+
#
|
69
|
+
# === Examples
|
70
|
+
# # Check that a partial index exists
|
71
|
+
# index_exists?(:suppliers, :company_id, :where => 'active')
|
72
|
+
#
|
73
|
+
# # GIVEN: 'index_suppliers_on_company_id' UNIQUE, btree (company_id) WHERE active
|
74
|
+
# index_exists?(:suppliers, :company_id, :unique => true, :where => 'active') => true
|
75
|
+
# index_exists?(:suppliers, :company_id, :unique => true) => false
|
76
|
+
#
|
77
|
+
def index_exists?(table_name, column_name, options = {})
|
78
|
+
column_names = Array.wrap(column_name)
|
79
|
+
index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, :column => column_names)
|
80
|
+
|
81
|
+
# Always compare the index name
|
82
|
+
default_comparator = lambda { |index| index.name == index_name }
|
83
|
+
comparators = [default_comparator]
|
84
|
+
|
85
|
+
# Add a comparator for each index option that is part of the query
|
86
|
+
index_options = [:unique, :where]
|
87
|
+
index_options.each do |index_option|
|
88
|
+
comparators << if options.key?(index_option)
|
89
|
+
lambda do |index|
|
90
|
+
pg_where_clause = index.send(index_option)
|
91
|
+
# pg does nothing to boolean clauses, e.g. 'where active' => 'where active'
|
92
|
+
if pg_where_clause.is_a?(TrueClass) or pg_where_clause.is_a?(FalseClass)
|
93
|
+
pg_where_clause == options[index_option]
|
94
|
+
else
|
95
|
+
# pg adds parentheses around non-boolean clauses, e.g. 'where color IS NULL' => 'where (color is NULL)'
|
96
|
+
pg_where_clause.gsub!(/[()]/,'')
|
97
|
+
# pg casts string comparison ::text. e.g. "where color = 'black'" => "where ((color)::text = 'black'::text)"
|
98
|
+
pg_where_clause.gsub!(/::text/,'')
|
99
|
+
# prevent case from impacting the comparison
|
100
|
+
pg_where_clause.downcase == options[index_option].downcase
|
101
|
+
end
|
102
|
+
end
|
103
|
+
else
|
104
|
+
# If the given index_option is not an argument to the index_exists? query,
|
105
|
+
# select only those pg indexes that do not have the component
|
106
|
+
lambda { |index| index.send(index_option).blank? }
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Search all indexes for any that match all comparators
|
111
|
+
indexes(table_name).any? do |index|
|
112
|
+
comparators.inject(true) { |ret, comparator| ret && comparator.call(index) }
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Derive the name of the index from the given table name and options hash.
|
117
|
+
def index_name(table_name, options) #:nodoc:
|
118
|
+
if Hash === options # legacy support
|
119
|
+
if options[:column]
|
120
|
+
column_names = Array.wrap(options[:column]).map {|c| expression_index_name(c)}
|
121
|
+
"index_#{table_name}_on_#{column_names * '_and_'}"
|
122
|
+
elsif options[:name]
|
123
|
+
options[:name]
|
124
|
+
else
|
125
|
+
raise ArgumentError, "You must specify the index name"
|
126
|
+
end
|
127
|
+
else
|
128
|
+
index_name(table_name, :column => options)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Override super method to provide support for expression column names.
|
133
|
+
def quoted_columns_for_index(column_names, options = {})
|
134
|
+
column_names.map do |name|
|
135
|
+
if name =~ FUNCTIONAL_INDEX_REGEXP
|
136
|
+
"#{$1}(#{$2}#{quote_column_name($3)})"
|
137
|
+
else
|
138
|
+
quote_column_name(name)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
protected :quoted_columns_for_index
|
143
|
+
|
144
|
+
# Map an expression to a name appropriate for an index.
|
145
|
+
def expression_index_name(column_name)
|
146
|
+
if column_name =~ FUNCTIONAL_INDEX_REGEXP
|
147
|
+
"#{$1.downcase}_#{$3}"
|
148
|
+
else
|
149
|
+
column_name
|
150
|
+
end
|
151
|
+
end
|
152
|
+
private :expression_index_name
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|