sequel 3.33.0 → 3.34.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (152) hide show
  1. data/CHANGELOG +140 -0
  2. data/Rakefile +7 -0
  3. data/bin/sequel +22 -2
  4. data/doc/dataset_basics.rdoc +1 -1
  5. data/doc/mass_assignment.rdoc +3 -1
  6. data/doc/querying.rdoc +28 -4
  7. data/doc/reflection.rdoc +23 -3
  8. data/doc/release_notes/3.34.0.txt +671 -0
  9. data/doc/schema_modification.rdoc +18 -2
  10. data/doc/virtual_rows.rdoc +49 -0
  11. data/lib/sequel/adapters/do/mysql.rb +0 -5
  12. data/lib/sequel/adapters/ibmdb.rb +9 -4
  13. data/lib/sequel/adapters/jdbc.rb +9 -4
  14. data/lib/sequel/adapters/jdbc/h2.rb +8 -2
  15. data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
  16. data/lib/sequel/adapters/jdbc/postgresql.rb +43 -0
  17. data/lib/sequel/adapters/jdbc/sqlite.rb +19 -0
  18. data/lib/sequel/adapters/mock.rb +24 -3
  19. data/lib/sequel/adapters/mysql.rb +29 -50
  20. data/lib/sequel/adapters/mysql2.rb +13 -28
  21. data/lib/sequel/adapters/oracle.rb +8 -2
  22. data/lib/sequel/adapters/postgres.rb +115 -20
  23. data/lib/sequel/adapters/shared/db2.rb +1 -1
  24. data/lib/sequel/adapters/shared/mssql.rb +14 -3
  25. data/lib/sequel/adapters/shared/mysql.rb +59 -11
  26. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +6 -0
  27. data/lib/sequel/adapters/shared/oracle.rb +1 -1
  28. data/lib/sequel/adapters/shared/postgres.rb +127 -30
  29. data/lib/sequel/adapters/shared/sqlite.rb +55 -38
  30. data/lib/sequel/adapters/sqlite.rb +9 -3
  31. data/lib/sequel/adapters/swift.rb +2 -2
  32. data/lib/sequel/adapters/swift/mysql.rb +0 -5
  33. data/lib/sequel/adapters/swift/postgres.rb +10 -0
  34. data/lib/sequel/ast_transformer.rb +4 -0
  35. data/lib/sequel/connection_pool.rb +8 -0
  36. data/lib/sequel/connection_pool/sharded_single.rb +5 -0
  37. data/lib/sequel/connection_pool/sharded_threaded.rb +17 -0
  38. data/lib/sequel/connection_pool/single.rb +5 -0
  39. data/lib/sequel/connection_pool/threaded.rb +14 -0
  40. data/lib/sequel/core.rb +24 -3
  41. data/lib/sequel/database/connecting.rb +24 -14
  42. data/lib/sequel/database/dataset_defaults.rb +1 -0
  43. data/lib/sequel/database/misc.rb +16 -25
  44. data/lib/sequel/database/query.rb +20 -2
  45. data/lib/sequel/database/schema_generator.rb +2 -2
  46. data/lib/sequel/database/schema_methods.rb +120 -23
  47. data/lib/sequel/dataset/actions.rb +91 -18
  48. data/lib/sequel/dataset/features.rb +5 -0
  49. data/lib/sequel/dataset/prepared_statements.rb +6 -2
  50. data/lib/sequel/dataset/sql.rb +68 -51
  51. data/lib/sequel/extensions/_pretty_table.rb +79 -0
  52. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +18 -13
  53. data/lib/sequel/extensions/migration.rb +4 -0
  54. data/lib/sequel/extensions/null_dataset.rb +90 -0
  55. data/lib/sequel/extensions/pg_array.rb +460 -0
  56. data/lib/sequel/extensions/pg_array_ops.rb +220 -0
  57. data/lib/sequel/extensions/pg_auto_parameterize.rb +174 -0
  58. data/lib/sequel/extensions/pg_hstore.rb +296 -0
  59. data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
  60. data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
  61. data/lib/sequel/extensions/pretty_table.rb +5 -71
  62. data/lib/sequel/extensions/query_literals.rb +79 -0
  63. data/lib/sequel/extensions/schema_caching.rb +76 -0
  64. data/lib/sequel/extensions/schema_dumper.rb +227 -31
  65. data/lib/sequel/extensions/select_remove.rb +35 -0
  66. data/lib/sequel/extensions/sql_expr.rb +4 -110
  67. data/lib/sequel/extensions/to_dot.rb +1 -1
  68. data/lib/sequel/model.rb +11 -2
  69. data/lib/sequel/model/associations.rb +35 -7
  70. data/lib/sequel/model/base.rb +159 -36
  71. data/lib/sequel/no_core_ext.rb +2 -0
  72. data/lib/sequel/plugins/caching.rb +25 -18
  73. data/lib/sequel/plugins/composition.rb +1 -1
  74. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  75. data/lib/sequel/plugins/identity_map.rb +11 -3
  76. data/lib/sequel/plugins/instance_filters.rb +10 -0
  77. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  78. data/lib/sequel/plugins/nested_attributes.rb +4 -3
  79. data/lib/sequel/plugins/prepared_statements.rb +3 -1
  80. data/lib/sequel/plugins/prepared_statements_associations.rb +5 -1
  81. data/lib/sequel/plugins/schema.rb +7 -2
  82. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  83. data/lib/sequel/plugins/static_cache.rb +99 -0
  84. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  85. data/lib/sequel/sql.rb +417 -7
  86. data/lib/sequel/version.rb +1 -1
  87. data/spec/adapters/firebird_spec.rb +1 -1
  88. data/spec/adapters/mssql_spec.rb +12 -15
  89. data/spec/adapters/mysql_spec.rb +81 -23
  90. data/spec/adapters/postgres_spec.rb +444 -77
  91. data/spec/adapters/spec_helper.rb +2 -0
  92. data/spec/adapters/sqlite_spec.rb +8 -8
  93. data/spec/core/connection_pool_spec.rb +85 -0
  94. data/spec/core/database_spec.rb +29 -5
  95. data/spec/core/dataset_spec.rb +171 -3
  96. data/spec/core/expression_filters_spec.rb +364 -0
  97. data/spec/core/mock_adapter_spec.rb +17 -3
  98. data/spec/core/schema_spec.rb +133 -0
  99. data/spec/extensions/association_dependencies_spec.rb +13 -13
  100. data/spec/extensions/caching_spec.rb +26 -3
  101. data/spec/extensions/class_table_inheritance_spec.rb +2 -2
  102. data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +23 -94
  103. data/spec/extensions/force_encoding_spec.rb +4 -2
  104. data/spec/extensions/hook_class_methods_spec.rb +5 -2
  105. data/spec/extensions/identity_map_spec.rb +17 -0
  106. data/spec/extensions/instance_filters_spec.rb +1 -1
  107. data/spec/extensions/lazy_attributes_spec.rb +2 -2
  108. data/spec/extensions/list_spec.rb +4 -4
  109. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  110. data/spec/extensions/migration_spec.rb +6 -2
  111. data/spec/extensions/nested_attributes_spec.rb +20 -0
  112. data/spec/extensions/null_dataset_spec.rb +85 -0
  113. data/spec/extensions/optimistic_locking_spec.rb +2 -2
  114. data/spec/extensions/pg_array_ops_spec.rb +105 -0
  115. data/spec/extensions/pg_array_spec.rb +196 -0
  116. data/spec/extensions/pg_auto_parameterize_spec.rb +64 -0
  117. data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
  118. data/spec/extensions/pg_hstore_spec.rb +195 -0
  119. data/spec/extensions/pg_statement_cache_spec.rb +209 -0
  120. data/spec/extensions/prepared_statements_spec.rb +4 -0
  121. data/spec/extensions/pretty_table_spec.rb +6 -0
  122. data/spec/extensions/query_literals_spec.rb +168 -0
  123. data/spec/extensions/schema_caching_spec.rb +41 -0
  124. data/spec/extensions/schema_dumper_spec.rb +231 -11
  125. data/spec/extensions/schema_spec.rb +14 -2
  126. data/spec/extensions/select_remove_spec.rb +38 -0
  127. data/spec/extensions/sharding_spec.rb +6 -6
  128. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  129. data/spec/extensions/spec_helper.rb +2 -1
  130. data/spec/extensions/sql_expr_spec.rb +28 -19
  131. data/spec/extensions/static_cache_spec.rb +145 -0
  132. data/spec/extensions/touch_spec.rb +1 -1
  133. data/spec/extensions/typecast_on_load_spec.rb +9 -1
  134. data/spec/integration/associations_test.rb +6 -6
  135. data/spec/integration/database_test.rb +1 -1
  136. data/spec/integration/dataset_test.rb +89 -26
  137. data/spec/integration/migrator_test.rb +2 -3
  138. data/spec/integration/model_test.rb +3 -3
  139. data/spec/integration/plugin_test.rb +85 -22
  140. data/spec/integration/prepared_statement_test.rb +28 -8
  141. data/spec/integration/schema_test.rb +78 -7
  142. data/spec/integration/spec_helper.rb +1 -0
  143. data/spec/integration/timezone_test.rb +1 -1
  144. data/spec/integration/transaction_test.rb +4 -6
  145. data/spec/integration/type_test.rb +2 -2
  146. data/spec/model/associations_spec.rb +94 -8
  147. data/spec/model/base_spec.rb +4 -4
  148. data/spec/model/hooks_spec.rb +2 -2
  149. data/spec/model/model_spec.rb +19 -7
  150. data/spec/model/record_spec.rb +135 -58
  151. data/spec/model/spec_helper.rb +1 -0
  152. metadata +35 -7
