postgres_ext 0.0.9 → 0.0.10
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.md +16 -0
- data/CONTRIBUTING.md +3 -3
- data/README.md +48 -11
- data/lib/postgres_ext/active_record.rb +1 -0
- data/lib/postgres_ext/active_record/connection_adapters/postgres_adapter.rb +35 -12
- data/lib/postgres_ext/active_record/sanitization.rb +30 -0
- data/lib/postgres_ext/active_record/schema_dumper.rb +20 -0
- data/lib/postgres_ext/arel/nodes/contained_within.rb +1 -0
- data/lib/postgres_ext/arel/visitors.rb +1 -1
- data/lib/postgres_ext/arel/visitors/{to_sql.rb → visitor.rb} +3 -2
- data/lib/postgres_ext/version.rb +1 -1
- data/spec/arel/arel_spec.rb +30 -0
- data/spec/arel/array_spec.rb +15 -4
- data/spec/dummy/db/migrate/20120501163758_create_people.rb +3 -3
- data/spec/dummy/db/schema.rb +11 -13
- data/spec/migrations/active_record_migration_spec.rb +8 -15
- data/spec/migrations/array_spec.rb +109 -36
- data/spec/migrations/cidr_spec.rb +1 -0
- data/spec/migrations/index_spec.rb +34 -7
- data/spec/migrations/inet_spec.rb +1 -0
- data/spec/migrations/macaddr_spec.rb +1 -0
- data/spec/migrations/uuid_spec.rb +1 -0
- data/spec/models/array_spec.rb +12 -1
- data/spec/schema_dumper/array_spec.rb +1 -0
- data/spec/schema_dumper/cidr_spec.rb +1 -0
- data/spec/schema_dumper/extension_spec.rb +14 -0
- data/spec/schema_dumper/index_spec.rb +28 -3
- data/spec/schema_dumper/inet_spec.rb +1 -0
- data/spec/schema_dumper/macaddr_spec.rb +1 -0
- data/spec/schema_dumper/uuid_spec.rb +1 -0
- data/spec/spec_helper.rb +4 -1
- metadata +10 -5
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
## 0.0.10
|
2
|
+
|
3
|
+
* Fixes parsing of number arrays when they are set from a string array - Alexey Noskov (@alno)
|
4
|
+
* Cleans up spec organization - Dan McClain (@danmcclain)
|
5
|
+
* Adds support for index operator classes (:index_opclass) in
|
6
|
+
migrations and schema dumps - & Dan McClain (@danmcclain)
|
7
|
+
* Fixes Arel Nodes created by postgres_ext - Dan McClain (@danmcclain)
|
8
|
+
* Add support to schema.rb to export and import extensions - Keenan
|
9
|
+
Brock (@kbrock)
|
10
|
+
* Handles PostgreSQL strings when passed in as defaults by fixing the
|
11
|
+
quote method
|
12
|
+
* Documentation updates. - Dan McClain & Doug Yun (@danmcclain
|
13
|
+
@duggieawesome)
|
14
|
+
* Fixes #update_column calls - Dan McClain (@danmcclain)
|
15
|
+
|
16
|
+
|
1
17
|
## 0.0.9
|
2
18
|
|
3
19
|
* Fixes #<attribute_name>?, Adds (pending) test case for #update_column - Dan McClain (@danmcclain)
|
data/CONTRIBUTING.md
CHANGED
@@ -6,7 +6,7 @@ If you need to open a new issue you *must* provide the following:
|
|
6
6
|
|
7
7
|
1. Version of Rails
|
8
8
|
|
9
|
-
Failure to include the
|
9
|
+
Failure to include the aforementioned requirements will result in the
|
10
10
|
issue being closed.
|
11
11
|
|
12
12
|
If you want to ensure that your issue gets fixed *fast* you should
|
@@ -28,8 +28,8 @@ If you'd like to submit a pull request please adhere to the following:
|
|
28
28
|
8. Follow Tim Pope's [model for git commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). This will make it
|
29
29
|
much easier to generate change logs and navigate through the logs
|
30
30
|
|
31
|
-
|
31
|
+
Please note that you must adhere to each of the aforementioned rules.
|
32
32
|
Failure to do so will result in an immediate closing of the pull
|
33
33
|
request. If you update and rebase the pull request to follow the
|
34
34
|
guidelines your pull request will be re-opened and considered for
|
35
|
-
inclusion.
|
35
|
+
inclusion.
|
data/README.md
CHANGED
@@ -34,12 +34,45 @@ ActiveRecord's data type handling.
|
|
34
34
|
* [Querying PostgreSQL datatypes](#querying-postgresql-datatypes)
|
35
35
|
|
36
36
|
### Usage Notes
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
37
|
+
Avoid the use of in place operators (ie `Array#<<`). These changes are
|
38
|
+
*not* tracked by Rails ([this issue](https://github.com/rails/rails/issues/6954))
|
39
|
+
explains why). In place modifications also modify the default object.
|
40
|
+
|
41
|
+
Assuming we have the following model:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
create_table :items do |t|
|
45
|
+
t.string :names, :array => true, :default => []
|
46
|
+
end
|
47
|
+
|
48
|
+
class Item < ActiveRecord::Base
|
49
|
+
end
|
50
|
+
```
|
51
|
+
|
52
|
+
The following will modify the default value of the names attribute.
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
a = Item.new
|
56
|
+
a.names << 'foo'
|
57
|
+
|
58
|
+
b = Item.new
|
59
|
+
puts b.names
|
60
|
+
# => ['foo']
|
61
|
+
```
|
62
|
+
|
63
|
+
The supported way of modifying `a.names`:
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
a = Item.new
|
67
|
+
a.names += 'foo'
|
68
|
+
|
69
|
+
b = Item.new
|
70
|
+
puts b.names
|
71
|
+
# => []
|
72
|
+
```
|
73
|
+
|
74
|
+
As a result, in place operators are discouraged and will not be
|
75
|
+
supported in postgres\_ext at this time.
|
43
76
|
|
44
77
|
## Migration/Schema.rb support
|
45
78
|
|
@@ -188,7 +221,8 @@ predicate for the `&&` operator.
|
|
188
221
|
```ruby
|
189
222
|
user_arel = User.arel_table
|
190
223
|
|
191
|
-
|
224
|
+
# Execute the query
|
225
|
+
User.where(user_arel[:tags].array_overlap(['one','two']))
|
192
226
|
# => SELECT \"users\".* FROM \"users\" WHERE \"users\".\"tags\" && '{one,two}'
|
193
227
|
```
|
194
228
|
|
@@ -220,9 +254,9 @@ user_arel = User.arel_table
|
|
220
254
|
any_tags_function = Arel::Nodes::NamedFunction.new('ANY', [user_arel[:tags]])
|
221
255
|
predicate = Arel::Nodes::Equality.new('test', any_tags_function)
|
222
256
|
|
223
|
-
|
257
|
+
# Execute the query
|
258
|
+
User.where(predicate)
|
224
259
|
#=> SELECT \"users\".* FROM \"users\" WHERE 'test' = ANY(\"users\".\"tags\")
|
225
|
-
|
226
260
|
```
|
227
261
|
|
228
262
|
The ALL version of this same predicate can be generated by swap `'ANY'`
|
@@ -250,7 +284,8 @@ predicate for the `<<` operator.
|
|
250
284
|
```ruby
|
251
285
|
user_arel = User.arel_table
|
252
286
|
|
253
|
-
|
287
|
+
# Execute the query
|
288
|
+
User.where(user_arel[:ip_address].contained_witin('127.0.0.1/24'))
|
254
289
|
# => SELECT \"users\".* FROM \"users\" WHERE \"users\".\"ip_address\" << '127.0.0.1/24'
|
255
290
|
```
|
256
291
|
|
@@ -258,10 +293,12 @@ User.where(user_arel[:ip_address].contained_witin('127.0.0.1/24')).to_sql
|
|
258
293
|
|
259
294
|
### Index types
|
260
295
|
|
261
|
-
Postgres\_ext allows you to specify
|
296
|
+
Postgres\_ext allows you to specify index type and index operator
|
297
|
+
class at index creation.
|
262
298
|
|
263
299
|
```ruby
|
264
300
|
add_index :table_name, :column, :index_type => :gin
|
301
|
+
add_index :table_name, :column, :index_type => :gin, :index_opclass => :gin_trgm_ops
|
265
302
|
```
|
266
303
|
|
267
304
|
### Where clauses
|
@@ -4,9 +4,8 @@ require 'pg_array_parser'
|
|
4
4
|
|
5
5
|
module ActiveRecord
|
6
6
|
module ConnectionAdapters
|
7
|
-
# class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where, :index_type)
|
8
7
|
class IndexDefinition
|
9
|
-
attr_accessor :index_type, :where
|
8
|
+
attr_accessor :index_type, :where, :index_opclass
|
10
9
|
end
|
11
10
|
|
12
11
|
class PostgreSQLColumn
|
@@ -205,8 +204,13 @@ module ActiveRecord
|
|
205
204
|
if options.is_a? Hash
|
206
205
|
index_type = options[:index_type] ? " USING #{options[:index_type]} " : ""
|
207
206
|
index_options = options[:where] ? " WHERE #{options[:where]}" : ""
|
207
|
+
index_opclass = options[:index_opclass]
|
208
208
|
end
|
209
|
-
execute "CREATE #{unique} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}#{index_type}(#{index_columns})#{index_options}"
|
209
|
+
execute "CREATE #{unique} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}#{index_type}(#{index_columns} #{index_opclass})#{index_options}"
|
210
|
+
end
|
211
|
+
|
212
|
+
def add_extension(extension_name, options={})
|
213
|
+
execute "CREATE extension if not exists \"#{extension_name}\""
|
210
214
|
end
|
211
215
|
|
212
216
|
def change_table(table_name, options = {})
|
@@ -268,15 +272,22 @@ module ActiveRecord
|
|
268
272
|
"'#{type_cast(value, column)}'"
|
269
273
|
elsif value.is_a? Array
|
270
274
|
"'#{array_to_string(value, column, true)}'"
|
275
|
+
elsif column.respond_to?(:array) && column.array && value =~ /^\{.*\}$/
|
276
|
+
"'#{value}'"
|
271
277
|
else
|
272
278
|
quote_without_extended_types(value, column)
|
273
279
|
end
|
274
280
|
end
|
275
281
|
alias_method_chain :quote, :extended_types
|
276
282
|
|
283
|
+
def opclasses
|
284
|
+
@opclasses ||= select_rows('SELECT opcname FROM pg_opclass').flatten.uniq
|
285
|
+
end
|
286
|
+
|
277
287
|
# this is based upon rails 4 changes to include different index methods
|
278
288
|
# Returns an array of indexes for the given table.
|
279
289
|
def indexes(table_name, name = nil)
|
290
|
+
opclasses
|
280
291
|
result = select_rows(<<-SQL, name)
|
281
292
|
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
|
282
293
|
FROM pg_class t
|
@@ -309,10 +320,15 @@ module ActiveRecord
|
|
309
320
|
#changed from rails 3.2
|
310
321
|
where = inddef.scan(/WHERE (.+)$/).flatten[0]
|
311
322
|
index_type = inddef.scan(/USING (.+?) /).flatten[0].to_sym
|
323
|
+
if index_type
|
324
|
+
index_op = inddef.scan(/USING .+? \(.+? (#{opclasses.join('|')})\)/).flatten
|
325
|
+
index_op = index_op[0].to_sym if index_op.present?
|
326
|
+
end
|
312
327
|
if column_names.present?
|
313
328
|
index_def = IndexDefinition.new(table_name, index_name, unique, column_names, [], orders)
|
314
329
|
index_def.where = where
|
315
330
|
index_def.index_type = index_type if index_type && index_type != :btree
|
331
|
+
index_def.index_opclass = index_op if index_type && index_type != :btree && index_op
|
316
332
|
index_def
|
317
333
|
# else nil
|
318
334
|
end
|
@@ -320,6 +336,10 @@ module ActiveRecord
|
|
320
336
|
end.compact
|
321
337
|
end
|
322
338
|
|
339
|
+
def extensions
|
340
|
+
select_rows('select extname from pg_extension', 'extensions').map { |row| row[0] }.delete_if {|name| name == 'plpgsql'}
|
341
|
+
end
|
342
|
+
|
323
343
|
private
|
324
344
|
|
325
345
|
def ipaddr_to_string(value)
|
@@ -331,25 +351,28 @@ module ActiveRecord
|
|
331
351
|
end
|
332
352
|
|
333
353
|
def item_to_string(value, column, encode_single_quotes = false)
|
334
|
-
if value.nil?
|
335
|
-
|
336
|
-
|
337
|
-
|
354
|
+
return 'NULL' if value.nil?
|
355
|
+
|
356
|
+
casted_value = type_cast(value, column, true)
|
357
|
+
|
358
|
+
if casted_value.is_a? String
|
359
|
+
casted_value = casted_value.dup
|
338
360
|
# Encode backslashes. One backslash becomes 4 in the resulting SQL.
|
339
361
|
# (why 4, and not 2? Trial and error shows 4 works, 2 fails to parse.)
|
340
|
-
|
362
|
+
casted_value.gsub!('\\', '\\\\\\\\')
|
341
363
|
# Encode a bare " in the string as \"
|
342
|
-
|
364
|
+
casted_value.gsub!('"', '\\"')
|
343
365
|
# PostgreSQL parses the string values differently if they are quoted for
|
344
366
|
# use in a statement, or if it will be used as part of a bound argument.
|
345
367
|
# For directly-inserted values (UPDATE foo SET bar='{"array"}') we need to
|
346
368
|
# escape ' as ''. For bound arguments, do not escape them.
|
347
369
|
if encode_single_quotes
|
348
|
-
|
370
|
+
casted_value.gsub!("'", "''")
|
349
371
|
end
|
350
|
-
|
372
|
+
|
373
|
+
"\"#{casted_value}\""
|
351
374
|
else
|
352
|
-
|
375
|
+
casted_value
|
353
376
|
end
|
354
377
|
end
|
355
378
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'active_record/sanitization'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Sanitization
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def sanitize_sql_hash_for_assignment(attrs)
|
9
|
+
attrs.map do |attr, value|
|
10
|
+
"#{connection.quote_column_name(attr)} = #{quote_bound_value(value, attr)}"
|
11
|
+
end.join(', ')
|
12
|
+
end
|
13
|
+
|
14
|
+
def quote_bound_value(value, column = nil, c = connection)
|
15
|
+
if column.present? && column != c
|
16
|
+
record_column = self.columns.select {|col| col.name == column}.first
|
17
|
+
c.quote(value, record_column)
|
18
|
+
elsif value.respond_to?(:map) && !value.acts_like?(:string)
|
19
|
+
if value.respond_to?(:empty?) && value.empty?
|
20
|
+
c.quote(nil)
|
21
|
+
else
|
22
|
+
value.map { |v| c.quote(v) }.join(',')
|
23
|
+
end
|
24
|
+
else
|
25
|
+
c.quote(value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -7,7 +7,26 @@ module ActiveRecord
|
|
7
7
|
VALID_COLUMN_SPEC_KEYS
|
8
8
|
end
|
9
9
|
|
10
|
+
def dump(stream)
|
11
|
+
header(stream)
|
12
|
+
# added
|
13
|
+
extensions(stream)
|
14
|
+
# /added
|
15
|
+
tables(stream)
|
16
|
+
trailer(stream)
|
17
|
+
stream
|
18
|
+
end
|
19
|
+
|
10
20
|
private
|
21
|
+
|
22
|
+
def extensions(stream)
|
23
|
+
exts=@connection.extensions
|
24
|
+
|
25
|
+
unless exts.empty?
|
26
|
+
stream.puts exts.map { |name| " add_extension \"#{name}\""}.join("\n") + "\n\n"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
11
30
|
def table(table, stream)
|
12
31
|
columns = @connection.columns(table)
|
13
32
|
begin
|
@@ -100,6 +119,7 @@ module ActiveRecord
|
|
100
119
|
# changed from rails 2.3
|
101
120
|
statement_parts << (':where => ' + index.where.inspect) if index.where
|
102
121
|
statement_parts << (':index_type => ' + index.index_type.inspect) if index.index_type
|
122
|
+
statement_parts << (':index_opclass => ' + index.index_opclass.inspect) if index.index_opclass
|
103
123
|
# /changed
|
104
124
|
|
105
125
|
' ' + statement_parts.join(', ')
|
@@ -1 +1 @@
|
|
1
|
-
require 'postgres_ext/arel/visitors/
|
1
|
+
require 'postgres_ext/arel/visitors/visitor'
|
@@ -1,7 +1,8 @@
|
|
1
|
-
require 'arel/visitors/
|
1
|
+
require 'arel/visitors/visitor'
|
2
2
|
module Arel
|
3
3
|
module Visitors
|
4
|
-
class
|
4
|
+
class Visitor
|
5
|
+
# We are adding our visitors to the main visitor for the time being until the right spot is found to monkey patch
|
5
6
|
private
|
6
7
|
def visit_Arel_Nodes_ContainedWithin o
|
7
8
|
"#{visit o.left} << #{visit o.right}"
|
data/lib/postgres_ext/version.rb
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Don\'t stomp all over the default ActiveRecord queries' do
|
4
|
+
let!(:adapter) { ActiveRecord::Base.connection }
|
5
|
+
|
6
|
+
before do
|
7
|
+
adapter.create_table :cars, :force => true do |t|
|
8
|
+
t.string :make
|
9
|
+
t.string :model
|
10
|
+
t.timestamps
|
11
|
+
end
|
12
|
+
|
13
|
+
class Car < ActiveRecord::Base
|
14
|
+
attr_accessible :make, :model
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
after do
|
19
|
+
adapter.drop_table :cars
|
20
|
+
Object.send(:remove_const, :Car)
|
21
|
+
end
|
22
|
+
|
23
|
+
describe 'Where Queries' do
|
24
|
+
describe 'Set query' do
|
25
|
+
it '' do
|
26
|
+
Car.where('id in (?)', [1,2]).to_sql.should eq "SELECT \"cars\".* FROM \"cars\" WHERE (id in (1,2))"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/spec/arel/array_spec.rb
CHANGED
@@ -32,15 +32,26 @@ describe 'Array Column Predicates' do
|
|
32
32
|
arel_table.where(arel_table[:tag_ids].array_overlap([1,2])).to_sql.should match /&& '\{1,2\}'/
|
33
33
|
end
|
34
34
|
|
35
|
+
it 'works with count (and other predicates)' do
|
36
|
+
arel_table = ArelArray.arel_table
|
37
|
+
|
38
|
+
ArelArray.where(arel_table[:tag_ids].array_overlap([1,2])).count.should eq 0
|
39
|
+
end
|
40
|
+
|
35
41
|
it 'returns matched records' do
|
36
42
|
one = ArelArray.create!(:tags => ['one'])
|
37
43
|
two = ArelArray.create!(:tags => ['two'])
|
38
44
|
arel_table = ArelArray.arel_table
|
39
45
|
|
40
|
-
|
41
|
-
ArelArray.
|
42
|
-
|
43
|
-
|
46
|
+
query = arel_table.where(arel_table[:tags].array_overlap(['one'])).project(Arel.sql('*'))
|
47
|
+
ArelArray.find_by_sql(query.to_sql).should include(one)
|
48
|
+
|
49
|
+
query = arel_table.where(arel_table[:tags].array_overlap(['two'])).project(Arel.sql('*'))
|
50
|
+
ArelArray.find_by_sql(query.to_sql).should include(two)
|
51
|
+
|
52
|
+
query = arel_table.where(arel_table[:tags].array_overlap(['two','one'])).project(Arel.sql('*'))
|
53
|
+
ArelArray.find_by_sql(query.to_sql).should include(two)
|
54
|
+
ArelArray.find_by_sql(query.to_sql).should include(one)
|
44
55
|
end
|
45
56
|
end
|
46
57
|
end
|
@@ -3,9 +3,9 @@ class CreatePeople < ActiveRecord::Migration
|
|
3
3
|
create_table :people do |t|
|
4
4
|
t.inet :ip
|
5
5
|
t.cidr :subnet, :subnet2
|
6
|
-
t.
|
7
|
-
t.
|
8
|
-
t.
|
6
|
+
t.integer :arrayzerd, :array => true
|
7
|
+
t.inet :inet_arrayzerd, :array => true
|
8
|
+
t.string :str_arrayzerd, :array => true, :limit => 5
|
9
9
|
t.string :test
|
10
10
|
|
11
11
|
t.timestamps
|
data/spec/dummy/db/schema.rb
CHANGED
@@ -13,20 +13,18 @@
|
|
13
13
|
|
14
14
|
ActiveRecord::Schema.define(:version => 20120501163758) do
|
15
15
|
|
16
|
-
|
17
|
-
t.inet "ip"
|
18
|
-
t.cidr "subnet"
|
19
|
-
t.cidr "subnet2"
|
20
|
-
t.integer_array "arrayzerd"
|
21
|
-
t.inet_array "inet_arrayzerd"
|
22
|
-
t.string_array "str_arrayzerd", :limit => 5
|
23
|
-
t.string "test"
|
24
|
-
t.datetime "created_at", :null => false
|
25
|
-
t.datetime "updated_at", :null => false
|
26
|
-
end
|
16
|
+
add_extension "hstore"
|
27
17
|
|
28
|
-
create_table "
|
29
|
-
t.
|
18
|
+
create_table "people", :force => true do |t|
|
19
|
+
t.inet "ip"
|
20
|
+
t.cidr "subnet"
|
21
|
+
t.cidr "subnet2"
|
22
|
+
t.integer "arrayzerd", :array => true
|
23
|
+
t.inet "inet_arrayzerd", :array => true
|
24
|
+
t.string "str_arrayzerd", :limit => 5, :array => true
|
25
|
+
t.string "test"
|
26
|
+
t.datetime "created_at", :null => false
|
27
|
+
t.datetime "updated_at", :null => false
|
30
28
|
end
|
31
29
|
|
32
30
|
end
|
@@ -1,11 +1,16 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe '
|
3
|
+
describe 'ActiveRecord Migrations' do
|
4
4
|
let!(:connection) { ActiveRecord::Base.connection }
|
5
|
+
after { connection.drop_table :generic_data_types }
|
5
6
|
it 'creates non-postgres_ext columns' do
|
6
|
-
|
7
7
|
lambda do
|
8
|
-
|
8
|
+
connection.create_table :generic_data_types do |t|
|
9
|
+
t.integer :col_1
|
10
|
+
t.string :col_2
|
11
|
+
t.datetime :col_3
|
12
|
+
end
|
13
|
+
connection.add_column :generic_data_types, :col_4, :text
|
9
14
|
end.should_not raise_exception
|
10
15
|
|
11
16
|
columns = connection.columns(:generic_data_types)
|
@@ -22,15 +27,3 @@ describe 'activerecord migrations' do
|
|
22
27
|
end
|
23
28
|
end
|
24
29
|
|
25
|
-
|
26
|
-
class DoMigration < ActiveRecord::Migration
|
27
|
-
def up
|
28
|
-
create_table :generic_data_types do |t|
|
29
|
-
t.integer :col_1
|
30
|
-
t.string :col_2
|
31
|
-
t.datetime :col_3
|
32
|
-
end
|
33
|
-
add_column :generic_data_types, :col_4, :text
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
@@ -1,47 +1,120 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe '
|
3
|
+
describe 'Array migrations' do
|
4
4
|
let!(:connection) { ActiveRecord::Base.connection }
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
|
6
|
+
context 'New Table' do
|
7
|
+
after { connection.drop_table :data_types }
|
8
|
+
describe 'Create table methods' do
|
9
|
+
it 'creates an array column' do
|
10
|
+
lambda do
|
11
|
+
connection.create_table :data_types do |t|
|
12
|
+
t.cidr :array_1, :array => true
|
13
|
+
t.cidr :array_2, :array_3, :array => true
|
14
|
+
t.column :array_4, :cidr, :array => true
|
15
|
+
end
|
16
|
+
end.should_not raise_exception
|
17
|
+
|
18
|
+
columns = connection.columns(:data_types)
|
19
|
+
|
20
|
+
array_1 = columns.detect { |c| c.name == 'array_1'}
|
21
|
+
array_2 = columns.detect { |c| c.name == 'array_2'}
|
22
|
+
array_3 = columns.detect { |c| c.name == 'array_3'}
|
23
|
+
array_4 = columns.detect { |c| c.name == 'array_4'}
|
24
|
+
|
25
|
+
array_1.sql_type.should eq 'cidr[]'
|
26
|
+
array_2.sql_type.should eq 'cidr[]'
|
27
|
+
array_3.sql_type.should eq 'cidr[]'
|
28
|
+
array_4.sql_type.should eq 'cidr[]'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'Existing Table' do
|
34
|
+
before { connection.create_table :data_types }
|
35
|
+
after { connection.drop_table :data_types }
|
36
|
+
describe 'Add Column' do
|
37
|
+
it 'creates an array column' do
|
38
|
+
lambda do
|
39
|
+
connection.add_column :data_types, :array_5, :cidr, :array => true
|
40
|
+
end.should_not raise_exception
|
41
|
+
|
42
|
+
columns = connection.columns(:data_types)
|
43
|
+
|
44
|
+
array_5 = columns.detect { |c| c.name == 'array_5'}
|
45
|
+
array_5.sql_type.should eq 'cidr[]'
|
11
46
|
end
|
12
|
-
end
|
47
|
+
end
|
13
48
|
|
14
|
-
|
15
|
-
|
16
|
-
|
49
|
+
describe 'Change table methods' do
|
50
|
+
it 'creates an array column' do
|
51
|
+
lambda do
|
52
|
+
connection.change_table :data_types do |t|
|
53
|
+
t.column :array_6, :cidr, :array => true
|
54
|
+
t.cidr :array_7, :array => true
|
55
|
+
end
|
56
|
+
end.should_not raise_exception
|
17
57
|
|
18
|
-
|
19
|
-
|
20
|
-
|
58
|
+
columns = connection.columns(:data_types)
|
59
|
+
|
60
|
+
array_6 = columns.detect { |c| c.name == 'array_6'}
|
61
|
+
array_7 = columns.detect { |c| c.name == 'array_7'}
|
62
|
+
|
63
|
+
array_6.sql_type.should eq 'cidr[]'
|
64
|
+
array_7.sql_type.should eq 'cidr[]'
|
21
65
|
end
|
22
|
-
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'Default Values' do
|
70
|
+
describe 'String defaults' do
|
71
|
+
after { connection.drop_table :default_strings }
|
72
|
+
it 'creates array column with proper defaults' do
|
73
|
+
lambda do
|
74
|
+
connection.create_table :default_strings do |t|
|
75
|
+
t.string :names_1, :array => true, :default => []
|
76
|
+
t.string :names_2, :array => true, :default => '{}'
|
77
|
+
t.string :names_3, :array => true, :default => ['something']
|
78
|
+
t.string :names_4, :array => true, :default => '{something}'
|
79
|
+
end
|
80
|
+
end.should_not raise_exception
|
81
|
+
columns = connection.columns(:default_strings)
|
82
|
+
|
83
|
+
names_1 = columns.detect { |c| c.name == 'names_1' }
|
84
|
+
names_2 = columns.detect { |c| c.name == 'names_2' }
|
85
|
+
names_3 = columns.detect { |c| c.name == 'names_3' }
|
86
|
+
names_4 = columns.detect { |c| c.name == 'names_4' }
|
87
|
+
|
88
|
+
names_1.default.should eq []
|
89
|
+
names_2.default.should eq []
|
90
|
+
names_3.default.should eq ['something']
|
91
|
+
names_4.default.should eq ['something']
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe 'Integer defaults' do
|
96
|
+
after { connection.drop_table :default_integers }
|
97
|
+
it 'creates array column with proper defaults' do
|
98
|
+
lambda do
|
99
|
+
connection.create_table :default_integers do |t|
|
100
|
+
t.integer :numbers_1, :array => true, :default => []
|
101
|
+
t.integer :numbers_2, :array => true, :default => '{}'
|
102
|
+
t.integer :numbers_3, :array => true, :default => [3]
|
103
|
+
t.integer :numbers_4, :array => true, :default => '{4}'
|
104
|
+
end
|
105
|
+
end.should_not raise_exception
|
106
|
+
columns = connection.columns(:default_integers)
|
107
|
+
|
108
|
+
numbers_1 = columns.detect { |c| c.name == 'numbers_1' }
|
109
|
+
numbers_2 = columns.detect { |c| c.name == 'numbers_2' }
|
110
|
+
numbers_3 = columns.detect { |c| c.name == 'numbers_3' }
|
111
|
+
numbers_4 = columns.detect { |c| c.name == 'numbers_4' }
|
23
112
|
|
24
|
-
|
25
|
-
|
26
|
-
|
113
|
+
numbers_1.default.should eq []
|
114
|
+
numbers_2.default.should eq []
|
115
|
+
numbers_3.default.should eq [3]
|
116
|
+
numbers_4.default.should eq [4]
|
27
117
|
end
|
28
|
-
end
|
29
|
-
|
30
|
-
columns = connection.columns(:data_types)
|
31
|
-
cidr_1 = columns.detect { |c| c.name == 'cidr_1'}
|
32
|
-
cidr_2 = columns.detect { |c| c.name == 'cidr_2'}
|
33
|
-
cidr_3 = columns.detect { |c| c.name == 'cidr_3'}
|
34
|
-
cidr_4 = columns.detect { |c| c.name == 'cidr_4'}
|
35
|
-
cidr_5 = columns.detect { |c| c.name == 'cidr_5'}
|
36
|
-
cidr_6 = columns.detect { |c| c.name == 'cidr_6'}
|
37
|
-
cidr_7 = columns.detect { |c| c.name == 'cidr_7'}
|
38
|
-
|
39
|
-
cidr_1.sql_type.should eq 'cidr[]'
|
40
|
-
cidr_2.sql_type.should eq 'cidr[]'
|
41
|
-
cidr_3.sql_type.should eq 'cidr[]'
|
42
|
-
cidr_4.sql_type.should eq 'cidr[]'
|
43
|
-
cidr_5.sql_type.should eq 'cidr[]'
|
44
|
-
cidr_6.sql_type.should eq 'cidr[]'
|
45
|
-
cidr_7.sql_type.should eq 'cidr[]'
|
118
|
+
end
|
46
119
|
end
|
47
120
|
end
|
@@ -2,21 +2,48 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe 'index migrations' do
|
4
4
|
let!(:connection) { ActiveRecord::Base.connection }
|
5
|
+
|
6
|
+
before do
|
7
|
+
connection.create_table :index_types do |t|
|
8
|
+
t.integer :col1, :array => true
|
9
|
+
t.integer :col2
|
10
|
+
t.text :col3
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
after { connection.drop_table :index_types }
|
15
|
+
|
5
16
|
it 'creates special index' do
|
6
17
|
lambda do
|
7
|
-
connection.create_table :index_types do |t|
|
8
|
-
t.integer :col1, :array => true
|
9
|
-
t.integer :col2
|
10
|
-
end
|
11
18
|
connection.add_index(:index_types, :col1, :index_type => :gin)
|
12
|
-
connection.add_index(:index_types, :col2, :where => '(col2 > 50)')
|
13
19
|
end.should_not raise_exception
|
14
20
|
|
15
21
|
indexes = connection.indexes(:index_types)
|
16
22
|
index_1 = indexes.detect { |c| c.columns.map(&:to_s) == ['col1']}
|
23
|
+
|
24
|
+
index_1.index_type.to_s.should eq 'gin'
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'creates indexes with where clauses' do
|
28
|
+
lambda do
|
29
|
+
connection.add_index(:index_types, :col2, :where => '(col2 > 50)')
|
30
|
+
end.should_not raise_exception
|
31
|
+
|
32
|
+
indexes = connection.indexes(:index_types)
|
17
33
|
index_2 = indexes.detect { |c| c.columns.map(&:to_s) == ['col2']}
|
18
34
|
|
19
|
-
|
20
|
-
|
35
|
+
index_2.where.should match /col2 > 50/
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'creates indexes with operator classes' do
|
39
|
+
lambda do
|
40
|
+
connection.add_index(:index_types, :col3, :index_type => :gin, :index_opclass => :gin_trgm_ops)
|
41
|
+
end.should_not raise_exception
|
42
|
+
|
43
|
+
indexes = connection.indexes(:index_types)
|
44
|
+
index_3 = indexes.detect { |c| c.columns.map(&:to_s) == ['col3']}
|
45
|
+
|
46
|
+
index_3.index_type.should eq :gin
|
47
|
+
index_3.index_opclass.should eq :gin_trgm_ops
|
21
48
|
end
|
22
49
|
end
|
data/spec/models/array_spec.rb
CHANGED
@@ -41,6 +41,18 @@ describe 'Models with array columns' do
|
|
41
41
|
u.reload
|
42
42
|
u.nick_names.should eq ['some', 'things']
|
43
43
|
end
|
44
|
+
|
45
|
+
it 'creates a user with an int array of some values' do
|
46
|
+
u = User.create(:favorite_numbers => [2,3,5])
|
47
|
+
u.reload
|
48
|
+
u.favorite_numbers.should eq [2,3,5]
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'creates a user with an int array of string values' do
|
52
|
+
u = User.create(:favorite_numbers => ['2','3','5'])
|
53
|
+
u.reload
|
54
|
+
u.favorite_numbers.should eq [2,3,5]
|
55
|
+
end
|
44
56
|
end
|
45
57
|
end
|
46
58
|
|
@@ -97,7 +109,6 @@ describe 'Models with array columns' do
|
|
97
109
|
context '#update_column' do
|
98
110
|
describe 'setting a value via update_column' do
|
99
111
|
it 'returns the value set when the record is retrieved' do
|
100
|
-
pending #This fails, not sure where to fix this, takes different code path than #update_attribute
|
101
112
|
user = User.create(:nick_names => [])
|
102
113
|
user.reload
|
103
114
|
|
@@ -2,6 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe 'array schema dump' do
|
4
4
|
let!(:connection) { ActiveRecord::Base.connection }
|
5
|
+
after { connection.drop_table :testings }
|
5
6
|
it 'correctly generates cidr column statements' do
|
6
7
|
stream = StringIO.new
|
7
8
|
connection.create_table :testings do |t|
|
@@ -2,6 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe 'CIDR schema dump' do
|
4
4
|
let!(:connection) { ActiveRecord::Base.connection }
|
5
|
+
after { connection.drop_table :testings }
|
5
6
|
it 'correctly generates cidr column statements' do
|
6
7
|
stream = StringIO.new
|
7
8
|
connection.create_table :testings do |t|
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'extension schema dump' do
|
4
|
+
let!(:connection) { ActiveRecord::Base.connection }
|
5
|
+
it 'correctly creates and exports database extensions' do
|
6
|
+
stream = StringIO.new
|
7
|
+
connection.add_extension 'hstore'
|
8
|
+
|
9
|
+
ActiveRecord::SchemaDumper.dump(connection, stream)
|
10
|
+
output = stream.string
|
11
|
+
|
12
|
+
output.should match /add_extension "hstore"/
|
13
|
+
end
|
14
|
+
end
|
@@ -1,14 +1,20 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe '
|
3
|
+
describe 'Index schema dumper' do
|
4
4
|
let!(:connection) { ActiveRecord::Base.connection }
|
5
|
-
|
5
|
+
|
6
|
+
before do
|
6
7
|
connection.create_table :index_types do |t|
|
7
8
|
t.integer :col1, :array => true
|
8
9
|
t.integer :col2
|
10
|
+
t.text :col3
|
9
11
|
end
|
12
|
+
end
|
13
|
+
|
14
|
+
after { connection.drop_table :index_types }
|
15
|
+
|
16
|
+
it 'handles index type parameters' do
|
10
17
|
connection.add_index(:index_types, :col1, :index_type => :gin)
|
11
|
-
connection.add_index(:index_types, :col2, :where => '(col2 > 50)')
|
12
18
|
|
13
19
|
stream = StringIO.new
|
14
20
|
ActiveRecord::SchemaDumper.dump(connection, stream)
|
@@ -16,6 +22,25 @@ describe 'index schema dumper' do
|
|
16
22
|
|
17
23
|
output.should match /:index_type => :gin/
|
18
24
|
output.should_not match /:index_type => :btree/
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'handles index where clauses' do
|
28
|
+
connection.add_index(:index_types, :col2, :where => '(col2 > 50)')
|
29
|
+
|
30
|
+
stream = StringIO.new
|
31
|
+
ActiveRecord::SchemaDumper.dump(connection, stream)
|
32
|
+
output = stream.string
|
33
|
+
|
19
34
|
output.should match /:where => "\(col2 > 50\)"/
|
20
35
|
end
|
36
|
+
|
37
|
+
it 'dumps index operator classes' do
|
38
|
+
connection.add_index(:index_types, :col3, :index_type => :gin, :index_opclass => :gin_trgm_ops)
|
39
|
+
|
40
|
+
stream = StringIO.new
|
41
|
+
ActiveRecord::SchemaDumper.dump(connection, stream)
|
42
|
+
output = stream.string
|
43
|
+
|
44
|
+
output.should match /:index_type => :gin,\s+:index_opclass => :gin_trgm_ops/
|
45
|
+
end
|
21
46
|
end
|
@@ -2,6 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe 'INET schema dump' do
|
4
4
|
let!(:connection) { ActiveRecord::Base.connection }
|
5
|
+
after { connection.drop_table :testings }
|
5
6
|
it 'correctly generates inet column statements' do
|
6
7
|
stream = StringIO.new
|
7
8
|
connection.create_table :testings do |t|
|
@@ -2,6 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe 'MACADDR schema dump' do
|
4
4
|
let!(:connection) { ActiveRecord::Base.connection }
|
5
|
+
after { connection.drop_table :testings }
|
5
6
|
it 'correctly generates macaddr column statements' do
|
6
7
|
stream = StringIO.new
|
7
8
|
connection.create_table :testings do |t|
|
@@ -2,6 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe 'UUID schema dump' do
|
4
4
|
let!(:connection) { ActiveRecord::Base.connection }
|
5
|
+
after { connection.drop_table :testings }
|
5
6
|
it 'correctly generates uuid column statements' do
|
6
7
|
stream = StringIO.new
|
7
8
|
connection.create_table :testings do |t|
|
data/spec/spec_helper.rb
CHANGED
@@ -5,6 +5,7 @@ require File.expand_path('../dummy/config/environment.rb', __FILE__)
|
|
5
5
|
require 'rspec/rails'
|
6
6
|
require 'rspec/autorun'
|
7
7
|
require 'bourne'
|
8
|
+
require 'debugger' unless ENV['CI'] || (RUBY_PLATFORM =~ /java/)
|
8
9
|
|
9
10
|
ENGINE_RAILS_ROOT=File.join(File.dirname(__FILE__), '../')
|
10
11
|
|
@@ -13,8 +14,10 @@ ENGINE_RAILS_ROOT=File.join(File.dirname(__FILE__), '../')
|
|
13
14
|
Dir[File.join(ENGINE_RAILS_ROOT, 'spec/support/**/*.rb')].each { |f| require f }
|
14
15
|
|
15
16
|
RSpec.configure do |config|
|
17
|
+
config.before(:suite) { ActiveRecord::Base.connection.add_extension('pg_trgm') }
|
18
|
+
config.use_transactional_fixtures = false
|
19
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
16
20
|
config.mock_with :mocha
|
17
|
-
config.use_transactional_fixtures = true
|
18
21
|
config.backtrace_clean_patterns = [
|
19
22
|
#/\/lib\d*\/ruby\//,
|
20
23
|
#/bin\//,
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: postgres_ext
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.10
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-12-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -159,15 +159,17 @@ files:
|
|
159
159
|
- lib/postgres_ext/active_record.rb
|
160
160
|
- lib/postgres_ext/active_record/connection_adapters.rb
|
161
161
|
- lib/postgres_ext/active_record/connection_adapters/postgres_adapter.rb
|
162
|
+
- lib/postgres_ext/active_record/sanitization.rb
|
162
163
|
- lib/postgres_ext/active_record/schema_dumper.rb
|
163
164
|
- lib/postgres_ext/arel.rb
|
164
165
|
- lib/postgres_ext/arel/nodes.rb
|
165
166
|
- lib/postgres_ext/arel/nodes/contained_within.rb
|
166
167
|
- lib/postgres_ext/arel/predications.rb
|
167
168
|
- lib/postgres_ext/arel/visitors.rb
|
168
|
-
- lib/postgres_ext/arel/visitors/
|
169
|
+
- lib/postgres_ext/arel/visitors/visitor.rb
|
169
170
|
- lib/postgres_ext/version.rb
|
170
171
|
- postgres_ext.gemspec
|
172
|
+
- spec/arel/arel_spec.rb
|
171
173
|
- spec/arel/array_spec.rb
|
172
174
|
- spec/arel/inet_spec.rb
|
173
175
|
- spec/columns/array_spec.rb
|
@@ -234,6 +236,7 @@ files:
|
|
234
236
|
- spec/models/inet_spec.rb
|
235
237
|
- spec/schema_dumper/array_spec.rb
|
236
238
|
- spec/schema_dumper/cidr_spec.rb
|
239
|
+
- spec/schema_dumper/extension_spec.rb
|
237
240
|
- spec/schema_dumper/index_spec.rb
|
238
241
|
- spec/schema_dumper/inet_spec.rb
|
239
242
|
- spec/schema_dumper/macaddr_spec.rb
|
@@ -253,7 +256,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
253
256
|
version: '0'
|
254
257
|
segments:
|
255
258
|
- 0
|
256
|
-
hash:
|
259
|
+
hash: 1227873910843194517
|
257
260
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
258
261
|
none: false
|
259
262
|
requirements:
|
@@ -262,7 +265,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
262
265
|
version: '0'
|
263
266
|
segments:
|
264
267
|
- 0
|
265
|
-
hash:
|
268
|
+
hash: 1227873910843194517
|
266
269
|
requirements: []
|
267
270
|
rubyforge_project:
|
268
271
|
rubygems_version: 1.8.23
|
@@ -270,6 +273,7 @@ signing_key:
|
|
270
273
|
specification_version: 3
|
271
274
|
summary: Extends ActiveRecord to handle native PostgreSQL data types
|
272
275
|
test_files:
|
276
|
+
- spec/arel/arel_spec.rb
|
273
277
|
- spec/arel/array_spec.rb
|
274
278
|
- spec/arel/inet_spec.rb
|
275
279
|
- spec/columns/array_spec.rb
|
@@ -336,6 +340,7 @@ test_files:
|
|
336
340
|
- spec/models/inet_spec.rb
|
337
341
|
- spec/schema_dumper/array_spec.rb
|
338
342
|
- spec/schema_dumper/cidr_spec.rb
|
343
|
+
- spec/schema_dumper/extension_spec.rb
|
339
344
|
- spec/schema_dumper/index_spec.rb
|
340
345
|
- spec/schema_dumper/inet_spec.rb
|
341
346
|
- spec/schema_dumper/macaddr_spec.rb
|