postgres_ext 0.0.9 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- 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
|