sequel 3.33.0 → 3.34.0

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.
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