viking-sequel 3.10.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 (237) hide show
  1. data/CHANGELOG +3134 -0
  2. data/COPYING +19 -0
  3. data/README.rdoc +723 -0
  4. data/Rakefile +193 -0
  5. data/bin/sequel +196 -0
  6. data/doc/advanced_associations.rdoc +644 -0
  7. data/doc/cheat_sheet.rdoc +218 -0
  8. data/doc/dataset_basics.rdoc +106 -0
  9. data/doc/dataset_filtering.rdoc +158 -0
  10. data/doc/opening_databases.rdoc +296 -0
  11. data/doc/prepared_statements.rdoc +104 -0
  12. data/doc/reflection.rdoc +84 -0
  13. data/doc/release_notes/1.0.txt +38 -0
  14. data/doc/release_notes/1.1.txt +143 -0
  15. data/doc/release_notes/1.3.txt +101 -0
  16. data/doc/release_notes/1.4.0.txt +53 -0
  17. data/doc/release_notes/1.5.0.txt +155 -0
  18. data/doc/release_notes/2.0.0.txt +298 -0
  19. data/doc/release_notes/2.1.0.txt +271 -0
  20. data/doc/release_notes/2.10.0.txt +328 -0
  21. data/doc/release_notes/2.11.0.txt +215 -0
  22. data/doc/release_notes/2.12.0.txt +534 -0
  23. data/doc/release_notes/2.2.0.txt +253 -0
  24. data/doc/release_notes/2.3.0.txt +88 -0
  25. data/doc/release_notes/2.4.0.txt +106 -0
  26. data/doc/release_notes/2.5.0.txt +137 -0
  27. data/doc/release_notes/2.6.0.txt +157 -0
  28. data/doc/release_notes/2.7.0.txt +166 -0
  29. data/doc/release_notes/2.8.0.txt +171 -0
  30. data/doc/release_notes/2.9.0.txt +97 -0
  31. data/doc/release_notes/3.0.0.txt +221 -0
  32. data/doc/release_notes/3.1.0.txt +406 -0
  33. data/doc/release_notes/3.10.0.txt +286 -0
  34. data/doc/release_notes/3.2.0.txt +268 -0
  35. data/doc/release_notes/3.3.0.txt +192 -0
  36. data/doc/release_notes/3.4.0.txt +325 -0
  37. data/doc/release_notes/3.5.0.txt +510 -0
  38. data/doc/release_notes/3.6.0.txt +366 -0
  39. data/doc/release_notes/3.7.0.txt +179 -0
  40. data/doc/release_notes/3.8.0.txt +151 -0
  41. data/doc/release_notes/3.9.0.txt +233 -0
  42. data/doc/schema.rdoc +36 -0
  43. data/doc/sharding.rdoc +113 -0
  44. data/doc/virtual_rows.rdoc +205 -0
  45. data/lib/sequel.rb +1 -0
  46. data/lib/sequel/adapters/ado.rb +90 -0
  47. data/lib/sequel/adapters/ado/mssql.rb +30 -0
  48. data/lib/sequel/adapters/amalgalite.rb +176 -0
  49. data/lib/sequel/adapters/db2.rb +139 -0
  50. data/lib/sequel/adapters/dbi.rb +113 -0
  51. data/lib/sequel/adapters/do.rb +188 -0
  52. data/lib/sequel/adapters/do/mysql.rb +49 -0
  53. data/lib/sequel/adapters/do/postgres.rb +91 -0
  54. data/lib/sequel/adapters/do/sqlite.rb +40 -0
  55. data/lib/sequel/adapters/firebird.rb +283 -0
  56. data/lib/sequel/adapters/informix.rb +77 -0
  57. data/lib/sequel/adapters/jdbc.rb +587 -0
  58. data/lib/sequel/adapters/jdbc/as400.rb +58 -0
  59. data/lib/sequel/adapters/jdbc/h2.rb +133 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +57 -0
  61. data/lib/sequel/adapters/jdbc/mysql.rb +78 -0
  62. data/lib/sequel/adapters/jdbc/oracle.rb +50 -0
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +108 -0
  64. data/lib/sequel/adapters/jdbc/sqlite.rb +55 -0
  65. data/lib/sequel/adapters/mysql.rb +421 -0
  66. data/lib/sequel/adapters/odbc.rb +143 -0
  67. data/lib/sequel/adapters/odbc/mssql.rb +42 -0
  68. data/lib/sequel/adapters/openbase.rb +64 -0
  69. data/lib/sequel/adapters/oracle.rb +131 -0
  70. data/lib/sequel/adapters/postgres.rb +504 -0
  71. data/lib/sequel/adapters/shared/mssql.rb +490 -0
  72. data/lib/sequel/adapters/shared/mysql.rb +498 -0
  73. data/lib/sequel/adapters/shared/oracle.rb +195 -0
  74. data/lib/sequel/adapters/shared/postgres.rb +830 -0
  75. data/lib/sequel/adapters/shared/progress.rb +44 -0
  76. data/lib/sequel/adapters/shared/sqlite.rb +389 -0
  77. data/lib/sequel/adapters/sqlite.rb +224 -0
  78. data/lib/sequel/adapters/utils/stored_procedures.rb +84 -0
  79. data/lib/sequel/connection_pool.rb +99 -0
  80. data/lib/sequel/connection_pool/sharded_single.rb +84 -0
  81. data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
  82. data/lib/sequel/connection_pool/single.rb +29 -0
  83. data/lib/sequel/connection_pool/threaded.rb +150 -0
  84. data/lib/sequel/core.rb +293 -0
  85. data/lib/sequel/core_sql.rb +241 -0
  86. data/lib/sequel/database.rb +1079 -0
  87. data/lib/sequel/database/schema_generator.rb +327 -0
  88. data/lib/sequel/database/schema_methods.rb +203 -0
  89. data/lib/sequel/database/schema_sql.rb +320 -0
  90. data/lib/sequel/dataset.rb +32 -0
  91. data/lib/sequel/dataset/actions.rb +441 -0
  92. data/lib/sequel/dataset/features.rb +86 -0
  93. data/lib/sequel/dataset/graph.rb +254 -0
  94. data/lib/sequel/dataset/misc.rb +119 -0
  95. data/lib/sequel/dataset/mutation.rb +64 -0
  96. data/lib/sequel/dataset/prepared_statements.rb +227 -0
  97. data/lib/sequel/dataset/query.rb +709 -0
  98. data/lib/sequel/dataset/sql.rb +996 -0
  99. data/lib/sequel/exceptions.rb +51 -0
  100. data/lib/sequel/extensions/blank.rb +43 -0
  101. data/lib/sequel/extensions/inflector.rb +242 -0
  102. data/lib/sequel/extensions/looser_typecasting.rb +21 -0
  103. data/lib/sequel/extensions/migration.rb +239 -0
  104. data/lib/sequel/extensions/named_timezones.rb +61 -0
  105. data/lib/sequel/extensions/pagination.rb +100 -0
  106. data/lib/sequel/extensions/pretty_table.rb +82 -0
  107. data/lib/sequel/extensions/query.rb +52 -0
  108. data/lib/sequel/extensions/schema_dumper.rb +271 -0
  109. data/lib/sequel/extensions/sql_expr.rb +122 -0
  110. data/lib/sequel/extensions/string_date_time.rb +46 -0
  111. data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
  112. data/lib/sequel/metaprogramming.rb +9 -0
  113. data/lib/sequel/model.rb +120 -0
  114. data/lib/sequel/model/associations.rb +1514 -0
  115. data/lib/sequel/model/base.rb +1069 -0
  116. data/lib/sequel/model/default_inflections.rb +45 -0
  117. data/lib/sequel/model/errors.rb +39 -0
  118. data/lib/sequel/model/exceptions.rb +21 -0
  119. data/lib/sequel/model/inflections.rb +162 -0
  120. data/lib/sequel/model/plugins.rb +70 -0
  121. data/lib/sequel/plugins/active_model.rb +59 -0
  122. data/lib/sequel/plugins/association_dependencies.rb +103 -0
  123. data/lib/sequel/plugins/association_proxies.rb +41 -0
  124. data/lib/sequel/plugins/boolean_readers.rb +53 -0
  125. data/lib/sequel/plugins/caching.rb +141 -0
  126. data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
  127. data/lib/sequel/plugins/composition.rb +138 -0
  128. data/lib/sequel/plugins/force_encoding.rb +72 -0
  129. data/lib/sequel/plugins/hook_class_methods.rb +126 -0
  130. data/lib/sequel/plugins/identity_map.rb +116 -0
  131. data/lib/sequel/plugins/instance_filters.rb +98 -0
  132. data/lib/sequel/plugins/instance_hooks.rb +57 -0
  133. data/lib/sequel/plugins/lazy_attributes.rb +77 -0
  134. data/lib/sequel/plugins/many_through_many.rb +208 -0
  135. data/lib/sequel/plugins/nested_attributes.rb +206 -0
  136. data/lib/sequel/plugins/optimistic_locking.rb +81 -0
  137. data/lib/sequel/plugins/rcte_tree.rb +281 -0
  138. data/lib/sequel/plugins/schema.rb +66 -0
  139. data/lib/sequel/plugins/serialization.rb +166 -0
  140. data/lib/sequel/plugins/single_table_inheritance.rb +74 -0
  141. data/lib/sequel/plugins/subclasses.rb +45 -0
  142. data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
  143. data/lib/sequel/plugins/timestamps.rb +87 -0
  144. data/lib/sequel/plugins/touch.rb +118 -0
  145. data/lib/sequel/plugins/typecast_on_load.rb +72 -0
  146. data/lib/sequel/plugins/validation_class_methods.rb +405 -0
  147. data/lib/sequel/plugins/validation_helpers.rb +223 -0
  148. data/lib/sequel/sql.rb +1020 -0
  149. data/lib/sequel/timezones.rb +161 -0
  150. data/lib/sequel/version.rb +12 -0
  151. data/lib/sequel_core.rb +1 -0
  152. data/lib/sequel_model.rb +1 -0
  153. data/spec/adapters/firebird_spec.rb +407 -0
  154. data/spec/adapters/informix_spec.rb +97 -0
  155. data/spec/adapters/mssql_spec.rb +403 -0
  156. data/spec/adapters/mysql_spec.rb +1019 -0
  157. data/spec/adapters/oracle_spec.rb +286 -0
  158. data/spec/adapters/postgres_spec.rb +969 -0
  159. data/spec/adapters/spec_helper.rb +51 -0
  160. data/spec/adapters/sqlite_spec.rb +432 -0
  161. data/spec/core/connection_pool_spec.rb +808 -0
  162. data/spec/core/core_sql_spec.rb +417 -0
  163. data/spec/core/database_spec.rb +1662 -0
  164. data/spec/core/dataset_spec.rb +3827 -0
  165. data/spec/core/expression_filters_spec.rb +595 -0
  166. data/spec/core/object_graph_spec.rb +296 -0
  167. data/spec/core/schema_generator_spec.rb +159 -0
  168. data/spec/core/schema_spec.rb +830 -0
  169. data/spec/core/spec_helper.rb +56 -0
  170. data/spec/core/version_spec.rb +7 -0
  171. data/spec/extensions/active_model_spec.rb +76 -0
  172. data/spec/extensions/association_dependencies_spec.rb +127 -0
  173. data/spec/extensions/association_proxies_spec.rb +50 -0
  174. data/spec/extensions/blank_spec.rb +67 -0
  175. data/spec/extensions/boolean_readers_spec.rb +92 -0
  176. data/spec/extensions/caching_spec.rb +250 -0
  177. data/spec/extensions/class_table_inheritance_spec.rb +252 -0
  178. data/spec/extensions/composition_spec.rb +194 -0
  179. data/spec/extensions/force_encoding_spec.rb +117 -0
  180. data/spec/extensions/hook_class_methods_spec.rb +470 -0
  181. data/spec/extensions/identity_map_spec.rb +202 -0
  182. data/spec/extensions/inflector_spec.rb +181 -0
  183. data/spec/extensions/instance_filters_spec.rb +55 -0
  184. data/spec/extensions/instance_hooks_spec.rb +133 -0
  185. data/spec/extensions/lazy_attributes_spec.rb +153 -0
  186. data/spec/extensions/looser_typecasting_spec.rb +39 -0
  187. data/spec/extensions/many_through_many_spec.rb +884 -0
  188. data/spec/extensions/migration_spec.rb +332 -0
  189. data/spec/extensions/named_timezones_spec.rb +72 -0
  190. data/spec/extensions/nested_attributes_spec.rb +396 -0
  191. data/spec/extensions/optimistic_locking_spec.rb +100 -0
  192. data/spec/extensions/pagination_spec.rb +99 -0
  193. data/spec/extensions/pretty_table_spec.rb +91 -0
  194. data/spec/extensions/query_spec.rb +85 -0
  195. data/spec/extensions/rcte_tree_spec.rb +205 -0
  196. data/spec/extensions/schema_dumper_spec.rb +357 -0
  197. data/spec/extensions/schema_spec.rb +127 -0
  198. data/spec/extensions/serialization_spec.rb +209 -0
  199. data/spec/extensions/single_table_inheritance_spec.rb +96 -0
  200. data/spec/extensions/spec_helper.rb +91 -0
  201. data/spec/extensions/sql_expr_spec.rb +89 -0
  202. data/spec/extensions/string_date_time_spec.rb +93 -0
  203. data/spec/extensions/subclasses_spec.rb +52 -0
  204. data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
  205. data/spec/extensions/thread_local_timezones_spec.rb +45 -0
  206. data/spec/extensions/timestamps_spec.rb +150 -0
  207. data/spec/extensions/touch_spec.rb +155 -0
  208. data/spec/extensions/typecast_on_load_spec.rb +69 -0
  209. data/spec/extensions/validation_class_methods_spec.rb +984 -0
  210. data/spec/extensions/validation_helpers_spec.rb +438 -0
  211. data/spec/integration/associations_test.rb +281 -0
  212. data/spec/integration/database_test.rb +26 -0
  213. data/spec/integration/dataset_test.rb +963 -0
  214. data/spec/integration/eager_loader_test.rb +734 -0
  215. data/spec/integration/model_test.rb +130 -0
  216. data/spec/integration/plugin_test.rb +814 -0
  217. data/spec/integration/prepared_statement_test.rb +213 -0
  218. data/spec/integration/schema_test.rb +361 -0
  219. data/spec/integration/spec_helper.rb +73 -0
  220. data/spec/integration/timezone_test.rb +55 -0
  221. data/spec/integration/transaction_test.rb +122 -0
  222. data/spec/integration/type_test.rb +96 -0
  223. data/spec/model/association_reflection_spec.rb +175 -0
  224. data/spec/model/associations_spec.rb +2633 -0
  225. data/spec/model/base_spec.rb +418 -0
  226. data/spec/model/dataset_methods_spec.rb +78 -0
  227. data/spec/model/eager_loading_spec.rb +1391 -0
  228. data/spec/model/hooks_spec.rb +240 -0
  229. data/spec/model/inflector_spec.rb +26 -0
  230. data/spec/model/model_spec.rb +593 -0
  231. data/spec/model/plugins_spec.rb +236 -0
  232. data/spec/model/record_spec.rb +1500 -0
  233. data/spec/model/spec_helper.rb +97 -0
  234. data/spec/model/validations_spec.rb +153 -0
  235. data/spec/rcov.opts +6 -0
  236. data/spec/spec_config.rb.example +10 -0
  237. metadata +346 -0