@@ -129,6 +129,7 @@ describe "Reversible Migrations with Sequel.migration{change{}}" do
129
129
  rename_column :e, :g
130
130
  end
131
131
  create_view(:c, 'SELECT * FROM b')
132
+ create_join_table(:cat_id=>:cats, :dog_id=>:dogs)
132
133
  end
133
134
  end
134
135
 
@@ -150,13 +151,16 @@ describe "Reversible Migrations with Sequel.migration{change{}}" do
150
151
  [:add_spatial_index, :e, {:name=>"e_s"}],
151
152
  [:rename_column, :e, :g]]
152
153
  ],
153
- [:create_view, :c, "SELECT * FROM b"]]
154
+ [:create_view, :c, "SELECT * FROM b"],
155
+ [:create_join_table, {:cat_id=>:cats, :dog_id=>:dogs}]]
154
156
  end
155
157
 
156
158
  specify "should execute down with reversing actions in reverse order" do
157
159
  p = @p
158
160
  Sequel.migration{change(&p)}.apply(@db, :down)
159
- @db.actions.should == [[:drop_view, :c],
161
+ @db.actions.should == [
162
+ [:drop_join_table, {:cat_id=>:cats, :dog_id=>:dogs}],
163
+ [:drop_view, :c],
160
164
  [:alter_table, [
161
165
  [:rename_column, :g, :e],
162
166
  [:drop_index, :e, {:name=>"e_s"}],
@@ -110,6 +110,16 @@ describe "NestedAttributes plugin" do
110
110
  @db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "UPDATE albums SET name = 'Al2' WHERE (id = 10)"]
111
111
  end
112
112
 
113
+ it "should support updating one_to_many objects with _delete/_remove flags set to false" do
114
+ al = @Album.load(:id=>10, :name=>'Al')
115
+ ar = @Artist.load(:id=>20, :name=>'Ar')
116
+ ar.associations[:albums] = [al]
117
+ ar.set(:albums_attributes=>[{:id=>10, :name=>'Al2', :_delete => 'f', :_remove => '0'}])
118
+ @db.sqls.should == []
119
+ ar.save
120
+ @db.sqls.should == ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "UPDATE albums SET name = 'Al2' WHERE (id = 10)"]
121
+ end
122
+
113
123
  it "should support updating many_to_many objects" do
114
124
  a = @Album.load(:id=>10, :name=>'Al')
115
125
  t = @Tag.load(:id=>20, :name=>'T')
@@ -120,6 +130,16 @@ describe "NestedAttributes plugin" do
120
130
  @db.sqls.should == ["UPDATE albums SET name = 'Al' WHERE (id = 10)", "UPDATE tags SET name = 'T2' WHERE (id = 20)"]
121
131
  end
122
132
 
133
+ it "should support updating many_to_many objects with _delete/_remove flags set to false" do
134
+ a = @Album.load(:id=>10, :name=>'Al')
135
+ t = @Tag.load(:id=>20, :name=>'T')
136
+ a.associations[:tags] = [t]
137
+ a.set(:tags_attributes=>[{:id=>20, :name=>'T2', '_delete' => false, '_remove' => 'F'}])
138
+ @db.sqls.should == []
139
+ a.save
140
+ @db.sqls.should == ["UPDATE albums SET name = 'Al' WHERE (id = 10)", "UPDATE tags SET name = 'T2' WHERE (id = 20)"]
141
+ end
142
+
123
143
  it "should support removing many_to_one objects" do
124
144
  al = @Album.load(:id=>10, :name=>'Al')
125
145
  ar = @Artist.load(:id=>20, :name=>'Ar')
@@ -0,0 +1,85 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "null_dataset extension" do
4
+ before do
5
+ @db = Sequel::mock(:fetch=>{:id=>1}, :autoid=>1, :numrows=>1, :columns=>[:id])
6
+ @ds = @db[:table].nullify
7
+ @i = 0
8
+ @pr = proc{|*a| @i += 1}
9
+ end
10
+ after do
11
+ @db.sqls.should == [] unless @skip_check
12
+ end
13
+
14
+ it "should make each be a noop" do
15
+ @ds.each(&@pr).should equal(@ds)
16
+ @i.should == 0
17
+ end
18
+
19
+ it "should make fetch_rows be a noop" do
20
+ @ds.fetch_rows("SELECT 1", &@pr).should == nil
21
+ @i.should == 0
22
+ end
23
+
24
+ it "should make insert be a noop" do
25
+ @ds.insert(1).should == nil
26
+ end
27
+
28
+ it "should make update be a noop" do
29
+ @ds.update(:a=>1).should == 0
30
+ end
31
+
32
+ it "should make delete be a noop" do
33
+ @ds.delete.should == 0
34
+ end
35
+
36
+ it "should make truncate be a noop" do
37
+ @ds.truncate.should == nil
38
+ end
39
+
40
+ it "should make execute_* be a noop" do
41
+ @ds.send(:execute_ddl,'FOO').should == nil
42
+ @ds.send(:execute_insert,'FOO').should == nil
43
+ @ds.send(:execute_dui,'FOO').should == nil
44
+ @ds.send(:execute,'FOO').should == nil
45
+ end
46
+
47
+ it "should have working columns" do
48
+ @skip_check = true
49
+ @ds.columns.should == [:id]
50
+ @db.sqls.should == ['SELECT * FROM table LIMIT 1']
51
+ end
52
+
53
+ it "should have count return 0" do
54
+ @ds.count.should == 0
55
+ end
56
+
57
+ it "should have empty return true" do
58
+ @ds.empty?.should == true
59
+ end
60
+
61
+ it "should make import a noop" do
62
+ @ds.import([:id], [[1], [2], [3]]).should == nil
63
+ end
64
+
65
+ it "should have nullify method returned modified receiver" do
66
+ @skip_check = true
67
+ ds = @db[:table]
68
+ ds.nullify.should_not equal(ds)
69
+ ds.each(&@pr)
70
+ @db.sqls.should == ['SELECT * FROM table']
71
+ @i.should == 1
72
+ end
73
+
74
+ it "should have null! method modify receiver" do
75
+ ds = @db[:table]
76
+ ds.nullify!.should equal(ds)
77
+ ds.each(&@pr)
78
+ @i.should == 0
79
+ end
80
+
81
+ it "should work with method chaining" do
82
+ @ds.where(:a=>1).select(:b).each(&@pr)
83
+ @i.should == 0
84
+ end
85
+ end
@@ -6,7 +6,7 @@ describe "optimistic_locking plugin" do
6
6
  end
7
7
  h = {1=>{:id=>1, :name=>'John', :lock_version=>2}}
8
8
  lv = @lv = "lock_version"
9
- @c.dataset.numrows = proc do |sql|
9
+ @c.instance_dataset.numrows = @c.dataset.numrows = proc do |sql|
10
10
  case sql
11
11
  when /UPDATE people SET (name|#{lv}) = ('Jim'|'Bob'|\d+), (?:name|#{lv}) = ('Jim'|'Bob'|\d+) WHERE \(\(id = (\d+)\) AND \(#{lv} = (\d+)\)\)/
12
12
  name, nlv = $1 == 'name' ? [$2, $3] : [$3, $2]
@@ -104,7 +104,7 @@ describe "optimistic_locking plugin" do
104
104
  end
105
105
 
106
106
  specify "should not increment the lock column when the update fails" do
107
- @c.dataset.meta_def(:update) { raise Exception }
107
+ @c.instance_dataset.meta_def(:update) { raise Exception }
108
108
  p1 = @c[1]
109
109
  p1.modified!
110
110
  lv = p1.lock_version
@@ -0,0 +1,105 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "Sequel::Postgres::ArrayOp" do
4
+ # Attempt to load pg_array first to test PGArray -> ArrayOp code
5
+ pg_array_loaded = begin
6
+ Sequel.extension :pg_array
7
+ true
8
+ rescue LoadError
9
+ false
10
+ end
11
+
12
+ Sequel.extension :pg_array_ops
13
+
14
+ before do
15
+ @db = Sequel.connect('mock://postgres', :quote_identifiers=>false)
16
+ @a = :a.pg_array
17
+ end
18
+
19
+ it "should support the standard mathematical operators" do
20
+ @db.literal(@a < @a).should == "(a < a)"
21
+ @db.literal(@a <= @a).should == "(a <= a)"
22
+ @db.literal(@a > @a).should == "(a > a)"
23
+ @db.literal(@a >= @a).should == "(a >= a)"
24
+ end
25
+
26
+ it "#[] should support subscript access" do
27
+ @db.literal(@a[1]).should == "a[1]"
28
+ @db.literal(@a[1][2]).should == "a[1][2]"
29
+ end
30
+
31
+ it "#any should use the ANY method" do
32
+ @db.literal(1=>@a.any).should == "(1 = ANY(a))"
33
+ end
34
+
35
+ it "#all should use the ALL method" do
36
+ @db.literal(1=>@a.all).should == "(1 = ALL(a))"
37
+ end
38
+
39
+ it "#contains should use the @> operator" do
40
+ @db.literal(@a.contains(:b)).should == "(a @> b)"
41
+ end
42
+
43
+ it "#contained_by should use the <@ operator" do
44
+ @db.literal(@a.contained_by(:b)).should == "(a <@ b)"
45
+ end
46
+
47
+ it "#overlaps should use the && operator" do
48
+ @db.literal(@a.overlaps(:b)).should == "(a && b)"
49
+ end
50
+
51
+ it "#push/concat should use the || operator in append mode" do
52
+ @db.literal(@a.push(:b)).should == "(a || b)"
53
+ @db.literal(@a.concat(:b)).should == "(a || b)"
54
+ end
55
+
56
+ it "#unshift should use the || operator in prepend mode" do
57
+ @db.literal(@a.unshift(:b)).should == "(b || a)"
58
+ end
59
+
60
+ it "#dims should use the array_dims function" do
61
+ @db.literal(@a.dims).should == "array_dims(a)"
62
+ end
63
+
64
+ it "#length should use the array_length function" do
65
+ @db.literal(@a.length).should == "array_length(a, 1)"
66
+ @db.literal(@a.length(2)).should == "array_length(a, 2)"
67
+ end
68
+
69
+ it "#length should use the array_lower function" do
70
+ @db.literal(@a.lower).should == "array_lower(a, 1)"
71
+ @db.literal(@a.lower(2)).should == "array_lower(a, 2)"
72
+ end
73
+
74
+ it "#to_string/join should use the array_to_string function" do
75
+ @db.literal(@a.to_string).should == "array_to_string(a, '', NULL)"
76
+ @db.literal(@a.join).should == "array_to_string(a, '', NULL)"
77
+ @db.literal(@a.join(':')).should == "array_to_string(a, ':', NULL)"
78
+ @db.literal(@a.join(':', '*')).should == "array_to_string(a, ':', '*')"
79
+ end
80
+
81
+ it "#unnest should use the unnest function" do
82
+ @db.literal(@a.unnest).should == "unnest(a)"
83
+ end
84
+
85
+ it "#pg_array should return self" do
86
+ @a.pg_array.should equal(@a)
87
+ end
88
+
89
+ it "should be able to turn expressions into array ops using pg_array" do
90
+ @db.literal(:a.qualify(:b).pg_array.push(3)).should == "(b.a || 3)"
91
+ @db.literal(:a.sql_function(:b).pg_array.push(3)).should == "(a(b) || 3)"
92
+ end
93
+
94
+ it "should be able to turn literal strings into array ops using pg_array" do
95
+ @db.literal('a'.lit.pg_array.unnest).should == "unnest(a)"
96
+ end
97
+
98
+ it "should be able to turn symbols into array ops using pg_array" do
99
+ @db.literal(:a.pg_array.unnest).should == "unnest(a)"
100
+ end
101
+
102
+ it "should allow transforming PGArray instances into ArrayOp instances" do
103
+ @db.literal([1,2].pg_array.op.push(3)).should == "(ARRAY[1,2] || 3)"
104
+ end if pg_array_loaded
105
+ end
@@ -0,0 +1,196 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ begin
4
+ Sequel.extension :pg_array
5
+ rescue LoadError => e
6
+ skip_warn "can't load pg_array extension (#{e.class}: #{e})"
7
+ else
8
+ describe "pg_array extension" do
9
+ before do
10
+ @db = Sequel.connect('mock://postgres', :quote_identifiers=>false)
11
+ @db.extend(Module.new{def bound_variable_arg(arg, conn) arg end})
12
+ @m = Sequel::Postgres
13
+ end
14
+
15
+ it "should parse single dimensional text arrays" do
16
+ @m::PGStringArray.parse("{a}").to_a.first.should be_a_kind_of(String)
17
+ @m::PGStringArray.parse("{}").to_a.should == []
18
+ @m::PGStringArray.parse("{a}").to_a.should == ['a']
19
+ @m::PGStringArray.parse('{"a b"}').to_a.should == ['a b']
20
+ @m::PGStringArray.parse('{a,b}').to_a.should == ['a', 'b']
21
+ end
22
+
23
+ it "should parse multi-dimensional text arrays" do
24
+ @m::PGStringArray.parse("{{}}").to_a.should == [[]]
25
+ @m::PGStringArray.parse("{{a},{b}}").to_a.should == [['a'], ['b']]
26
+ @m::PGStringArray.parse('{{"a b"},{c}}').to_a.should == [['a b'], ['c']]
27
+ @m::PGStringArray.parse('{{{a},{b}},{{c},{d}}}').to_a.should == [[['a'], ['b']], [['c'], ['d']]]
28
+ @m::PGStringArray.parse('{{{a,e},{b,f}},{{c,g},{d,h}}}').to_a.should == [[['a', 'e'], ['b', 'f']], [['c', 'g'], ['d', 'h']]]
29
+ end
30
+
31
+ it "should parse text arrays with embedded deliminaters" do
32
+ @m::PGStringArray.parse('{{"{},","\\",\\,\\\\\\"\\""}}').to_a.should == [['{},', '",,\\""']]
33
+ end
34
+
35
+ it "should parse single dimensional integer arrays" do
36
+ @m::PGIntegerArray.parse("{1}").to_a.first.should be_a_kind_of(Integer)
37
+ @m::PGIntegerArray.parse("{}").to_a.should == []
38
+ @m::PGIntegerArray.parse("{1}").to_a.should == [1]
39
+ @m::PGIntegerArray.parse('{2,3}').to_a.should == [2, 3]
40
+ @m::PGIntegerArray.parse('{3,4,5}').to_a.should == [3, 4, 5]
41
+ end
42
+
43
+ it "should parse multiple dimensional integer arrays" do
44
+ @m::PGIntegerArray.parse("{{}}").to_a.should == [[]]
45
+ @m::PGIntegerArray.parse("{{1}}").to_a.should == [[1]]
46
+ @m::PGIntegerArray.parse('{{2},{3}}').to_a.should == [[2], [3]]
47
+ @m::PGIntegerArray.parse('{{{1,2},{3,4}},{{5,6},{7,8}}}').to_a.should == [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
48
+ end
49
+
50
+ it "should parse single dimensional float arrays" do
51
+ @m::PGFloatArray.parse("{}").to_a.should == []
52
+ @m::PGFloatArray.parse("{1.5}").to_a.should == [1.5]
53
+ @m::PGFloatArray.parse('{2.5,3.5}').to_a.should == [2.5, 3.5]
54
+ @m::PGFloatArray.parse('{3.5,4.5,5.5}').to_a.should == [3.5, 4.5, 5.5]
55
+ end
56
+
57
+ it "should parse multiple dimensional float arrays" do
58
+ @m::PGFloatArray.parse("{{}}").to_a.should == [[]]
59
+ @m::PGFloatArray.parse("{{1.5}}").to_a.should == [[1.5]]
60
+ @m::PGFloatArray.parse('{{2.5},{3.5}}').to_a.should == [[2.5], [3.5]]
61
+ @m::PGFloatArray.parse('{{{1.5,2.5},{3.5,4.5}},{{5.5,6.5},{7.5,8.5}}}').to_a.should == [[[1.5, 2.5], [3.5, 4.5]], [[5.5, 6.5], [7.5, 8.5]]]
62
+ end
63
+
64
+ it "should parse integers in float arrays as floats" do
65
+ @m::PGFloatArray.parse("{1}").to_a.first.should be_a_kind_of(Float)
66
+ @m::PGFloatArray.parse("{1}").to_a.should == [1.0]
67
+ @m::PGFloatArray.parse('{{{1,2},{3,4}},{{5,6},{7,8}}}').to_a.should == [[[1.0, 2.0], [3.0, 4.0]], [[5.0, 6.0], [7.0, 8.0]]]
68
+ end
69
+
70
+ it "should parse single dimensional decimal arrays" do
71
+ @m::PGDecimalArray.parse("{}").to_a.should == []
72
+ @m::PGDecimalArray.parse("{1.5}").to_a.should == [BigDecimal.new('1.5')]
73
+ @m::PGDecimalArray.parse('{2.5,3.5}').to_a.should == [BigDecimal.new('2.5'), BigDecimal.new('3.5')]
74
+ @m::PGDecimalArray.parse('{3.5,4.5,5.5}').to_a.should == [BigDecimal.new('3.5'), BigDecimal.new('4.5'), BigDecimal.new('5.5')]
75
+ end
76
+
77
+ it "should parse multiple dimensional decimal arrays" do
78
+ @m::PGDecimalArray.parse("{{}}").to_a.should == [[]]
79
+ @m::PGDecimalArray.parse("{{1.5}}").to_a.should == [[BigDecimal.new('1.5')]]
80
+ @m::PGDecimalArray.parse('{{2.5},{3.5}}').to_a.should == [[BigDecimal.new('2.5')], [BigDecimal.new('3.5')]]
81
+ @m::PGDecimalArray.parse('{{{1.5,2.5},{3.5,4.5}},{{5.5,6.5},{7.5,8.5}}}').to_a.should == [[[BigDecimal.new('1.5'), BigDecimal.new('2.5')], [BigDecimal.new('3.5'), BigDecimal.new('4.5')]], [[BigDecimal.new('5.5'), BigDecimal.new('6.5')], [BigDecimal.new('7.5'), BigDecimal.new('8.5')]]]
82
+ end
83
+
84
+ it "should parse decimal values with arbitrary precision" do
85
+ @m::PGDecimalArray.parse("{1.000000000000000000005}").to_a.should == [BigDecimal.new('1.000000000000000000005')]
86
+ @m::PGDecimalArray.parse("{{1.000000000000000000005,2.000000000000000000005},{3.000000000000000000005,4.000000000000000000005}}").to_a.should == [[BigDecimal.new('1.000000000000000000005'), BigDecimal.new('2.000000000000000000005')], [BigDecimal.new('3.000000000000000000005'), BigDecimal.new('4.000000000000000000005')]]
87
+ end
88
+
89
+ it "should parse integers in decimal arrays as BigDecimals" do
90
+ @m::PGDecimalArray.parse("{1}").to_a.first.should be_a_kind_of(BigDecimal)
91
+ @m::PGDecimalArray.parse("{1}").to_a.should == [BigDecimal.new('1')]
92
+ @m::PGDecimalArray.parse('{{{1,2},{3,4}},{{5,6},{7,8}}}').to_a.should == [[[BigDecimal.new('1'), BigDecimal.new('2')], [BigDecimal.new('3'), BigDecimal.new('4')]], [[BigDecimal.new('5'), BigDecimal.new('6')], [BigDecimal.new('7'), BigDecimal.new('8')]]]
93
+ end
94
+
95
+ it "should parse arrays with NULL values" do
96
+ [@m::PGStringArray, @m::PGIntegerArray, @m::PGFloatArray, @m::PGDecimalArray].each do |c|
97
+ c.parse("{NULL}").should == [nil]
98
+ c.parse("{NULL,NULL}").should == [nil,nil]
99
+ c.parse("{{NULL,NULL},{NULL,NULL}}").should == [[nil,nil],[nil,nil]]
100
+ end
101
+ end
102
+
103
+ it 'should parse arrays with "NULL" values' do
104
+ @m::PGStringArray.parse('{NULL,"NULL",NULL}').to_a.should == [nil, "NULL", nil]
105
+ @m::PGStringArray.parse('{NULLA,"NULL",NULL}').to_a.should == ["NULLA", "NULL", nil]
106
+ end
107
+
108
+ it "should literalize arrays without types correctly" do
109
+ @db.literal(@m::PGArray.new([])).should == 'ARRAY[]'
110
+ @db.literal(@m::PGArray.new([1])).should == 'ARRAY[1]'
111
+ @db.literal(@m::PGArray.new([nil])).should == 'ARRAY[NULL]'
112
+ @db.literal(@m::PGArray.new([nil, 1])).should == 'ARRAY[NULL,1]'
113
+ @db.literal(@m::PGArray.new([1.0, 2.5])).should == 'ARRAY[1.0,2.5]'
114
+ @db.literal(@m::PGArray.new([BigDecimal.new('1'), BigDecimal.new('2.000000000000000000005')])).should == 'ARRAY[1.0,2.000000000000000000005]'
115
+ @db.literal(@m::PGArray.new([nil, "NULL"])).should == "ARRAY[NULL,'NULL']"
116
+ @db.literal(@m::PGArray.new([nil, "{},[]'\""])).should == "ARRAY[NULL,'{},[]''\"']"
117
+ end
118
+
119
+ it "should literalize multidimensional arrays correctly" do
120
+ @db.literal(@m::PGArray.new([[]])).should == 'ARRAY[[]]'
121
+ @db.literal(@m::PGArray.new([[1, 2]])).should == 'ARRAY[[1,2]]'
122
+ @db.literal(@m::PGArray.new([[3], [5]])).should == 'ARRAY[[3],[5]]'
123
+ @db.literal(@m::PGArray.new([[[1.0]], [[2.5]]])).should == 'ARRAY[[[1.0]],[[2.5]]]'
124
+ @db.literal(@m::PGArray.new([[[["NULL"]]]])).should == "ARRAY[[[['NULL']]]]"
125
+ @db.literal(@m::PGArray.new([["a", "b"], ["{},[]'\"", nil]])).should == "ARRAY[['a','b'],['{},[]''\"',NULL]]"
126
+ end
127
+
128
+ it "should literalize with types correctly" do
129
+ @db.literal(@m::PGArray.new([1], :int4)).should == 'ARRAY[1]::int4[]'
130
+ @db.literal(@m::PGArray.new([nil], :text)).should == 'ARRAY[NULL]::text[]'
131
+ @db.literal(@m::PGArray.new([nil, 1], :int8)).should == 'ARRAY[NULL,1]::int8[]'
132
+ @db.literal(@m::PGArray.new([1.0, 2.5], :real)).should == 'ARRAY[1.0,2.5]::real[]'
133
+ @db.literal(@m::PGArray.new([BigDecimal.new('1'), BigDecimal.new('2.000000000000000000005')], :decimal)).should == 'ARRAY[1.0,2.000000000000000000005]::decimal[]'
134
+ @db.literal(@m::PGArray.new([nil, "NULL"], :varchar)).should == "ARRAY[NULL,'NULL']::varchar[]"
135
+ @db.literal(@m::PGArray.new([nil, "{},[]'\""], :"varchar(255)")).should == "ARRAY[NULL,'{},[]''\"']::varchar(255)[]"
136
+ end
137
+
138
+ it "should have reasonable default types" do
139
+ @db.literal(@m::PGArray.new([])).should == 'ARRAY[]'
140
+ @db.literal(@m::PGIntegerArray.new([])).should == 'ARRAY[]::int4[]'
141
+ @db.literal(@m::PGFloatArray.new([])).should == 'ARRAY[]::double precision[]'
142
+ @db.literal(@m::PGStringArray.new([])).should == 'ARRAY[]::text[]'
143
+ @db.literal(@m::PGDecimalArray.new([])).should == 'ARRAY[]::decimal[]'
144
+ end
145
+
146
+ it "should use varchar type for char arrays without length" do
147
+ @db.literal(@m::PGStringArray.new([], :char)).should == 'ARRAY[]::varchar[]'
148
+ @db.literal(@m::PGStringArray.new([], 'char')).should == 'ARRAY[]::varchar[]'
149
+ end
150
+
151
+ it "should use given type for char arrays with length" do
152
+ @db.literal(@m::PGStringArray.new([], :'char(2)')).should == 'ARRAY[]::char(2)[]'
153
+ @db.literal(@m::PGStringArray.new([], 'char(1)')).should == 'ARRAY[]::char(1)[]'
154
+ end
155
+
156
+ it "should have Array#pg_array method for easy PGArray creation" do
157
+ @db.literal([1].pg_array).should == 'ARRAY[1]'
158
+ @db.literal([1, 2].pg_array(:int4)).should == 'ARRAY[1,2]::int4[]'
159
+ @db.literal([[[1], [2]], [[3], [4]]].pg_array(:real)).should == 'ARRAY[[[1],[2]],[[3],[4]]]::real[]'
160
+ end
161
+
162
+ it "should support using arrays as bound variables" do
163
+ @db.extend Sequel::Postgres::PGArray::DatabaseMethods
164
+ @db.bound_variable_arg(1, nil).should == 1
165
+ @db.bound_variable_arg([1,2].pg_array, nil).should == '{1,2}'
166
+ @db.bound_variable_arg([1,2], nil).should == '{1,2}'
167
+ @db.bound_variable_arg([[1,2]], nil).should == '{{1,2}}'
168
+ @db.bound_variable_arg([1.0,2.0], nil).should == '{1.0,2.0}'
169
+ @db.bound_variable_arg(["\\ \"", 'NULL', nil], nil).should == '{"\\\\ \\"","NULL",NULL}'
170
+ end
171
+
172
+ it "should parse array types from the schema correctly" do
173
+ @db.extend Sequel::Postgres::PGArray::DatabaseMethods
174
+ @db.fetch = [{:name=>'id', :db_type=>'integer'}, {:name=>'i', :db_type=>'integer[]'}, {:name=>'f', :db_type=>'real[]'}, {:name=>'d', :db_type=>'numeric[]'}, {:name=>'t', :db_type=>'text[]'}]
175
+ @db.schema(:items).map{|e| e[1][:type]}.should == [:integer, :integer_array, :float_array, :decimal_array, :string_array]
176
+ end
177
+
178
+ it "should support typecasting of the various array types" do
179
+ @db.extend Sequel::Postgres::PGArray::DatabaseMethods
180
+ a = [1, 2]
181
+ o = a.pg_array
182
+ %w[integer float decimal string].each do |x|
183
+ @db.typecast_value(:"#{x}_array", o).should equal(o)
184
+ @db.typecast_value(:"#{x}_array", a).should == a
185
+ @db.typecast_value(:"#{x}_array", a).should be_a_kind_of(eval("Sequel::Postgres::PG#{x.capitalize}Array"))
186
+ @db.typecast_value(:"#{x}_array", '{}').should == []
187
+ @db.typecast_value(:"#{x}_array", '{}').should be_a_kind_of(eval("Sequel::Postgres::PG#{x.capitalize}Array"))
188
+ end
189
+ @db.typecast_value(:integer_array, '{1}').should == [1]
190
+ @db.typecast_value(:float_array, '{1}').should == [1.0]
191
+ @db.typecast_value(:decimal_array, '{1}').should == [BigDecimal.new('1')]
192
+ @db.typecast_value(:string_array, '{1}').should == ['1']
193
+ proc{@db.typecast_value(:integer_array, {})}.should raise_error(Sequel::InvalidValue)
194
+ end
195
+ end
196
+ end