redhillonrails_core 1.0.6 → 1.0.8
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.
- data/CHANGELOG +9 -0
- data/README.rdoc +39 -14
- data/VERSION +1 -1
- data/lib/red_hill_consulting/core/active_record/base.rb +1 -0
- data/lib/red_hill_consulting/core/active_record/connection_adapters/abstract_adapter.rb +5 -0
- data/lib/red_hill_consulting/core/active_record/connection_adapters/index_definition.rb +10 -8
- data/lib/red_hill_consulting/core/active_record/connection_adapters/postgresql_adapter.rb +62 -20
- data/lib/red_hill_consulting/core/active_record/schema_dumper.rb +11 -3
- data/lib/redhillonrails_core.rb +0 -2
- data/redhillonrails_core.gemspec +5 -5
- metadata +6 -4
data/CHANGELOG
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
1.0.8
|
2
|
+
* handle expressions when creating indexes
|
3
|
+
* relay on ActiveRecord add_index method when handling legacy API
|
4
|
+
1.0.7
|
5
|
+
* support for partial indexes
|
6
|
+
1.0.6
|
7
|
+
* Fixed case-sensitive indexes
|
8
|
+
1.0.5
|
9
|
+
* added missing quoting
|
1
10
|
1.0.4
|
2
11
|
* Autoloading
|
3
12
|
1.0.3
|
data/README.rdoc
CHANGED
@@ -1,14 +1,28 @@
|
|
1
|
-
|
1
|
+
= Credits
|
2
|
+
|
3
|
+
This plugin was created by harukizaemon(http://github.com/harukizaemon) but is not supported currently by him.
|
4
|
+
I've forked it to make it edge-rails compatible and to introduce new features.
|
2
5
|
|
3
6
|
= RedHill on Rails Core
|
4
7
|
|
5
|
-
RedHill on Rails Core
|
8
|
+
RedHill on Rails Core provides bunch of useful database features:
|
6
9
|
|
7
10
|
* Creating and dropping views;
|
8
11
|
* Creating and removing foreign-keys;
|
12
|
+
* Creating partial indexes (postgresql only)
|
9
13
|
* Obtaining indexes directly from a model class; and
|
10
14
|
* Determining when <code>Schema.define()</code> is running.
|
11
15
|
|
16
|
+
=== Installation
|
17
|
+
|
18
|
+
As a gem
|
19
|
+
|
20
|
+
gem install redhillonrails_core
|
21
|
+
|
22
|
+
...or as a plugin
|
23
|
+
|
24
|
+
script/plugin install http://github.com/mlomnicki/redhillonrails_core.git
|
25
|
+
|
12
26
|
=== View Support
|
13
27
|
|
14
28
|
The plugin provides a mechanism for creating and dropping views as well as
|
@@ -118,11 +132,22 @@ indexes for a given model--<code>ActiveRecord::Base</code>--class. For example:
|
|
118
132
|
|
119
133
|
Would return all the indexes for the +invoices+ table.
|
120
134
|
|
121
|
-
===
|
135
|
+
=== Partial Indexes (indexes with conditions)
|
122
136
|
|
123
|
-
|
124
|
-
|
125
|
-
|
137
|
+
Partial indexes index only a portion of the database. Only PostgreSQL supports this feature.
|
138
|
+
|
139
|
+
add_index :users, :username, :unique => true, :conditions => {:state => "active"}
|
140
|
+
|
141
|
+
=== Indexing using an arbitrary expression (PostgreSQL only)
|
142
|
+
|
143
|
+
Create expression-based indexes:
|
144
|
+
|
145
|
+
add_index :users, [:first_name, :last_name], :expression => 'LOWER(first_name || last_name)'
|
146
|
+
add_index :places, :expression => 'sin(lat) * cos(lng)', :name => 'index_places_on_something'
|
147
|
+
add_index :documents, :body, :expression => "USING gin (to_tsvector('english', body))"
|
148
|
+
|
149
|
+
Expression is a pass-through: no quoting, escaping is done on it. Presumably, this expression is
|
150
|
+
part of migrations, or at least, code under your control.
|
126
151
|
|
127
152
|
=== Case-insensitive Indexes
|
128
153
|
|
@@ -141,14 +166,14 @@ Note also that this ties in well with Rails built-in support for case-insensitiv
|
|
141
166
|
|
142
167
|
validates_uniqueness_of :name, :case_sensitive => false
|
143
168
|
|
144
|
-
===
|
169
|
+
=== Schema Defining
|
170
|
+
|
171
|
+
The plugin also adds a method--<code>defining?()</code>--to
|
172
|
+
<code>ActiveRecord::Schema</code> to indicate when <code>define()</code> is running. This is necessary
|
173
|
+
as some migration plugins must change their behaviour accordingly.
|
145
174
|
|
146
|
-
|
147
|
-
* Foreign Key Migrations (foreign_key_migrations)
|
148
|
-
* Row Version Migrations (row_version_migrations)
|
149
|
-
* Schema Validations (schema_validations)
|
175
|
+
=== Contributors
|
150
176
|
|
151
|
-
|
177
|
+
* François Beausoleil - http://github.com/francois
|
178
|
+
* Greg Barnett
|
152
179
|
|
153
|
-
This plugin is copyright 2006 by RedHill Consulting, Pty. Ltd. and is released
|
154
|
-
under the MIT license.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.
|
1
|
+
1.0.8
|
@@ -26,6 +26,7 @@ module RedHillConsulting::Core::ActiveRecord
|
|
26
26
|
columns_without_redhillonrails_core
|
27
27
|
cols = columns_hash
|
28
28
|
indexes.each do |index|
|
29
|
+
next if index.columns.empty?
|
29
30
|
column_name = index.columns.reverse.detect { |name| name !~ /_id$/ } || index.columns.last
|
30
31
|
column = cols[column_name]
|
31
32
|
column.case_sensitive = index.case_sensitive?
|
@@ -58,5 +58,10 @@ module RedHillConsulting::Core::ActiveRecord::ConnectionAdapters
|
|
58
58
|
reverse_foreign_keys(name).each { |foreign_key| remove_foreign_key(foreign_key.table_name, foreign_key.name, options) }
|
59
59
|
drop_table_without_redhillonrails_core(name, options)
|
60
60
|
end
|
61
|
+
|
62
|
+
def supports_partial_indexes?
|
63
|
+
false
|
64
|
+
end
|
65
|
+
|
61
66
|
end
|
62
67
|
end
|
@@ -1,11 +1,13 @@
|
|
1
1
|
module RedHillConsulting::Core::ActiveRecord::ConnectionAdapters
|
2
|
-
|
3
|
-
|
4
|
-
@case_sensitive.nil? ? true : @case_sensitive
|
5
|
-
end
|
2
|
+
module IndexDefinition
|
3
|
+
attr_accessor :conditions, :expression
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
def case_sensitive?
|
6
|
+
@case_sensitive.nil? ? true : @case_sensitive
|
7
|
+
end
|
8
|
+
|
9
|
+
def case_sensitive=(case_sensitive)
|
10
|
+
@case_sensitive = case_sensitive
|
11
|
+
end
|
12
|
+
end
|
11
13
|
end
|
@@ -15,43 +15,74 @@ module RedHillConsulting::Core::ActiveRecord::ConnectionAdapters
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def add_index(table_name, column_name, options = {})
|
18
|
+
column_name, options = [], column_name if column_name.is_a?(Hash)
|
18
19
|
column_names = Array(column_name)
|
19
|
-
|
20
|
+
if column_names.empty?
|
21
|
+
raise ArgumentError, "No columns and :expression missing from options - cannot create index" if options[:expression].blank?
|
22
|
+
raise ArgumentError, "Index name not given. Pass :name option" if options[:name].blank?
|
23
|
+
end
|
24
|
+
|
25
|
+
index_type = options[:unique] ? "UNIQUE" : ""
|
26
|
+
index_name = options[:name] || index_name(table_name, column_names)
|
27
|
+
conditions = options[:conditions]
|
20
28
|
|
21
|
-
if
|
22
|
-
|
23
|
-
index_name = options[:name] || index_name
|
29
|
+
if column_names.empty? then
|
30
|
+
sql = "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{options[:expression]}"
|
24
31
|
else
|
25
|
-
|
26
|
-
end
|
32
|
+
quoted_column_names = column_names.map { |e| options[:case_sensitive] == false && e.to_s !~ /_id$/ ? "LOWER(#{quote_column_name(e)})" : quote_column_name(e) }
|
27
33
|
|
28
|
-
|
34
|
+
sql = "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names.join(", ")})"
|
35
|
+
sql += " WHERE (#{ ActiveRecord::Base.send(:sanitize_sql, conditions, quote_table_name(table_name)) })" if conditions
|
36
|
+
end
|
37
|
+
execute sql
|
38
|
+
end
|
29
39
|
|
30
|
-
|
40
|
+
def supports_partial_indexes?
|
41
|
+
true
|
31
42
|
end
|
43
|
+
|
44
|
+
INDEX_CASE_INSENSITIVE_REGEX = /\((.*LOWER\([^:]+(::text)?\).*)\)/i
|
45
|
+
INDEX_PARTIAL_REGEX = /\((.*)\)\s+WHERE (.*)$/i
|
46
|
+
INDEX_NON_BTREE_REGEX = /((?:gin|gist|hash).*)$/i
|
32
47
|
|
33
48
|
def indexes_with_redhillonrails_core(table_name, name = nil)
|
34
49
|
indexes = indexes_without_redhillonrails_core(table_name, name)
|
50
|
+
# Process indexes containg expressions and partial indexes
|
51
|
+
# Ie. consider
|
35
52
|
result = query(<<-SQL, name)
|
36
53
|
SELECT c2.relname, i.indisunique, pg_catalog.pg_get_indexdef(i.indexrelid, 0, true)
|
37
54
|
FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i
|
38
55
|
WHERE c.relname = '#{table_name}'
|
39
56
|
AND c.oid = i.indrelid AND i.indexrelid = c2.oid
|
40
|
-
AND i.indisprimary = 'f'
|
41
|
-
AND i.indexprs IS NOT NULL
|
57
|
+
AND i.indisprimary = 'f'
|
58
|
+
AND (i.indexprs IS NOT NULL OR i.indpred IS NOT NULL)
|
42
59
|
ORDER BY 1
|
43
60
|
SQL
|
44
61
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
62
|
+
|
63
|
+
# Correctly process complex indexes, ie:
|
64
|
+
# CREATE INDEX test_index ON custom_pages USING btree (lower(title::text), created_at) WHERE kind = 1 AND author_id = 3
|
65
|
+
result.each do |(index_name, unique, index_def)|
|
66
|
+
case_sensitive_match = INDEX_CASE_INSENSITIVE_REGEX.match(index_def)
|
67
|
+
partial_index_match = INDEX_PARTIAL_REGEX.match(index_def)
|
68
|
+
if case_sensitive_match || partial_index_match
|
69
|
+
# column_definitions may be ie. 'LOWER(lower)' or 'login, deleted_at' or LOWER(login), deleted_at
|
70
|
+
column_definitions = case_sensitive_match ? case_sensitive_match[1] : partial_index_match[1]
|
71
|
+
|
72
|
+
indexes.delete_if { |index| index.name == index_name } # prevent duplicated indexes
|
73
|
+
column_names = determine_index_column_names(column_definitions)
|
74
|
+
|
75
|
+
index = ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, index_name, unique == "t", column_names)
|
76
|
+
index.case_sensitive = !!case_sensitive_match
|
77
|
+
# conditions may be ie. active = true AND deleted_at IS NULL.
|
78
|
+
index.conditions = partial_index_match[2] if partial_index_match
|
79
|
+
indexes << index
|
80
|
+
|
81
|
+
elsif non_btree_match = INDEX_NON_BTREE_REGEX.match(index_def) then
|
82
|
+
indexes.delete_if { |index| index.name == index_name } # prevent duplicated indexes
|
83
|
+
|
84
|
+
index = ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, index_name, false, [])
|
85
|
+
index.expression = non_btree_match[1]
|
55
86
|
indexes << index
|
56
87
|
end
|
57
88
|
end
|
@@ -127,5 +158,16 @@ module RedHillConsulting::Core::ActiveRecord::ConnectionAdapters
|
|
127
158
|
|
128
159
|
foreign_keys
|
129
160
|
end
|
161
|
+
|
162
|
+
# Converts form like: column1, LOWER(column2)
|
163
|
+
# to: column1, column2
|
164
|
+
def determine_index_column_names(column_definitions)
|
165
|
+
column_definitions.split(", ").map do |name|
|
166
|
+
name = $1 if name =~ /^LOWER\(([^:]+)(::text)?\)$/i
|
167
|
+
name = $1 if name =~ /^"(.*)"$/
|
168
|
+
name
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
130
172
|
end
|
131
173
|
end
|
@@ -25,9 +25,17 @@ module RedHillConsulting::Core::ActiveRecord
|
|
25
25
|
def indexes_with_redhillonrails_core(table, stream)
|
26
26
|
indexes = @connection.indexes(table)
|
27
27
|
indexes.each do |index|
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
if index.expression.blank? then
|
29
|
+
stream.print " add_index #{index.table.inspect}, #{index.columns.inspect}, :name => #{index.name.inspect}"
|
30
|
+
stream.print ", :unique => true" if index.unique
|
31
|
+
stream.print ", :case_sensitive => false" unless index.case_sensitive?
|
32
|
+
stream.print ", :conditions => #{index.conditions.inspect}" unless index.conditions.blank?
|
33
|
+
else
|
34
|
+
stream.print " add_index #{index.table.inspect}"
|
35
|
+
stream.print ", :expression => #{index.expression.inspect}"
|
36
|
+
stream.print ", :name => #{index.name.inspect}"
|
37
|
+
end
|
38
|
+
|
31
39
|
stream.puts
|
32
40
|
end
|
33
41
|
stream.puts unless indexes.empty?
|
data/lib/redhillonrails_core.rb
CHANGED
@@ -35,5 +35,3 @@ ActiveRecord::ConnectionAdapters::TableDefinition.send(:include, RedHillConsulti
|
|
35
35
|
ActiveRecord::ConnectionAdapters::Column.send(:include, RedHillConsulting::Core::ActiveRecord::ConnectionAdapters::Column)
|
36
36
|
ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include, RedHillConsulting::Core::ActiveRecord::ConnectionAdapters::AbstractAdapter)
|
37
37
|
ActiveRecord::ConnectionAdapters::SchemaStatements.send(:include, RedHillConsulting::Core::ActiveRecord::ConnectionAdapters::SchemaStatements)
|
38
|
-
|
39
|
-
|
data/redhillonrails_core.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{redhillonrails_core}
|
8
|
-
s.version = "1.0.
|
8
|
+
s.version = "1.0.8"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = ["
|
12
|
-
s.date = %q{2010-
|
11
|
+
s.authors = ["Michał Łomnicki"]
|
12
|
+
s.date = %q{2010-10-19}
|
13
13
|
s.description = %q{RedHill on Rails Core is a plugin that features to support other RedHill on Rails plugins. It creates and drops views and foreign-keys or obtains indexes directly from a model class.}
|
14
14
|
s.email = %q{michal.lomnicki@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -48,14 +48,14 @@ Gem::Specification.new do |s|
|
|
48
48
|
s.homepage = %q{http://github.com/mlomnicki/redhillonrails_core}
|
49
49
|
s.rdoc_options = ["--charset=UTF-8"]
|
50
50
|
s.require_paths = ["lib"]
|
51
|
-
s.rubygems_version = %q{1.3.
|
51
|
+
s.rubygems_version = %q{1.3.7}
|
52
52
|
s.summary = %q{RedHill on Rails Core is a plugin that features to support other RedHill on Rails plugins}
|
53
53
|
|
54
54
|
if s.respond_to? :specification_version then
|
55
55
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
56
56
|
s.specification_version = 3
|
57
57
|
|
58
|
-
if Gem::Version.new(Gem::
|
58
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
59
59
|
else
|
60
60
|
end
|
61
61
|
else
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 1
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 1.0.
|
8
|
+
- 8
|
9
|
+
version: 1.0.8
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- "Micha\xC5\x82 \xC5\x81omnicki"
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-10-19 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|
@@ -65,6 +65,7 @@ rdoc_options:
|
|
65
65
|
require_paths:
|
66
66
|
- lib
|
67
67
|
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
68
69
|
requirements:
|
69
70
|
- - ">="
|
70
71
|
- !ruby/object:Gem::Version
|
@@ -72,6 +73,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
72
73
|
- 0
|
73
74
|
version: "0"
|
74
75
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
75
77
|
requirements:
|
76
78
|
- - ">="
|
77
79
|
- !ruby/object:Gem::Version
|
@@ -81,7 +83,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
83
|
requirements: []
|
82
84
|
|
83
85
|
rubyforge_project:
|
84
|
-
rubygems_version: 1.3.
|
86
|
+
rubygems_version: 1.3.7
|
85
87
|
signing_key:
|
86
88
|
specification_version: 3
|
87
89
|
summary: RedHill on Rails Core is a plugin that features to support other RedHill on Rails plugins
|