@@ -0,0 +1,97 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper.rb')
2
+
3
+ unless defined?(INFORMIX_DB)
4
+ INFORMIX_DB = Sequel.connect('informix://localhost/mydb')
5
+ end
6
+ INTEGRATION_DB = INFORMIX_DB unless defined?(INTEGRATION_DB)
7
+
8
+ if INFORMIX_DB.table_exists?(:test)
9
+ INFORMIX_DB.drop_table :test
10
+ end
11
+ INFORMIX_DB.create_table :test do
12
+ text :name
13
+ integer :value
14
+
15
+ index :value
16
+ end
17
+
18
+ context "A Informix database" do
19
+ specify "should provide disconnect functionality" do
20
+ INFORMIX_DB.execute("select user from dual")
21
+ INFORMIX_DB.pool.size.should == 1
22
+ INFORMIX_DB.disconnect
23
+ INFORMIX_DB.pool.size.should == 0
24
+ end
25
+ end
26
+
27
+ context "A Informix dataset" do
28
+ before do
29
+ @d = INFORMIX_DB[:test]
30
+ @d.delete # remove all records
31
+ end
32
+
33
+ specify "should return the correct record count" do
34
+ @d.count.should == 0
35
+ @d << {:name => 'abc', :value => 123}
36
+ @d << {:name => 'abc', :value => 456}
37
+ @d << {:name => 'def', :value => 789}
38
+ @d.count.should == 3
39
+ end
40
+
41
+ specify "should return the correct records" do
42
+ @d.to_a.should == []
43
+ @d << {:name => 'abc', :value => 123}
44
+ @d << {:name => 'abc', :value => 456}
45
+ @d << {:name => 'def', :value => 789}
46
+
47
+ @d.order(:value).to_a.should == [
48
+ {:name => 'abc', :value => 123},
49
+ {:name => 'abc', :value => 456},
50
+ {:name => 'def', :value => 789}
51
+ ]
52
+ end
53
+
54
+ specify "should update records correctly" do
55
+ @d << {:name => 'abc', :value => 123}
56
+ @d << {:name => 'abc', :value => 456}
57
+ @d << {:name => 'def', :value => 789}
58
+ @d.filter(:name => 'abc').update(:value => 530)
59
+
60
+ # the third record should stay the same
61
+ # floating-point precision bullshit
62
+ @d[:name => 'def'][:value].should == 789
63
+ @d.filter(:value => 530).count.should == 2
64
+ end
65
+
66
+ specify "should delete records correctly" do
67
+ @d << {:name => 'abc', :value => 123}
68
+ @d << {:name => 'abc', :value => 456}
69
+ @d << {:name => 'def', :value => 789}
70
+ @d.filter(:name => 'abc').delete
71
+
72
+ @d.count.should == 1
73
+ @d.first[:name].should == 'def'
74
+ end
75
+
76
+ specify "should be able to literalize booleans" do
77
+ proc {@d.literal(true)}.should_not raise_error
78
+ proc {@d.literal(false)}.should_not raise_error
79
+ end
80
+
81
+ specify "should support transactions" do
82
+ INFORMIX_DB.transaction do
83
+ @d << {:name => 'abc', :value => 1}
84
+ end
85
+
86
+ @d.count.should == 1
87
+ end
88
+
89
+ specify "should support #first and #last" do
90
+ @d << {:name => 'abc', :value => 123}
91
+ @d << {:name => 'abc', :value => 456}
92
+ @d << {:name => 'def', :value => 789}
93
+
94
+ @d.order(:value).first.should == {:name => 'abc', :value => 123}
95
+ @d.order(:value).last.should == {:name => 'def', :value => 789}
96
+ end
97
+ end
@@ -0,0 +1,403 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper.rb')
2
+
3
+ require ENV['SEQUEL_MSSQL_SPEC_REQUIRE'] if ENV['SEQUEL_MSSQL_SPEC_REQUIRE']
4
+
5
+ unless defined?(MSSQL_DB)
6
+ MSSQL_URL = 'jdbc:sqlserver://localhost;integratedSecurity=true;database=sandbox' unless defined? MSSQL_URL
7
+ MSSQL_DB = Sequel.connect(ENV['SEQUEL_MSSQL_SPEC_DB']||MSSQL_URL)
8
+ end
9
+ INTEGRATION_DB = MSSQL_DB unless defined?(INTEGRATION_DB)
10
+
11
+ def MSSQL_DB.sqls
12
+ (@sqls ||= [])
13
+ end
14
+ logger = Object.new
15
+ def logger.method_missing(m, msg)
16
+ MSSQL_DB.sqls << msg
17
+ end
18
+ MSSQL_DB.logger = logger
19
+
20
+ MSSQL_DB.create_table! :test do
21
+ text :name
22
+ integer :value, :index => true
23
+ end
24
+ MSSQL_DB.create_table! :test2 do
25
+ text :name
26
+ integer :value
27
+ end
28
+ MSSQL_DB.create_table! :test3 do
29
+ integer :value
30
+ timestamp :time
31
+ end
32
+ MSSQL_DB.create_table! :test4 do
33
+ varchar :name, :size => 20
34
+ varbinary :value
35
+ end
36
+
37
+ context "A MSSQL database" do
38
+ before do
39
+ @db = MSSQL_DB
40
+ end
41
+
42
+ cspecify "should be able to read fractional part of timestamp", :odbc do
43
+ rs = @db["select getutcdate() as full_date, cast(datepart(millisecond, getutcdate()) as int) as milliseconds"].first
44
+ rs[:milliseconds].should == rs[:full_date].usec/1000
45
+ end
46
+
47
+ cspecify "should be able to write fractional part of timestamp", :odbc do
48
+ t = Time.utc(2001, 12, 31, 23, 59, 59, 997000)
49
+ (t.usec/1000).should == @db["select cast(datepart(millisecond, ?) as int) as milliseconds", t].get
50
+ end
51
+
52
+ specify "should not raise an error when getting the server version" do
53
+ proc{@db.server_version}.should_not raise_error
54
+ proc{@db.dataset.server_version}.should_not raise_error
55
+ end
56
+
57
+ specify "should work with NOLOCK" do
58
+ @db.transaction{@db[:test3].nolock.all.should == []}
59
+ end
60
+ end
61
+
62
+ context "MSSQL Dataset#join_table" do
63
+ specify "should emulate the USING clause with ON" do
64
+ MSSQL_DB[:items].join(:categories, [:id]).sql.should ==
65
+ 'SELECT * FROM ITEMS INNER JOIN CATEGORIES ON (CATEGORIES.ID = ITEMS.ID)'
66
+ MSSQL_DB[:items].join(:categories, [:id1, :id2]).sql.should ==
67
+ 'SELECT * FROM ITEMS INNER JOIN CATEGORIES ON ((CATEGORIES.ID1 = ITEMS.ID1) AND (CATEGORIES.ID2 = ITEMS.ID2))'
68
+ MSSQL_DB[:items___i].join(:categories___c, [:id]).sql.should ==
69
+ 'SELECT * FROM ITEMS AS I INNER JOIN CATEGORIES AS C ON (C.ID = I.ID)'
70
+ end
71
+ end
72
+
73
+ context "MSSQL Dataset#output" do
74
+ before do
75
+ @db = MSSQL_DB
76
+ @db.create_table!(:items){String :name; Integer :value}
77
+ @db.create_table!(:out){String :name; Integer :value}
78
+ @ds = @db[:items]
79
+ end
80
+ after do
81
+ @db.drop_table(:items)
82
+ @db.drop_table(:out)
83
+ end
84
+
85
+ specify "should format OUTPUT clauses without INTO for DELETE statements" do
86
+ @ds.output(nil, [:deleted__name, :deleted__value]).delete_sql.should =~
87
+ /DELETE FROM ITEMS OUTPUT DELETED.(NAME|VALUE), DELETED.(NAME|VALUE)/
88
+ @ds.output(nil, [:deleted.*]).delete_sql.should =~
89
+ /DELETE FROM ITEMS OUTPUT DELETED.*/
90
+ end
91
+
92
+ specify "should format OUTPUT clauses with INTO for DELETE statements" do
93
+ @ds.output(:out, [:deleted__name, :deleted__value]).delete_sql.should =~
94
+ /DELETE FROM ITEMS OUTPUT DELETED.(NAME|VALUE), DELETED.(NAME|VALUE) INTO OUT/
95
+ @ds.output(:out, {:name => :deleted__name, :value => :deleted__value}).delete_sql.should =~
96
+ /DELETE FROM ITEMS OUTPUT DELETED.(NAME|VALUE), DELETED.(NAME|VALUE) INTO OUT \((NAME|VALUE), (NAME|VALUE)\)/
97
+ end
98
+
99
+ specify "should format OUTPUT clauses without INTO for INSERT statements" do
100
+ @ds.output(nil, [:inserted__name, :inserted__value]).insert_sql(:name => "name", :value => 1).should =~
101
+ /INSERT INTO ITEMS \((NAME|VALUE), (NAME|VALUE)\) OUTPUT INSERTED.(NAME|VALUE), INSERTED.(NAME|VALUE) VALUES \((N'name'|1), (N'name'|1)\)/
102
+ @ds.output(nil, [:inserted.*]).insert_sql(:name => "name", :value => 1).should =~
103
+ /INSERT INTO ITEMS \((NAME|VALUE), (NAME|VALUE)\) OUTPUT INSERTED.* VALUES \((N'name'|1), (N'name'|1)\)/
104
+ end
105
+
106
+ specify "should format OUTPUT clauses with INTO for INSERT statements" do
107
+ @ds.output(:out, [:inserted__name, :inserted__value]).insert_sql(:name => "name", :value => 1).should =~
108
+ /INSERT INTO ITEMS \((NAME|VALUE), (NAME|VALUE)\) OUTPUT INSERTED.(NAME|VALUE), INSERTED.(NAME|VALUE) INTO OUT VALUES \((N'name'|1), (N'name'|1)\)/
109
+ @ds.output(:out, {:name => :inserted__name, :value => :inserted__value}).insert_sql(:name => "name", :value => 1).should =~
110
+ /INSERT INTO ITEMS \((NAME|VALUE), (NAME|VALUE)\) OUTPUT INSERTED.(NAME|VALUE), INSERTED.(NAME|VALUE) INTO OUT \((NAME|VALUE), (NAME|VALUE)\) VALUES \((N'name'|1), (N'name'|1)\)/
111
+ end
112
+
113
+ specify "should format OUTPUT clauses without INTO for UPDATE statements" do
114
+ @ds.output(nil, [:inserted__name, :deleted__value]).update_sql(:value => 2).should =~
115
+ /UPDATE ITEMS SET VALUE = 2 OUTPUT (INSERTED.NAME|DELETED.VALUE), (INSERTED.NAME|DELETED.VALUE)/
116
+ @ds.output(nil, [:inserted.*]).update_sql(:value => 2).should =~
117
+ /UPDATE ITEMS SET VALUE = 2 OUTPUT INSERTED.*/
118
+ end
119
+
120
+ specify "should format OUTPUT clauses with INTO for UPDATE statements" do
121
+ @ds.output(:out, [:inserted__name, :deleted__value]).update_sql(:value => 2).should =~
122
+ /UPDATE ITEMS SET VALUE = 2 OUTPUT (INSERTED.NAME|DELETED.VALUE), (INSERTED.NAME|DELETED.VALUE) INTO OUT/
123
+ @ds.output(:out, {:name => :inserted__name, :value => :deleted__value}).update_sql(:value => 2).should =~
124
+ /UPDATE ITEMS SET VALUE = 2 OUTPUT (INSERTED.NAME|DELETED.VALUE), (INSERTED.NAME|DELETED.VALUE) INTO OUT \((NAME|VALUE), (NAME|VALUE)\)/
125
+ end
126
+
127
+ specify "should execute OUTPUT clauses in DELETE statements" do
128
+ @ds.insert(:name => "name", :value => 1)
129
+ @ds.output(:out, [:deleted__name, :deleted__value]).delete
130
+ @db[:out].all.should == [{:name => "name", :value => 1}]
131
+ @ds.insert(:name => "name", :value => 2)
132
+ @ds.output(:out, {:name => :deleted__name, :value => :deleted__value}).delete
133
+ @db[:out].all.should == [{:name => "name", :value => 1}, {:name => "name", :value => 2}]
134
+ end
135
+
136
+ specify "should execute OUTPUT clauses in INSERT statements" do
137
+ @ds.output(:out, [:inserted__name, :inserted__value]).insert(:name => "name", :value => 1)
138
+ @db[:out].all.should == [{:name => "name", :value => 1}]
139
+ @ds.output(:out, {:name => :inserted__name, :value => :inserted__value}).insert(:name => "name", :value => 2)
140
+ @db[:out].all.should == [{:name => "name", :value => 1}, {:name => "name", :value => 2}]
141
+ end
142
+
143
+ specify "should execute OUTPUT clauses in UPDATE statements" do
144
+ @ds.insert(:name => "name", :value => 1)
145
+ @ds.output(:out, [:inserted__name, :deleted__value]).update(:value => 2)
146
+ @db[:out].all.should == [{:name => "name", :value => 1}]
147
+ @ds.output(:out, {:name => :inserted__name, :value => :deleted__value}).update(:value => 3)
148
+ @db[:out].all.should == [{:name => "name", :value => 1}, {:name => "name", :value => 2}]
149
+ end
150
+ end
151
+
152
+ context "MSSQL dataset" do
153
+ before do
154
+ @db = MSSQL_DB
155
+ @ds = MSSQL_DB[:t]
156
+ end
157
+
158
+ context "using #with and #with_recursive" do
159
+ before do
160
+ @ds1 = @ds.with(:t, @db[:x])
161
+ @ds2 = @ds.with_recursive(:t, @db[:x], @db[:t])
162
+ end
163
+
164
+ specify "should prepend UPDATE statements with WITH clause" do
165
+ @ds1.update_sql(:x => :y).should == 'WITH T AS (SELECT * FROM X) UPDATE T SET X = Y'
166
+ @ds2.update_sql(:x => :y).should == 'WITH T AS (SELECT * FROM X UNION ALL SELECT * FROM T) UPDATE T SET X = Y'
167
+ end
168
+
169
+ specify "should prepend DELETE statements with WITH clause" do
170
+ @ds1.filter(:y => 1).delete_sql.should == 'WITH T AS (SELECT * FROM X) DELETE FROM T WHERE (Y = 1)'
171
+ @ds2.filter(:y => 1).delete_sql.should == 'WITH T AS (SELECT * FROM X UNION ALL SELECT * FROM T) DELETE FROM T WHERE (Y = 1)'
172
+ end
173
+
174
+ specify "should prepend INSERT statements with WITH clause" do
175
+ @ds1.insert_sql(@db[:t]).should == 'WITH T AS (SELECT * FROM X) INSERT INTO T SELECT * FROM T'
176
+ @ds2.insert_sql(@db[:t]).should == 'WITH T AS (SELECT * FROM X UNION ALL SELECT * FROM T) INSERT INTO T SELECT * FROM T'
177
+ end
178
+
179
+ context "on #import" do
180
+ before do
181
+ @db = @db.clone
182
+ class << @db
183
+ attr_reader :import_sqls
184
+
185
+ def execute(sql, opts={})
186
+ @import_sqls ||= []
187
+ @import_sqls << sql
188
+ end
189
+ alias execute_dui execute
190
+
191
+ def transaction(opts={})
192
+ @import_sqls ||= []
193
+ @import_sqls << 'BEGIN'
194
+ yield
195
+ @import_sqls << 'COMMIT'
196
+ end
197
+ end
198
+ end
199
+
200
+ specify "should prepend INSERT statements with WITH clause" do
201
+ @db[:items].with(:items, @db[:inventory].group(:type)).import([:x, :y], [[1, 2], [3, 4], [5, 6]], :slice => 2)
202
+ @db.import_sqls.should == [
203
+ 'BEGIN',
204
+ "WITH ITEMS AS (SELECT * FROM INVENTORY GROUP BY TYPE) INSERT INTO ITEMS (X, Y) SELECT 1, 2 UNION ALL SELECT 3, 4",
205
+ 'COMMIT',
206
+ 'BEGIN',
207
+ "WITH ITEMS AS (SELECT * FROM INVENTORY GROUP BY TYPE) INSERT INTO ITEMS (X, Y) SELECT 5, 6",
208
+ 'COMMIT'
209
+ ]
210
+ end
211
+ end
212
+ end
213
+ end
214
+
215
+ context "MSSQL joined datasets" do
216
+ before do
217
+ @db = MSSQL_DB
218
+ end
219
+
220
+ specify "should format DELETE statements" do
221
+ @db[:t1].inner_join(:t2, :t1__pk => :t2__pk).delete_sql.should ==
222
+ "DELETE FROM T1 FROM T1 INNER JOIN T2 ON (T1.PK = T2.PK)"
223
+ end
224
+
225
+ specify "should format UPDATE statements" do
226
+ @db[:t1].inner_join(:t2, :t1__pk => :t2__pk).update_sql(:pk => :t2__pk).should ==
227
+ "UPDATE T1 SET PK = T2.PK FROM T1 INNER JOIN T2 ON (T1.PK = T2.PK)"
228
+ end
229
+ end
230
+
231
+ describe "Offset support" do
232
+ before do
233
+ @db = MSSQL_DB
234
+ @db.create_table!(:i){Integer :id; Integer :parent_id}
235
+ @ds = @db[:i].order(:id)
236
+ @hs = []
237
+ @ds.row_proc = proc{|r| @hs << r.dup; r[:id] *= 2; r[:parent_id] *= 3; r}
238
+ @ds.import [:id, :parent_id], [[1,nil],[2,nil],[3,1],[4,1],[5,3],[6,5]]
239
+ end
240
+ after do
241
+ @db.drop_table(:i)
242
+ end
243
+
244
+ specify "should return correct rows" do
245
+ @ds.limit(2, 2).all.should == [{:id=>6, :parent_id=>3}, {:id=>8, :parent_id=>3}]
246
+ end
247
+
248
+ specify "should not include offset column in hashes passed to row_proc" do
249
+ @ds.limit(2, 2).all
250
+ @hs.should == [{:id=>3, :parent_id=>1}, {:id=>4, :parent_id=>1}]
251
+ end
252
+ end
253
+
254
+ describe "Common Table Expressions" do
255
+ before do
256
+ @db = MSSQL_DB
257
+ @db.create_table!(:i1){Integer :id; Integer :parent_id}
258
+ @db.create_table!(:i2){Integer :id; Integer :parent_id}
259
+ @ds = @db[:i1]
260
+ @ds2 = @db[:i2]
261
+ @ds.import [:id, :parent_id], [[1,nil],[2,nil],[3,1],[4,1],[5,3],[6,5]]
262
+ end
263
+ after do
264
+ @db.drop_table(:i1)
265
+ @db.drop_table(:i2)
266
+ end
267
+
268
+ specify "using #with should be able to update" do
269
+ @ds.insert(:id=>1)
270
+ @ds2.insert(:id=>2, :parent_id=>1)
271
+ @ds2.insert(:id=>3, :parent_id=>2)
272
+ @ds.with(:t, @ds2).filter(:id => @db[:t].select(:id)).update(:parent_id => @db[:t].filter(:id => :i1__id).select(:parent_id).limit(1))
273
+ @ds[:id => 1].should == {:id => 1, :parent_id => nil}
274
+ @ds[:id => 2].should == {:id => 2, :parent_id => 1}
275
+ @ds[:id => 3].should == {:id => 3, :parent_id => 2}
276
+ @ds[:id => 4].should == {:id => 4, :parent_id => 1}
277
+ end
278
+
279
+ specify "using #with_recursive should be able to update" do
280
+ ds = @ds.with_recursive(:t, @ds.filter(:parent_id=>1).or(:id => 1), @ds.join(:t, :i=>:parent_id).select(:i1__id, :i1__parent_id), :args=>[:i, :pi])
281
+ ds.filter(~{:id => @db[:t].select(:i)}).update(:parent_id => 1)
282
+ @ds[:id => 1].should == {:id => 1, :parent_id => nil}
283
+ @ds[:id => 2].should == {:id => 2, :parent_id => 1}
284
+ @ds[:id => 5].should == {:id => 5, :parent_id => 3}
285
+ end
286
+
287
+ specify "using #with should be able to insert" do
288
+ @ds2.insert(:id=>7)
289
+ @ds.with(:t, @ds2).insert(@db[:t])
290
+ @ds[:id => 7].should == {:id => 7, :parent_id => nil}
291
+ end
292
+
293
+ specify "using #with_recursive should be able to insert" do
294
+ ds = @ds2.with_recursive(:t, @ds.filter(:parent_id=>1), @ds.join(:t, :i=>:parent_id).select(:i1__id, :i1__parent_id), :args=>[:i, :pi])
295
+ ds.insert @db[:t]
296
+ @ds2.all.should == [{:id => 3, :parent_id => 1}, {:id => 4, :parent_id => 1}, {:id => 5, :parent_id => 3}, {:id => 6, :parent_id => 5}]
297
+ end
298
+
299
+ specify "using #with should be able to delete" do
300
+ @ds2.insert(:id=>6)
301
+ @ds2.insert(:id=>5)
302
+ @ds2.insert(:id=>4)
303
+ @ds.with(:t, @ds2).filter(:id => @db[:t].select(:id)).delete
304
+ @ds.all.should == [{:id => 1, :parent_id => nil}, {:id => 2, :parent_id => nil}, {:id => 3, :parent_id => 1}]
305
+ end
306
+
307
+ specify "using #with_recursive should be able to delete" do
308
+ @ds.insert(:id=>7, :parent_id=>2)
309
+ ds = @ds.with_recursive(:t, @ds.filter(:parent_id=>1), @ds.join(:t, :i=>:parent_id).select(:i1__id, :i1__parent_id), :args=>[:i, :pi])
310
+ ds.filter(:i1__id => @db[:t].select(:i)).delete
311
+ @ds.all.should == [{:id => 1, :parent_id => nil}, {:id => 2, :parent_id => nil}, {:id => 7, :parent_id => 2}]
312
+ end
313
+
314
+ specify "using #with should be able to import" do
315
+ @ds2.insert(:id=>7)
316
+ @ds.with(:t, @ds2).import [:id, :parent_id], @db[:t].select(:id, :parent_id)
317
+ @ds[:id => 7].should == {:id => 7, :parent_id => nil}
318
+ end
319
+
320
+ specify "using #with_recursive should be able to import" do
321
+ ds = @ds2.with_recursive(:t, @ds.filter(:parent_id=>1), @ds.join(:t, :i=>:parent_id).select(:i1__id, :i1__parent_id), :args=>[:i, :pi])
322
+ ds.import [:id, :parent_id], @db[:t].select(:i, :pi)
323
+ @ds2.all.should == [{:id => 3, :parent_id => 1}, {:id => 4, :parent_id => 1}, {:id => 5, :parent_id => 3}, {:id => 6, :parent_id => 5}]
324
+ end
325
+ end
326
+
327
+ context "MSSSQL::Dataset#insert" do
328
+ before do
329
+ @db = MSSQL_DB
330
+ @db.create_table!(:test5){primary_key :xid; Integer :value}
331
+ @db.sqls.clear
332
+ @ds = @db[:test5]
333
+ end
334
+ after do
335
+ @db.drop_table(:test5) rescue nil
336
+ end
337
+
338
+ specify "should have insert_select return nil if disable_insert_output is used" do
339
+ @ds.disable_insert_output.insert_select(:value=>10).should == nil
340
+ end
341
+
342
+ specify "should have insert_select return nil if the server version is not 2005+" do
343
+ @ds.meta_def(:server_version){8000760}
344
+ @ds.insert_select(:value=>10).should == nil
345
+ end
346
+
347
+ specify "should have insert_select insert the record and return the inserted record" do
348
+ h = @ds.insert_select(:value=>10)
349
+ h[:value].should == 10
350
+ @ds.first(:xid=>h[:xid])[:value].should == 10
351
+ end
352
+ end
353
+
354
+ context "MSSSQL::Dataset#disable_insert_output" do
355
+ specify "should play nicely with simple_select_all?" do
356
+ MSSQL_DB[:test].disable_insert_output.send(:simple_select_all?).should == true
357
+ end
358
+ end
359
+
360
+ context "MSSSQL::Dataset#into" do
361
+ before do
362
+ @db = MSSQL_DB
363
+ end
364
+
365
+ specify "should format SELECT statement" do
366
+ @db[:t].into(:new).select_sql.should == "SELECT * INTO NEW FROM T"
367
+ end
368
+
369
+ specify "should select rows into a new table" do
370
+ @db.create_table!(:t) {Integer :id; String :value}
371
+ @db[:t].insert(:id => 1, :value => "test")
372
+ @db << @db[:t].into(:new).select_sql
373
+ @db[:new].all.should == [{:id => 1, :value => "test"}]
374
+ @db.drop_table(:t)
375
+ @db.drop_table(:new)
376
+ end
377
+ end
378
+
379
+ context "A MSSQL database" do
380
+ before do
381
+ @db = MSSQL_DB
382
+ end
383
+ after do
384
+ @db.drop_table(:a)
385
+ end
386
+
387
+ specify "should handle many existing types for set_column_allow_null" do
388
+ @db.create_table!(:a){column :a, 'integer'}
389
+ @db.alter_table(:a){set_column_allow_null :a, false}
390
+ @db.create_table!(:a){column :a, 'decimal(24, 2)'}
391
+ @db.alter_table(:a){set_column_allow_null :a, false}
392
+ @db.schema(:a).first.last[:column_size].should == 24
393
+ @db.schema(:a).first.last[:scale].should == 2
394
+ @db.create_table!(:a){column :a, 'decimal(10)'}
395
+ @db.schema(:a).first.last[:column_size].should == 10
396
+ @db.schema(:a).first.last[:scale].should == 0
397
+ @db.alter_table(:a){set_column_allow_null :a, false}
398
+ @db.create_table!(:a){column :a, 'nchar(2)'}
399
+ @db.alter_table(:a){set_column_allow_null :a, false}
400
+ s = @db.schema(:a).first.last
401
+ (s[:max_chars] || s[:column_size]).should == 2
402
+ end
403
+ end