sequel 3.21.0 → 3.36.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 (311) hide show
  1. data/CHANGELOG +855 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +23 -7
  4. data/Rakefile +42 -24
  5. data/bin/sequel +26 -8
  6. data/doc/active_record.rdoc +1 -0
  7. data/doc/advanced_associations.rdoc +171 -11
  8. data/doc/association_basics.rdoc +293 -43
  9. data/doc/dataset_basics.rdoc +5 -5
  10. data/doc/dataset_filtering.rdoc +8 -0
  11. data/doc/mass_assignment.rdoc +56 -0
  12. data/doc/migration.rdoc +85 -552
  13. data/doc/model_hooks.rdoc +78 -27
  14. data/doc/object_model.rdoc +541 -0
  15. data/doc/opening_databases.rdoc +97 -50
  16. data/doc/prepared_statements.rdoc +40 -14
  17. data/doc/querying.rdoc +28 -4
  18. data/doc/reflection.rdoc +30 -4
  19. data/doc/release_notes/3.22.0.txt +39 -0
  20. data/doc/release_notes/3.23.0.txt +172 -0
  21. data/doc/release_notes/3.24.0.txt +420 -0
  22. data/doc/release_notes/3.25.0.txt +88 -0
  23. data/doc/release_notes/3.26.0.txt +88 -0
  24. data/doc/release_notes/3.27.0.txt +82 -0
  25. data/doc/release_notes/3.28.0.txt +304 -0
  26. data/doc/release_notes/3.29.0.txt +459 -0
  27. data/doc/release_notes/3.30.0.txt +135 -0
  28. data/doc/release_notes/3.31.0.txt +146 -0
  29. data/doc/release_notes/3.32.0.txt +202 -0
  30. data/doc/release_notes/3.33.0.txt +157 -0
  31. data/doc/release_notes/3.34.0.txt +671 -0
  32. data/doc/release_notes/3.35.0.txt +144 -0
  33. data/doc/release_notes/3.36.0.txt +245 -0
  34. data/doc/schema_modification.rdoc +572 -0
  35. data/doc/sharding.rdoc +95 -3
  36. data/doc/testing.rdoc +129 -0
  37. data/doc/thread_safety.rdoc +17 -0
  38. data/doc/transactions.rdoc +137 -0
  39. data/doc/virtual_rows.rdoc +51 -2
  40. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  41. data/lib/sequel/adapters/ado.rb +40 -18
  42. data/lib/sequel/adapters/amalgalite.rb +17 -9
  43. data/lib/sequel/adapters/db2.rb +175 -86
  44. data/lib/sequel/adapters/dbi.rb +15 -15
  45. data/lib/sequel/adapters/do/mysql.rb +17 -11
  46. data/lib/sequel/adapters/do/postgres.rb +1 -57
  47. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  48. data/lib/sequel/adapters/do.rb +18 -36
  49. data/lib/sequel/adapters/firebird.rb +27 -208
  50. data/lib/sequel/adapters/ibmdb.rb +453 -0
  51. data/lib/sequel/adapters/informix.rb +6 -23
  52. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  53. data/lib/sequel/adapters/jdbc/db2.rb +47 -0
  54. data/lib/sequel/adapters/jdbc/derby.rb +287 -0
  55. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  56. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  57. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  58. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  59. data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  61. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  62. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +58 -48
  64. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  65. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  66. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  67. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  68. data/lib/sequel/adapters/jdbc.rb +265 -94
  69. data/lib/sequel/adapters/mock.rb +361 -0
  70. data/lib/sequel/adapters/mysql.rb +118 -222
  71. data/lib/sequel/adapters/mysql2.rb +58 -57
  72. data/lib/sequel/adapters/odbc/mssql.rb +15 -7
  73. data/lib/sequel/adapters/odbc.rb +28 -9
  74. data/lib/sequel/adapters/openbase.rb +3 -5
  75. data/lib/sequel/adapters/oracle.rb +349 -53
  76. data/lib/sequel/adapters/postgres.rb +356 -107
  77. data/lib/sequel/adapters/shared/access.rb +32 -9
  78. data/lib/sequel/adapters/shared/db2.rb +342 -0
  79. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  80. data/lib/sequel/adapters/shared/informix.rb +53 -0
  81. data/lib/sequel/adapters/shared/mssql.rb +335 -118
  82. data/lib/sequel/adapters/shared/mysql.rb +340 -76
  83. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  84. data/lib/sequel/adapters/shared/oracle.rb +243 -45
  85. data/lib/sequel/adapters/shared/postgres.rb +524 -279
  86. data/lib/sequel/adapters/shared/progress.rb +5 -8
  87. data/lib/sequel/adapters/shared/sqlite.rb +265 -89
  88. data/lib/sequel/adapters/sqlite.rb +101 -39
  89. data/lib/sequel/adapters/swift/mysql.rb +9 -12
  90. data/lib/sequel/adapters/swift/postgres.rb +9 -74
  91. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  92. data/lib/sequel/adapters/swift.rb +8 -7
  93. data/lib/sequel/adapters/tinytds.rb +161 -21
  94. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  95. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  96. data/lib/sequel/ast_transformer.rb +194 -0
  97. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  98. data/lib/sequel/connection_pool/sharded_threaded.rb +28 -2
  99. data/lib/sequel/connection_pool/single.rb +5 -0
  100. data/lib/sequel/connection_pool/threaded.rb +38 -14
  101. data/lib/sequel/connection_pool.rb +10 -2
  102. data/lib/sequel/core.rb +118 -11
  103. data/lib/sequel/database/connecting.rb +38 -28
  104. data/lib/sequel/database/dataset.rb +4 -4
  105. data/lib/sequel/database/dataset_defaults.rb +63 -2
  106. data/lib/sequel/database/logging.rb +7 -2
  107. data/lib/sequel/database/misc.rb +150 -46
  108. data/lib/sequel/database/query.rb +215 -65
  109. data/lib/sequel/database/schema_generator.rb +64 -20
  110. data/lib/sequel/database/schema_methods.rb +212 -54
  111. data/lib/sequel/dataset/actions.rb +322 -85
  112. data/lib/sequel/dataset/features.rb +96 -8
  113. data/lib/sequel/dataset/graph.rb +38 -11
  114. data/lib/sequel/dataset/misc.rb +50 -23
  115. data/lib/sequel/dataset/mutation.rb +12 -7
  116. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  117. data/lib/sequel/dataset/query.rb +237 -44
  118. data/lib/sequel/dataset/sql.rb +698 -278
  119. data/lib/sequel/dataset.rb +8 -0
  120. data/lib/sequel/exceptions.rb +4 -0
  121. data/lib/sequel/extensions/_pretty_table.rb +79 -0
  122. data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
  123. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  124. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +16 -34
  125. data/lib/sequel/extensions/migration.rb +97 -11
  126. data/lib/sequel/extensions/named_timezones.rb +5 -0
  127. data/lib/sequel/extensions/null_dataset.rb +90 -0
  128. data/lib/sequel/extensions/pg_array.rb +460 -0
  129. data/lib/sequel/extensions/pg_array_ops.rb +220 -0
  130. data/lib/sequel/extensions/pg_auto_parameterize.rb +169 -0
  131. data/lib/sequel/extensions/pg_hstore.rb +296 -0
  132. data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
  133. data/lib/sequel/extensions/pg_inet.rb +89 -0
  134. data/lib/sequel/extensions/pg_json.rb +178 -0
  135. data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
  136. data/lib/sequel/extensions/pretty_table.rb +5 -71
  137. data/lib/sequel/extensions/query.rb +1 -1
  138. data/lib/sequel/extensions/query_literals.rb +79 -0
  139. data/lib/sequel/extensions/schema_caching.rb +76 -0
  140. data/lib/sequel/extensions/schema_dumper.rb +251 -37
  141. data/lib/sequel/extensions/select_remove.rb +35 -0
  142. data/lib/sequel/extensions/server_block.rb +139 -0
  143. data/lib/sequel/extensions/sql_expr.rb +4 -110
  144. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  145. data/lib/sequel/extensions/to_dot.rb +95 -83
  146. data/lib/sequel/model/associations.rb +1187 -393
  147. data/lib/sequel/model/base.rb +578 -170
  148. data/lib/sequel/model/errors.rb +1 -1
  149. data/lib/sequel/model/exceptions.rb +5 -1
  150. data/lib/sequel/model/inflections.rb +1 -1
  151. data/lib/sequel/model.rb +30 -11
  152. data/lib/sequel/no_core_ext.rb +2 -0
  153. data/lib/sequel/plugins/active_model.rb +13 -1
  154. data/lib/sequel/plugins/association_pks.rb +22 -4
  155. data/lib/sequel/plugins/caching.rb +25 -18
  156. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  157. data/lib/sequel/plugins/composition.rb +44 -12
  158. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  159. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  160. data/lib/sequel/plugins/dirty.rb +214 -0
  161. data/lib/sequel/plugins/eager_each.rb +59 -0
  162. data/lib/sequel/plugins/force_encoding.rb +6 -6
  163. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  164. data/lib/sequel/plugins/identity_map.rb +123 -8
  165. data/lib/sequel/plugins/instance_filters.rb +10 -0
  166. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  167. data/lib/sequel/plugins/json_serializer.rb +58 -6
  168. data/lib/sequel/plugins/list.rb +12 -2
  169. data/lib/sequel/plugins/many_through_many.rb +79 -28
  170. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  171. data/lib/sequel/plugins/nested_attributes.rb +139 -63
  172. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  173. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  174. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  175. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  176. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  177. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  178. data/lib/sequel/plugins/schema.rb +7 -2
  179. data/lib/sequel/plugins/serialization.rb +98 -49
  180. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  181. data/lib/sequel/plugins/sharding.rb +21 -54
  182. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  183. data/lib/sequel/plugins/static_cache.rb +99 -0
  184. data/lib/sequel/plugins/subclasses.rb +10 -2
  185. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  186. data/lib/sequel/plugins/timestamps.rb +1 -1
  187. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  188. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  189. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  190. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  191. data/lib/sequel/sql.rb +526 -59
  192. data/lib/sequel/timezones.rb +57 -38
  193. data/lib/sequel/version.rb +1 -1
  194. data/spec/adapters/db2_spec.rb +146 -0
  195. data/spec/adapters/firebird_spec.rb +1 -1
  196. data/spec/adapters/mssql_spec.rb +142 -40
  197. data/spec/adapters/mysql_spec.rb +380 -234
  198. data/spec/adapters/oracle_spec.rb +67 -77
  199. data/spec/adapters/postgres_spec.rb +1050 -212
  200. data/spec/adapters/spec_helper.rb +8 -5
  201. data/spec/adapters/sqlite_spec.rb +139 -27
  202. data/spec/core/connection_pool_spec.rb +196 -93
  203. data/spec/core/database_spec.rb +879 -510
  204. data/spec/core/dataset_spec.rb +1415 -1086
  205. data/spec/core/expression_filters_spec.rb +554 -109
  206. data/spec/core/mock_adapter_spec.rb +449 -0
  207. data/spec/core/object_graph_spec.rb +86 -114
  208. data/spec/core/schema_generator_spec.rb +4 -4
  209. data/spec/core/schema_spec.rb +482 -38
  210. data/spec/core/spec_helper.rb +6 -48
  211. data/spec/extensions/active_model_spec.rb +13 -0
  212. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  213. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  214. data/spec/extensions/association_dependencies_spec.rb +15 -25
  215. data/spec/extensions/association_pks_spec.rb +66 -32
  216. data/spec/extensions/association_proxies_spec.rb +4 -4
  217. data/spec/extensions/caching_spec.rb +47 -51
  218. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  219. data/spec/extensions/columns_introspection_spec.rb +91 -0
  220. data/spec/extensions/composition_spec.rb +18 -13
  221. data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +119 -139
  222. data/spec/extensions/dataset_associations_spec.rb +199 -0
  223. data/spec/extensions/defaults_setter_spec.rb +64 -0
  224. data/spec/extensions/dirty_spec.rb +155 -0
  225. data/spec/extensions/eager_each_spec.rb +34 -0
  226. data/spec/extensions/force_encoding_spec.rb +4 -2
  227. data/spec/extensions/hook_class_methods_spec.rb +69 -92
  228. data/spec/extensions/identity_map_spec.rb +131 -22
  229. data/spec/extensions/inflector_spec.rb +0 -4
  230. data/spec/extensions/instance_filters_spec.rb +11 -21
  231. data/spec/extensions/instance_hooks_spec.rb +72 -0
  232. data/spec/extensions/json_serializer_spec.rb +49 -6
  233. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  234. data/spec/extensions/list_spec.rb +48 -39
  235. data/spec/extensions/many_through_many_spec.rb +283 -250
  236. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  237. data/spec/extensions/migration_spec.rb +140 -35
  238. data/spec/extensions/named_timezones_spec.rb +29 -11
  239. data/spec/extensions/nested_attributes_spec.rb +251 -89
  240. data/spec/extensions/null_dataset_spec.rb +85 -0
  241. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  242. data/spec/extensions/pg_array_ops_spec.rb +105 -0
  243. data/spec/extensions/pg_array_spec.rb +196 -0
  244. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  245. data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
  246. data/spec/extensions/pg_hstore_spec.rb +195 -0
  247. data/spec/extensions/pg_inet_spec.rb +44 -0
  248. data/spec/extensions/pg_json_spec.rb +101 -0
  249. data/spec/extensions/pg_statement_cache_spec.rb +209 -0
  250. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  251. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  252. data/spec/extensions/prepared_statements_spec.rb +87 -0
  253. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  254. data/spec/extensions/pretty_table_spec.rb +7 -6
  255. data/spec/extensions/query_literals_spec.rb +168 -0
  256. data/spec/extensions/query_spec.rb +6 -0
  257. data/spec/extensions/rcte_tree_spec.rb +50 -43
  258. data/spec/extensions/schema_caching_spec.rb +41 -0
  259. data/spec/extensions/schema_dumper_spec.rb +429 -22
  260. data/spec/extensions/schema_spec.rb +25 -21
  261. data/spec/extensions/select_remove_spec.rb +38 -0
  262. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  263. data/spec/extensions/serialization_spec.rb +85 -43
  264. data/spec/extensions/server_block_spec.rb +90 -0
  265. data/spec/extensions/sharding_spec.rb +69 -143
  266. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  267. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  268. data/spec/extensions/spec_helper.rb +22 -61
  269. data/spec/extensions/sql_expr_spec.rb +29 -60
  270. data/spec/extensions/static_cache_spec.rb +145 -0
  271. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  272. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  273. data/spec/extensions/timestamps_spec.rb +6 -6
  274. data/spec/extensions/to_dot_spec.rb +4 -7
  275. data/spec/extensions/touch_spec.rb +13 -14
  276. data/spec/extensions/tree_spec.rb +11 -26
  277. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  278. data/spec/extensions/update_primary_key_spec.rb +30 -24
  279. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  280. data/spec/extensions/validation_helpers_spec.rb +16 -35
  281. data/spec/extensions/xml_serializer_spec.rb +29 -4
  282. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  283. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  284. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  285. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  286. data/spec/integration/associations_test.rb +659 -21
  287. data/spec/integration/database_test.rb +9 -3
  288. data/spec/integration/dataset_test.rb +605 -91
  289. data/spec/integration/eager_loader_test.rb +19 -21
  290. data/spec/integration/migrator_test.rb +2 -3
  291. data/spec/integration/model_test.rb +81 -2
  292. data/spec/integration/plugin_test.rb +454 -143
  293. data/spec/integration/prepared_statement_test.rb +263 -128
  294. data/spec/integration/schema_test.rb +309 -43
  295. data/spec/integration/spec_helper.rb +40 -30
  296. data/spec/integration/timezone_test.rb +38 -12
  297. data/spec/integration/transaction_test.rb +183 -8
  298. data/spec/integration/type_test.rb +18 -4
  299. data/spec/model/association_reflection_spec.rb +201 -7
  300. data/spec/model/associations_spec.rb +1067 -661
  301. data/spec/model/base_spec.rb +329 -95
  302. data/spec/model/dataset_methods_spec.rb +7 -27
  303. data/spec/model/eager_loading_spec.rb +677 -676
  304. data/spec/model/hooks_spec.rb +349 -67
  305. data/spec/model/model_spec.rb +240 -168
  306. data/spec/model/plugins_spec.rb +24 -13
  307. data/spec/model/record_spec.rb +490 -232
  308. data/spec/model/spec_helper.rb +14 -71
  309. data/spec/model/validations_spec.rb +11 -0
  310. data/spec/rcov.opts +2 -0
  311. metadata +178 -80
@@ -6,6 +6,14 @@ unless defined?(POSTGRES_DB)
6
6
  end
7
7
  INTEGRATION_DB = POSTGRES_DB unless defined?(INTEGRATION_DB)
8
8
 
9
+ # Automatic parameterization changes the SQL used, so don't check
10
+ # for expected SQL if it is being used.
11
+ if defined?(Sequel::Postgres::AutoParameterize)
12
+ check_sqls = false
13
+ else
14
+ check_sqls = true
15
+ end
16
+
9
17
  def POSTGRES_DB.sqls
10
18
  (@sqls ||= [])
11
19
  end
@@ -13,9 +21,9 @@ logger = Object.new
13
21
  def logger.method_missing(m, msg)
14
22
  POSTGRES_DB.sqls << msg
15
23
  end
16
- POSTGRES_DB.loggers = [logger]
24
+ POSTGRES_DB.loggers << logger
17
25
 
18
- #POSTGRES_DB.instance_variable_set(:@server_version, 80100)
26
+ #POSTGRES_DB.instance_variable_set(:@server_version, 80200)
19
27
  POSTGRES_DB.create_table! :test do
20
28
  text :name
21
29
  integer :value, :index => true
@@ -37,7 +45,7 @@ describe "A PostgreSQL database" do
37
45
  before do
38
46
  @db = POSTGRES_DB
39
47
  end
40
-
48
+
41
49
  specify "should provide the server version" do
42
50
  @db.server_version.should > 70000
43
51
  end
@@ -52,25 +60,34 @@ describe "A PostgreSQL database" do
52
60
  [:value, {:type=>:blob, :allow_null=>true, :default=>nil, :ruby_default=>nil, :db_type=>"bytea", :primary_key=>false}]
53
61
  ]
54
62
  end
63
+
64
+ specify "should parse foreign keys for tables in a schema" do
65
+ begin
66
+ @db.create_table!(:public__testfk){primary_key :id; foreign_key :i, :public__testfk}
67
+ @db.foreign_key_list(:public__testfk).should == [{:on_delete=>:no_action, :on_update=>:no_action, :columns=>[:i], :key=>[:id], :deferrable=>false, :table=>Sequel.qualify(:public, :testfk), :name=>:testfk_i_fkey}]
68
+ ensure
69
+ @db.drop_table(:public__testfk)
70
+ end
71
+ end
55
72
  end
56
73
 
57
74
  describe "A PostgreSQL dataset" do
58
75
  before do
59
76
  @d = POSTGRES_DB[:test]
60
77
  @d.delete # remove all records
78
+ POSTGRES_DB.sqls.clear
61
79
  end
62
-
80
+
63
81
  specify "should quote columns and tables using double quotes if quoting identifiers" do
64
- @d.quote_identifiers = true
65
82
  @d.select(:name).sql.should == \
66
83
  'SELECT "name" FROM "test"'
67
-
84
+
68
85
  @d.select('COUNT(*)'.lit).sql.should == \
69
86
  'SELECT COUNT(*) FROM "test"'
70
87
 
71
88
  @d.select(:max.sql_function(:value)).sql.should == \
72
89
  'SELECT max("value") FROM "test"'
73
-
90
+
74
91
  @d.select(:NOW.sql_function).sql.should == \
75
92
  'SELECT NOW() FROM "test"'
76
93
 
@@ -82,34 +99,32 @@ describe "A PostgreSQL dataset" do
82
99
 
83
100
  @d.select('test.name AS item_name'.lit).sql.should == \
84
101
  'SELECT test.name AS item_name FROM "test"'
85
-
102
+
86
103
  @d.select('"name"'.lit).sql.should == \
87
104
  'SELECT "name" FROM "test"'
88
105
 
89
106
  @d.select('max(test."name") AS "max_name"'.lit).sql.should == \
90
107
  'SELECT max(test."name") AS "max_name" FROM "test"'
91
-
92
- @d.select(:test.sql_function(:abc, 'hello')).sql.should == \
93
- "SELECT test(\"abc\", 'hello') FROM \"test\""
94
108
 
95
- @d.select(:test.sql_function(:abc__def, 'hello')).sql.should == \
96
- "SELECT test(\"abc\".\"def\", 'hello') FROM \"test\""
109
+ @d.insert_sql(:x => :y).should =~ \
110
+ /\AINSERT INTO "test" \("x"\) VALUES \("y"\)( RETURNING NULL)?\z/
97
111
 
98
- @d.select(:test.sql_function(:abc__def, 'hello').as(:x2)).sql.should == \
99
- "SELECT test(\"abc\".\"def\", 'hello') AS \"x2\" FROM \"test\""
112
+ if check_sqls
113
+ @d.select(:test.sql_function(:abc, 'hello')).sql.should == \
114
+ "SELECT test(\"abc\", 'hello') FROM \"test\""
100
115
 
101
- @d.insert_sql(:value => 333).should =~ \
102
- /\AINSERT INTO "test" \("value"\) VALUES \(333\)( RETURNING NULL)?\z/
116
+ @d.select(:test.sql_function(:abc__def, 'hello')).sql.should == \
117
+ "SELECT test(\"abc\".\"def\", 'hello') FROM \"test\""
103
118
 
104
- @d.insert_sql(:x => :y).should =~ \
105
- /\AINSERT INTO "test" \("x"\) VALUES \("y"\)( RETURNING NULL)?\z/
119
+ @d.select(:test.sql_function(:abc__def, 'hello').as(:x2)).sql.should == \
120
+ "SELECT test(\"abc\".\"def\", 'hello') AS \"x2\" FROM \"test\""
106
121
 
107
- @d.disable_insert_returning.insert_sql(:value => 333).should =~ \
108
- /\AINSERT INTO "test" \("value"\) VALUES \(333\)\z/
122
+ @d.insert_sql(:value => 333).should =~ \
123
+ /\AINSERT INTO "test" \("value"\) VALUES \(333\)( RETURNING NULL)?\z/
124
+ end
109
125
  end
110
-
126
+
111
127
  specify "should quote fields correctly when reversing the order if quoting identifiers" do
112
- @d.quote_identifiers = true
113
128
  @d.reverse_order(:name).sql.should == \
114
129
  'SELECT * FROM "test" ORDER BY "name" DESC'
115
130
 
@@ -129,7 +144,7 @@ describe "A PostgreSQL dataset" do
129
144
  @d.filter(:name => /bc/).count.should == 2
130
145
  @d.filter(:name => /^bc/).count.should == 1
131
146
  end
132
-
147
+
133
148
  specify "should support NULLS FIRST and NULLS LAST" do
134
149
  @d << {:name => 'abc'}
135
150
  @d << {:name => 'bcd'}
@@ -138,23 +153,138 @@ describe "A PostgreSQL dataset" do
138
153
  @d.order(:value.asc(:nulls=>:last), :name).select_map(:name).should == %w[bcd abc bcd]
139
154
  @d.order(:value.asc(:nulls=>:first), :name).reverse.select_map(:name).should == %w[bcd bcd abc]
140
155
  end
141
-
156
+
142
157
  specify "#lock should lock tables and yield if a block is given" do
143
158
  @d.lock('EXCLUSIVE'){@d.insert(:name=>'a')}
144
159
  end
145
-
160
+
161
+ specify "should support :using when altering a column's type" do
162
+ begin
163
+ @db = POSTGRES_DB
164
+ @db.create_table!(:atest){Integer :t}
165
+ @db[:atest].insert(1262304000)
166
+ @db.alter_table(:atest){set_column_type :t, Time, :using=>'epoch'.cast(Time) + '1 second'.cast(:interval) * :t}
167
+ @db[:atest].get(:t.extract(:year)).should == 2010
168
+ ensure
169
+ @db.drop_table?(:atest)
170
+ end
171
+ end
172
+
173
+ specify "should support :using with a string when altering a column's type" do
174
+ begin
175
+ @db = POSTGRES_DB
176
+ @db.create_table!(:atest){Integer :t}
177
+ @db[:atest].insert(1262304000)
178
+ @db.alter_table(:atest){set_column_type :t, Time, :using=>"'epoch'::timestamp + '1 second'::interval * t"}
179
+ @db[:atest].get(:t.extract(:year)).should == 2010
180
+ ensure
181
+ @db.drop_table?(:atest)
182
+ end
183
+ end
184
+
185
+ specify "should have #transaction support various types of synchronous options" do
186
+ @db = POSTGRES_DB
187
+ @db.transaction(:synchronous=>:on){}
188
+ @db.transaction(:synchronous=>true){}
189
+ @db.transaction(:synchronous=>:off){}
190
+ @db.transaction(:synchronous=>false){}
191
+ @db.sqls.grep(/synchronous/).should == ["SET LOCAL synchronous_commit = on", "SET LOCAL synchronous_commit = on", "SET LOCAL synchronous_commit = off", "SET LOCAL synchronous_commit = off"]
192
+
193
+ @db.sqls.clear
194
+ @db.transaction(:synchronous=>nil){}
195
+ @db.sqls.should == ['BEGIN', 'COMMIT']
196
+
197
+ if @db.server_version >= 90100
198
+ @db.sqls.clear
199
+ @db.transaction(:synchronous=>:local){}
200
+ @db.sqls.grep(/synchronous/).should == ["SET LOCAL synchronous_commit = local"]
201
+
202
+ if @db.server_version >= 90200
203
+ @db.sqls.clear
204
+ @db.transaction(:synchronous=>:remote_write){}
205
+ @db.sqls.grep(/synchronous/).should == ["SET LOCAL synchronous_commit = remote_write"]
206
+ end
207
+ end
208
+ end
209
+
210
+ specify "should have #transaction support read only transactions" do
211
+ @db = POSTGRES_DB
212
+ @db.transaction(:read_only=>true){}
213
+ @db.transaction(:read_only=>false){}
214
+ @db.transaction(:isolation=>:serializable, :read_only=>true){}
215
+ @db.transaction(:isolation=>:serializable, :read_only=>false){}
216
+ @db.sqls.grep(/READ/).should == ["SET TRANSACTION READ ONLY", "SET TRANSACTION READ WRITE", "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY", "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ WRITE"]
217
+ end
218
+
219
+ specify "should have #transaction support deferrable transactions" do
220
+ @db = POSTGRES_DB
221
+ @db.transaction(:deferrable=>true){}
222
+ @db.transaction(:deferrable=>false){}
223
+ @db.transaction(:deferrable=>true, :read_only=>true){}
224
+ @db.transaction(:deferrable=>false, :read_only=>false){}
225
+ @db.transaction(:isolation=>:serializable, :deferrable=>true, :read_only=>true){}
226
+ @db.transaction(:isolation=>:serializable, :deferrable=>false, :read_only=>false){}
227
+ @db.sqls.grep(/DEF/).should == ["SET TRANSACTION DEFERRABLE", "SET TRANSACTION NOT DEFERRABLE", "SET TRANSACTION READ ONLY DEFERRABLE", "SET TRANSACTION READ WRITE NOT DEFERRABLE", "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY DEFERRABLE", "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE READ WRITE NOT DEFERRABLE"]
228
+ end if POSTGRES_DB.server_version >= 90100
229
+
230
+ specify "should support creating indexes concurrently" do
231
+ POSTGRES_DB.sqls.clear
232
+ POSTGRES_DB.add_index :test, [:name, :value], :concurrently=>true
233
+ POSTGRES_DB.sqls.should == ['CREATE INDEX CONCURRENTLY "test_name_value_index" ON "test" ("name", "value")'] if check_sqls
234
+ end
235
+
236
+ specify "should support dropping indexes only if they already exist" do
237
+ POSTGRES_DB.add_index :test, [:name, :value], :name=>'tnv1'
238
+ POSTGRES_DB.sqls.clear
239
+ POSTGRES_DB.drop_index :test, [:name, :value], :if_exists=>true, :name=>'tnv1'
240
+ POSTGRES_DB.sqls.should == ['DROP INDEX IF EXISTS "tnv1"']
241
+ end
242
+
243
+ specify "should support CASCADE when dropping indexes" do
244
+ POSTGRES_DB.add_index :test, [:name, :value], :name=>'tnv2'
245
+ POSTGRES_DB.sqls.clear
246
+ POSTGRES_DB.drop_index :test, [:name, :value], :cascade=>true, :name=>'tnv2'
247
+ POSTGRES_DB.sqls.should == ['DROP INDEX "tnv2" CASCADE']
248
+ end
249
+
250
+ specify "should support dropping indexes concurrently" do
251
+ POSTGRES_DB.add_index :test, [:name, :value], :name=>'tnv2'
252
+ POSTGRES_DB.sqls.clear
253
+ POSTGRES_DB.drop_index :test, [:name, :value], :concurrently=>true, :name=>'tnv2'
254
+ POSTGRES_DB.sqls.should == ['DROP INDEX CONCURRENTLY "tnv2"']
255
+ end if POSTGRES_DB.server_version >= 90200
256
+
146
257
  specify "#lock should lock table if inside a transaction" do
147
258
  POSTGRES_DB.transaction{@d.lock('EXCLUSIVE'); @d.insert(:name=>'a')}
148
259
  end
149
-
260
+
150
261
  specify "#lock should return nil" do
151
262
  @d.lock('EXCLUSIVE'){@d.insert(:name=>'a')}.should == nil
152
263
  POSTGRES_DB.transaction{@d.lock('EXCLUSIVE').should == nil; @d.insert(:name=>'a')}
153
264
  end
154
-
265
+
155
266
  specify "should raise an error if attempting to update a joined dataset with a single FROM table" do
156
267
  proc{POSTGRES_DB[:test].join(:test2, [:name]).update(:name=>'a')}.should raise_error(Sequel::Error, 'Need multiple FROM tables if updating/deleting a dataset with JOINs')
157
268
  end
269
+
270
+ specify "should truncate with options" do
271
+ @d << { :name => 'abc', :value => 1}
272
+ @d.count.should == 1
273
+ @d.truncate(:cascade => true)
274
+ @d.count.should == 0
275
+ if @d.db.server_version > 80400
276
+ @d << { :name => 'abc', :value => 1}
277
+ @d.truncate(:cascade => true, :only=>true, :restart=>true)
278
+ @d.count.should == 0
279
+ end
280
+ end
281
+
282
+ specify "should truncate multiple tables at once" do
283
+ tables = [:test, :test2, :test3, :test4]
284
+ tables.each{|t| @d.from(t).insert}
285
+ @d.from(:test, :test2, :test3, :test4).truncate
286
+ tables.each{|t| @d.from(t).count.should == 0}
287
+ end
158
288
  end
159
289
 
160
290
  describe "Dataset#distinct" do
@@ -167,9 +297,9 @@ describe "Dataset#distinct" do
167
297
  @ds = @db[:a]
168
298
  end
169
299
  after do
170
- @db.drop_table(:a)
300
+ @db.drop_table?(:a)
171
301
  end
172
-
302
+
173
303
  it "#distinct with arguments should return results distinct on those arguments" do
174
304
  @ds.insert(20, 10)
175
305
  @ds.insert(30, 10)
@@ -191,41 +321,45 @@ if POSTGRES_DB.pool.respond_to?(:max_size) and POSTGRES_DB.pool.max_size > 1
191
321
  @ds = POSTGRES_DB[:items]
192
322
  end
193
323
  after do
194
- POSTGRES_DB.drop_table(:items)
324
+ POSTGRES_DB.drop_table?(:items)
195
325
  POSTGRES_DB.disconnect
196
326
  end
197
-
327
+
198
328
  specify "should handle FOR UPDATE" do
199
329
  @ds.insert(:number=>20)
200
- c = nil
201
- t = nil
330
+ c, t = nil, nil
331
+ q = Queue.new
202
332
  POSTGRES_DB.transaction do
203
333
  @ds.for_update.first(:id=>1)
204
334
  t = Thread.new do
205
335
  POSTGRES_DB.transaction do
336
+ q.push nil
206
337
  @ds.filter(:id=>1).update(:name=>'Jim')
207
338
  c = @ds.first(:id=>1)
339
+ q.push nil
208
340
  end
209
341
  end
210
- sleep 0.01
342
+ q.pop
211
343
  @ds.filter(:id=>1).update(:number=>30)
212
344
  end
345
+ q.pop
213
346
  t.join
214
347
  c.should == {:id=>1, :number=>30, :name=>'Jim'}
215
348
  end
216
-
349
+
217
350
  specify "should handle FOR SHARE" do
218
351
  @ds.insert(:number=>20)
219
- c = nil
220
- t = nil
352
+ c, t = nil
353
+ q = Queue.new
221
354
  POSTGRES_DB.transaction do
222
355
  @ds.for_share.first(:id=>1)
223
356
  t = Thread.new do
224
357
  POSTGRES_DB.transaction do
225
358
  c = @ds.for_share.filter(:id=>1).first
359
+ q.push nil
226
360
  end
227
361
  end
228
- sleep 0.1
362
+ q.pop
229
363
  @ds.filter(:id=>1).update(:name=>'Jim')
230
364
  c.should == {:id=>1, :number=>20, :name=>nil}
231
365
  end
@@ -236,26 +370,59 @@ end
236
370
 
237
371
  describe "A PostgreSQL dataset with a timestamp field" do
238
372
  before do
239
- @d = POSTGRES_DB[:test3]
373
+ @db = POSTGRES_DB
374
+ @d = @db[:test3]
240
375
  @d.delete
241
376
  end
377
+ after do
378
+ @db.convert_infinite_timestamps = false if @db.adapter_scheme == :postgres
379
+ end
242
380
 
243
- cspecify "should store milliseconds in time fields for Time objects", :do do
381
+ cspecify "should store milliseconds in time fields for Time objects", :do, :swift do
244
382
  t = Time.now
245
383
  @d << {:value=>1, :time=>t}
246
- t2 = @d[:value =>'1'][:time]
384
+ t2 = @d[:value =>1][:time]
247
385
  @d.literal(t2).should == @d.literal(t)
248
386
  t2.strftime('%Y-%m-%d %H:%M:%S').should == t.strftime('%Y-%m-%d %H:%M:%S')
249
- t2.is_a?(Time) ? t2.usec : t2.strftime('%N').to_i/1000 == t.usec
387
+ (t2.is_a?(Time) ? t2.usec : t2.strftime('%N').to_i/1000).should == t.usec
250
388
  end
251
389
 
252
- cspecify "should store milliseconds in time fields for DateTime objects", :do do
390
+ cspecify "should store milliseconds in time fields for DateTime objects", :do, :swift do
253
391
  t = DateTime.now
254
392
  @d << {:value=>1, :time=>t}
255
- t2 = @d[:value =>'1'][:time]
393
+ t2 = @d[:value =>1][:time]
256
394
  @d.literal(t2).should == @d.literal(t)
257
395
  t2.strftime('%Y-%m-%d %H:%M:%S').should == t.strftime('%Y-%m-%d %H:%M:%S')
258
- t2.is_a?(Time) ? t2.usec : t2.strftime('%N').to_i/1000 == t.strftime('%N').to_i/1000
396
+ (t2.is_a?(Time) ? t2.usec : t2.strftime('%N').to_i/1000).should == t.strftime('%N').to_i/1000
397
+ end
398
+
399
+ if POSTGRES_DB.adapter_scheme == :postgres
400
+ specify "should handle infinite timestamps if convert_infinite_timestamps is set" do
401
+ @d << {:time=>'infinity'.cast(:timestamp)}
402
+ @db.convert_infinite_timestamps = :nil
403
+ @db[:test3].get(:time).should == nil
404
+ @db.convert_infinite_timestamps = :string
405
+ @db[:test3].get(:time).should == 'infinity'
406
+ @db.convert_infinite_timestamps = :float
407
+ @db[:test3].get(:time).should == 1.0/0.0
408
+
409
+ @d.update(:time=>'-infinity'.cast(:timestamp))
410
+ @db.convert_infinite_timestamps = :nil
411
+ @db[:test3].get(:time).should == nil
412
+ @db.convert_infinite_timestamps = :string
413
+ @db[:test3].get(:time).should == '-infinity'
414
+ @db.convert_infinite_timestamps = :float
415
+ @db[:test3].get(:time).should == -1.0/0.0
416
+ end
417
+
418
+ specify "should handle conversions from infinite strings/floats in models" do
419
+ c = Class.new(Sequel::Model(:test3))
420
+ @db.convert_infinite_timestamps = :float
421
+ c.new(:time=>'infinity').time.should == 'infinity'
422
+ c.new(:time=>'-infinity').time.should == '-infinity'
423
+ c.new(:time=>1.0/0.0).time.should == 1.0/0.0
424
+ c.new(:time=>-1.0/0.0).time.should == -1.0/0.0
425
+ end
259
426
  end
260
427
  end
261
428
 
@@ -281,12 +448,12 @@ describe "A PostgreSQL database" do
281
448
  @db[:test2].columns.should == [:name, :value, :xyz]
282
449
  @db[:test2] << {:name => 'mmm', :value => 111}
283
450
  @db[:test2].first[:xyz].should == '000'
284
-
451
+
285
452
  @db[:test2].columns.should == [:name, :value, :xyz]
286
453
  @db.drop_column :test2, :xyz
287
-
454
+
288
455
  @db[:test2].columns.should == [:name, :value]
289
-
456
+
290
457
  @db[:test2].delete
291
458
  @db.add_column :test2, :xyz, :text, :default => '000'
292
459
  @db[:test2] << {:name => 'mmm', :value => 111, :xyz => 'qqqq'}
@@ -295,31 +462,31 @@ describe "A PostgreSQL database" do
295
462
  @db.rename_column :test2, :xyz, :zyx
296
463
  @db[:test2].columns.should == [:name, :value, :zyx]
297
464
  @db[:test2].first[:zyx].should == 'qqqq'
298
-
465
+
299
466
  @db.add_column :test2, :xyz, :float
300
467
  @db[:test2].delete
301
468
  @db[:test2] << {:name => 'mmm', :value => 111, :xyz => 56.78}
302
469
  @db.set_column_type :test2, :xyz, :integer
303
-
470
+
304
471
  @db[:test2].first[:xyz].should == 57
305
472
  end
306
-
473
+
307
474
  specify "#locks should be a dataset returning database locks " do
308
475
  @db.locks.should be_a_kind_of(Sequel::Dataset)
309
476
  @db.locks.all.should be_a_kind_of(Array)
310
477
  end
311
- end
478
+ end
312
479
 
313
480
  describe "A PostgreSQL database" do
314
481
  before do
315
482
  @db = POSTGRES_DB
316
- @db.drop_table(:posts) rescue nil
483
+ @db.drop_table?(:posts)
317
484
  @db.sqls.clear
318
485
  end
319
486
  after do
320
- @db.drop_table(:posts) rescue nil
487
+ @db.drop_table?(:posts)
321
488
  end
322
-
489
+
323
490
  specify "should support resetting the primary key sequence" do
324
491
  @db.create_table(:posts){primary_key :a}
325
492
  @db[:posts].insert(:a=>20).should == 20
@@ -330,7 +497,7 @@ describe "A PostgreSQL database" do
330
497
  @db[:posts].insert.should == 21
331
498
  @db[:posts].order(:a).map(:a).should == [1, 2, 10, 20, 21]
332
499
  end
333
-
500
+
334
501
  specify "should support specifying Integer/Bignum/Fixnum types in primary keys and have them be auto incrementing" do
335
502
  @db.create_table(:posts){primary_key :a, :type=>Integer}
336
503
  @db[:posts].insert.should == 1
@@ -347,22 +514,22 @@ describe "A PostgreSQL database" do
347
514
  @db.create_table(:posts){Integer :a}
348
515
  @db.reset_primary_key_sequence(:posts).should == nil
349
516
  end
350
-
517
+
351
518
  specify "should support opclass specification" do
352
519
  @db.create_table(:posts){text :title; text :body; integer :user_id; index(:user_id, :opclass => :int4_ops, :type => :btree)}
353
520
  @db.sqls.should == [
354
- "CREATE TABLE posts (title text, body text, user_id integer)",
355
- "CREATE INDEX posts_user_id_index ON posts USING btree (user_id int4_ops)"
521
+ 'CREATE TABLE "posts" ("title" text, "body" text, "user_id" integer)',
522
+ 'CREATE INDEX "posts_user_id_index" ON "posts" USING btree ("user_id" int4_ops)'
356
523
  ]
357
524
  end
358
525
 
359
526
  specify "should support fulltext indexes and searching" do
360
527
  @db.create_table(:posts){text :title; text :body; full_text_index [:title, :body]; full_text_index :title, :language => 'french'}
361
528
  @db.sqls.should == [
362
- "CREATE TABLE posts (title text, body text)",
363
- "CREATE INDEX posts_title_body_index ON posts USING gin (to_tsvector('simple', (COALESCE(title, '') || ' ' || COALESCE(body, ''))))",
364
- "CREATE INDEX posts_title_index ON posts USING gin (to_tsvector('french', (COALESCE(title, ''))))"
365
- ]
529
+ %{CREATE TABLE "posts" ("title" text, "body" text)},
530
+ %{CREATE INDEX "posts_title_body_index" ON "posts" USING gin (to_tsvector('simple'::regconfig, (COALESCE("title", '') || ' ' || COALESCE("body", ''))))},
531
+ %{CREATE INDEX "posts_title_index" ON "posts" USING gin (to_tsvector('french'::regconfig, (COALESCE("title", ''))))}
532
+ ] if check_sqls
366
533
 
367
534
  @db[:posts].insert(:title=>'ruby rails', :body=>'yowsa')
368
535
  @db[:posts].insert(:title=>'sequel', :body=>'ruby')
@@ -373,51 +540,54 @@ describe "A PostgreSQL database" do
373
540
  @db[:posts].full_text_search([:title, :body], ['yowsa', 'rails']).all.should == [:title=>'ruby rails', :body=>'yowsa']
374
541
  @db[:posts].full_text_search(:title, 'scooby', :language => 'french').all.should == [{:title=>'ruby scooby', :body=>'x'}]
375
542
  @db.sqls.should == [
376
- "SELECT * FROM posts WHERE (to_tsvector('simple', (COALESCE(title, ''))) @@ to_tsquery('simple', 'rails'))",
377
- "SELECT * FROM posts WHERE (to_tsvector('simple', (COALESCE(title, '') || ' ' || COALESCE(body, ''))) @@ to_tsquery('simple', 'yowsa | rails'))",
378
- "SELECT * FROM posts WHERE (to_tsvector('french', (COALESCE(title, ''))) @@ to_tsquery('french', 'scooby'))"]
543
+ %{SELECT * FROM "posts" WHERE (to_tsvector('simple'::regconfig, (COALESCE("title", ''))) @@ to_tsquery('simple'::regconfig, 'rails'))},
544
+ %{SELECT * FROM "posts" WHERE (to_tsvector('simple'::regconfig, (COALESCE("title", '') || ' ' || COALESCE("body", ''))) @@ to_tsquery('simple'::regconfig, 'yowsa | rails'))},
545
+ %{SELECT * FROM "posts" WHERE (to_tsvector('french'::regconfig, (COALESCE("title", ''))) @@ to_tsquery('french'::regconfig, 'scooby'))}] if check_sqls
546
+
547
+ @db[:posts].full_text_search(:title, :$n).call(:select, :n=>'rails').should == [{:title=>'ruby rails', :body=>'yowsa'}]
548
+ @db[:posts].full_text_search(:title, :$n).prepare(:select, :fts_select).call(:n=>'rails').should == [{:title=>'ruby rails', :body=>'yowsa'}]
379
549
  end
380
550
 
381
551
  specify "should support spatial indexes" do
382
552
  @db.create_table(:posts){box :geom; spatial_index [:geom]}
383
553
  @db.sqls.should == [
384
- "CREATE TABLE posts (geom box)",
385
- "CREATE INDEX posts_geom_index ON posts USING gist (geom)"
554
+ 'CREATE TABLE "posts" ("geom" box)',
555
+ 'CREATE INDEX "posts_geom_index" ON "posts" USING gist ("geom")'
386
556
  ]
387
557
  end
388
-
558
+
389
559
  specify "should support indexes with index type" do
390
560
  @db.create_table(:posts){varchar :title, :size => 5; index :title, :type => 'hash'}
391
561
  @db.sqls.should == [
392
- "CREATE TABLE posts (title varchar(5))",
393
- "CREATE INDEX posts_title_index ON posts USING hash (title)"
562
+ 'CREATE TABLE "posts" ("title" varchar(5))',
563
+ 'CREATE INDEX "posts_title_index" ON "posts" USING hash ("title")'
394
564
  ]
395
565
  end
396
-
566
+
397
567
  specify "should support unique indexes with index type" do
398
568
  @db.create_table(:posts){varchar :title, :size => 5; index :title, :type => 'btree', :unique => true}
399
569
  @db.sqls.should == [
400
- "CREATE TABLE posts (title varchar(5))",
401
- "CREATE UNIQUE INDEX posts_title_index ON posts USING btree (title)"
570
+ 'CREATE TABLE "posts" ("title" varchar(5))',
571
+ 'CREATE UNIQUE INDEX "posts_title_index" ON "posts" USING btree ("title")'
402
572
  ]
403
573
  end
404
-
574
+
405
575
  specify "should support partial indexes" do
406
576
  @db.create_table(:posts){varchar :title, :size => 5; index :title, :where => {:title => '5'}}
407
577
  @db.sqls.should == [
408
- "CREATE TABLE posts (title varchar(5))",
409
- "CREATE INDEX posts_title_index ON posts (title) WHERE (title = '5')"
578
+ 'CREATE TABLE "posts" ("title" varchar(5))',
579
+ 'CREATE INDEX "posts_title_index" ON "posts" ("title") WHERE ("title" = \'5\')'
410
580
  ]
411
581
  end
412
-
582
+
413
583
  specify "should support identifiers for table names in indicies" do
414
584
  @db.create_table(Sequel::SQL::Identifier.new(:posts)){varchar :title, :size => 5; index :title, :where => {:title => '5'}}
415
585
  @db.sqls.should == [
416
- "CREATE TABLE posts (title varchar(5))",
417
- "CREATE INDEX posts_title_index ON posts (title) WHERE (title = '5')"
586
+ 'CREATE TABLE "posts" ("title" varchar(5))',
587
+ 'CREATE INDEX "posts_title_index" ON "posts" ("title") WHERE ("title" = \'5\')'
418
588
  ]
419
589
  end
420
-
590
+
421
591
  specify "should support renaming tables" do
422
592
  @db.create_table!(:posts1){primary_key :a}
423
593
  @db.rename_table(:posts1, :posts)
@@ -427,38 +597,33 @@ end
427
597
  describe "Postgres::Dataset#import" do
428
598
  before do
429
599
  @db = POSTGRES_DB
430
- @db.create_table!(:test){Integer :x; Integer :y}
600
+ @db.create_table!(:test){primary_key :x; Integer :y}
431
601
  @db.sqls.clear
432
602
  @ds = @db[:test]
433
603
  end
434
604
  after do
435
- @db.drop_table(:test) rescue nil
605
+ @db.drop_table?(:test)
436
606
  end
437
-
438
- specify "#import should return separate insert statements if server_version < 80200" do
439
- @ds.meta_def(:server_version){80199}
440
-
607
+
608
+
609
+ specify "#import should a single insert statement" do
441
610
  @ds.import([:x, :y], [[1, 2], [3, 4]])
442
-
443
- @db.sqls.should == [
444
- 'BEGIN',
445
- 'INSERT INTO test (x, y) VALUES (1, 2)',
446
- 'INSERT INTO test (x, y) VALUES (3, 4)',
447
- 'COMMIT'
448
- ]
611
+ @db.sqls.should == ['BEGIN', 'INSERT INTO "test" ("x", "y") VALUES (1, 2), (3, 4)', 'COMMIT']
449
612
  @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
450
613
  end
451
-
452
- specify "#import should a single insert statement if server_version >= 80200" do
453
- @ds.meta_def(:server_version){80200}
454
-
455
- @ds.import([:x, :y], [[1, 2], [3, 4]])
456
-
457
- @db.sqls.should == [
458
- 'BEGIN',
459
- 'INSERT INTO test (x, y) VALUES (1, 2), (3, 4)',
460
- 'COMMIT'
461
- ]
614
+
615
+ specify "#import should work correctly when returning primary keys" do
616
+ @ds.import([:x, :y], [[1, 2], [3, 4]], :return=>:primary_key).should == [1, 3]
617
+ @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
618
+ end
619
+
620
+ specify "#import should work correctly when returning primary keys with :slice option" do
621
+ @ds.import([:x, :y], [[1, 2], [3, 4]], :return=>:primary_key, :slice=>1).should == [1, 3]
622
+ @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
623
+ end
624
+
625
+ specify "#import should work correctly with an arbitrary returning value" do
626
+ @ds.returning(:y, :x).import([:x, :y], [[1, 2], [3, 4]]).should == [{:y=>2, :x=>1}, {:y=>4, :x=>3}]
462
627
  @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
463
628
  end
464
629
  end
@@ -471,7 +636,7 @@ describe "Postgres::Dataset#insert" do
471
636
  @ds = @db[:test5]
472
637
  end
473
638
  after do
474
- @db.drop_table(:test5) rescue nil
639
+ @db.drop_table?(:test5)
475
640
  end
476
641
 
477
642
  specify "should work with static SQL" do
@@ -480,56 +645,17 @@ describe "Postgres::Dataset#insert" do
480
645
  @ds.all.should == [{:xid=>1, :value=>10}, {:xid=>2, :value=>20}]
481
646
  end
482
647
 
483
- specify "should work regardless of how it is used" do
484
- @ds.insert(:value=>10).should == 1
485
- @ds.disable_insert_returning.insert(:value=>20).should == 2
486
- @ds.meta_def(:server_version){80100}
487
- @ds.insert(:value=>13).should == 3
488
-
489
- @db.sqls.reject{|x| x =~ /pg_class/}.should == [
490
- 'INSERT INTO test5 (value) VALUES (10) RETURNING xid',
491
- 'INSERT INTO test5 (value) VALUES (20)',
492
- "SELECT currval('\"public\".test5_xid_seq')",
493
- 'INSERT INTO test5 (value) VALUES (13)',
494
- "SELECT currval('\"public\".test5_xid_seq')"
495
- ]
496
- @ds.all.should == [{:xid=>1, :value=>10}, {:xid=>2, :value=>20}, {:xid=>3, :value=>13}]
497
- end
498
-
499
- specify "should call execute_insert if server_version < 80200" do
500
- @ds.meta_def(:server_version){80100}
501
- @ds.should_receive(:execute_insert).once.with('INSERT INTO test5 (value) VALUES (10)', :table=>:test5, :values=>{:value=>10})
502
- @ds.insert(:value=>10)
503
- end
504
-
505
- specify "should call execute_insert if disabling insert returning" do
506
- @ds.disable_insert_returning!
507
- @ds.should_receive(:execute_insert).once.with('INSERT INTO test5 (value) VALUES (10)', :table=>:test5, :values=>{:value=>10})
508
- @ds.insert(:value=>10)
648
+ specify "should insert correctly if using a column array and a value array" do
649
+ @ds.insert([:value], [10]).should == 1
650
+ @ds.all.should == [{:xid=>1, :value=>10}]
509
651
  end
510
652
 
511
- specify "should use INSERT RETURNING if server_version >= 80200" do
512
- @ds.meta_def(:server_version){80201}
513
- @ds.insert(:value=>10)
514
- @db.sqls.last.should == 'INSERT INTO test5 (value) VALUES (10) RETURNING xid'
515
- end
516
-
517
- specify "should have insert_returning_sql use the RETURNING keyword" do
518
- @ds.insert_returning_sql(:xid, :value=>10).should == "INSERT INTO test5 (value) VALUES (10) RETURNING xid"
519
- @ds.insert_returning_sql('*'.lit, :value=>10).should == "INSERT INTO test5 (value) VALUES (10) RETURNING *"
520
- end
521
-
522
- specify "should have insert_select return nil if server_version < 80200" do
523
- @ds.meta_def(:server_version){80100}
524
- @ds.insert_select(:value=>10).should == nil
525
- end
526
-
527
- specify "should have insert_select return nil if disable_insert_returning is used" do
528
- @ds.disable_insert_returning.insert_select(:value=>10).should == nil
653
+ specify "should use INSERT RETURNING" do
654
+ @ds.insert(:value=>10).should == 1
655
+ @db.sqls.last.should == 'INSERT INTO "test5" ("value") VALUES (10) RETURNING "xid"' if check_sqls
529
656
  end
530
657
 
531
- specify "should have insert_select insert the record and return the inserted record if server_version >= 80200" do
532
- @ds.meta_def(:server_version){80201}
658
+ specify "should have insert_select insert the record and return the inserted record" do
533
659
  h = @ds.insert_select(:value=>10)
534
660
  h[:value].should == 10
535
661
  @ds.first(:xid=>h[:xid])[:value].should == 10
@@ -558,11 +684,10 @@ describe "Postgres::Database schema qualified tables" do
558
684
  POSTGRES_DB.instance_variable_set(:@primary_key_sequences, {})
559
685
  end
560
686
  after do
561
- POSTGRES_DB.quote_identifiers = false
562
687
  POSTGRES_DB << "DROP SCHEMA schema_test CASCADE"
563
688
  POSTGRES_DB.default_schema = nil
564
689
  end
565
-
690
+
566
691
  specify "should be able to create, drop, select and insert into tables in a given schema" do
567
692
  POSTGRES_DB.create_table(:schema_test__schema_test){primary_key :i}
568
693
  POSTGRES_DB[:schema_test__schema_test].first.should == nil
@@ -575,19 +700,19 @@ describe "Postgres::Database schema qualified tables" do
575
700
  POSTGRES_DB.from('schema_test.schema_test'.lit).first.should == nil
576
701
  POSTGRES_DB.drop_table(:schema_test.qualify(:schema_test))
577
702
  end
578
-
703
+
579
704
  specify "#tables should not include tables in a default non-public schema" do
580
705
  POSTGRES_DB.create_table(:schema_test__schema_test){integer :i}
581
706
  POSTGRES_DB.tables.should include(:schema_test)
582
707
  POSTGRES_DB.tables.should_not include(:pg_am)
583
708
  POSTGRES_DB.tables.should_not include(:domain_udt_usage)
584
709
  end
585
-
710
+
586
711
  specify "#tables should return tables in the schema provided by the :schema argument" do
587
712
  POSTGRES_DB.create_table(:schema_test__schema_test){integer :i}
588
713
  POSTGRES_DB.tables(:schema=>:schema_test).should == [:schema_test]
589
714
  end
590
-
715
+
591
716
  specify "#schema should not include columns from tables in a default non-public schema" do
592
717
  POSTGRES_DB.create_table(:schema_test__domains){integer :i}
593
718
  sch = POSTGRES_DB.schema(:domains)
@@ -595,7 +720,7 @@ describe "Postgres::Database schema qualified tables" do
595
720
  cs.should include(:i)
596
721
  cs.should_not include(:data_type)
597
722
  end
598
-
723
+
599
724
  specify "#schema should only include columns from the table in the given :schema argument" do
600
725
  POSTGRES_DB.create_table!(:domains){integer :d}
601
726
  POSTGRES_DB.create_table(:schema_test__domains){integer :i}
@@ -605,47 +730,51 @@ describe "Postgres::Database schema qualified tables" do
605
730
  cs.should_not include(:d)
606
731
  POSTGRES_DB.drop_table(:domains)
607
732
  end
608
-
609
- specify "#table_exists? should not include tables from the default non-public schemas" do
610
- POSTGRES_DB.create_table(:schema_test__schema_test){integer :i}
611
- POSTGRES_DB.table_exists?(:schema_test).should == true
612
- POSTGRES_DB.table_exists?(:domain_udt_usage).should == false
733
+
734
+ specify "#schema should raise an exception if columns from tables in two separate schema are returned" do
735
+ POSTGRES_DB.create_table!(:public__domains){integer :d}
736
+ POSTGRES_DB.create_table(:schema_test__domains){integer :i}
737
+ begin
738
+ proc{POSTGRES_DB.schema(:domains)}.should raise_error(Sequel::Error)
739
+ POSTGRES_DB.schema(:public__domains).map{|x| x.first}.should == [:d]
740
+ POSTGRES_DB.schema(:schema_test__domains).map{|x| x.first}.should == [:i]
741
+ ensure
742
+ POSTGRES_DB.drop_table?(:public__domains)
743
+ end
613
744
  end
614
-
745
+
615
746
  specify "#table_exists? should see if the table is in a given schema" do
616
747
  POSTGRES_DB.create_table(:schema_test__schema_test){integer :i}
617
748
  POSTGRES_DB.table_exists?(:schema_test__schema_test).should == true
618
749
  end
619
-
750
+
620
751
  specify "should be able to get primary keys for tables in a given schema" do
621
752
  POSTGRES_DB.create_table(:schema_test__schema_test){primary_key :i}
622
753
  POSTGRES_DB.primary_key(:schema_test__schema_test).should == 'i'
623
754
  end
624
-
755
+
625
756
  specify "should be able to get serial sequences for tables in a given schema" do
626
757
  POSTGRES_DB.create_table(:schema_test__schema_test){primary_key :i}
627
758
  POSTGRES_DB.primary_key_sequence(:schema_test__schema_test).should == '"schema_test".schema_test_i_seq'
628
759
  end
629
-
760
+
630
761
  specify "should be able to get serial sequences for tables that have spaces in the name in a given schema" do
631
- POSTGRES_DB.quote_identifiers = true
632
762
  POSTGRES_DB.create_table(:"schema_test__schema test"){primary_key :i}
633
763
  POSTGRES_DB.primary_key_sequence(:"schema_test__schema test").should == '"schema_test"."schema test_i_seq"'
634
764
  end
635
-
765
+
636
766
  specify "should be able to get custom sequences for tables in a given schema" do
637
767
  POSTGRES_DB << "CREATE SEQUENCE schema_test.kseq"
638
768
  POSTGRES_DB.create_table(:schema_test__schema_test){integer :j; primary_key :k, :type=>:integer, :default=>"nextval('schema_test.kseq'::regclass)".lit}
639
769
  POSTGRES_DB.primary_key_sequence(:schema_test__schema_test).should == '"schema_test".kseq'
640
770
  end
641
-
771
+
642
772
  specify "should be able to get custom sequences for tables that have spaces in the name in a given schema" do
643
- POSTGRES_DB.quote_identifiers = true
644
773
  POSTGRES_DB << "CREATE SEQUENCE schema_test.\"ks eq\""
645
774
  POSTGRES_DB.create_table(:"schema_test__schema test"){integer :j; primary_key :k, :type=>:integer, :default=>"nextval('schema_test.\"ks eq\"'::regclass)".lit}
646
775
  POSTGRES_DB.primary_key_sequence(:"schema_test__schema test").should == '"schema_test"."ks eq"'
647
776
  end
648
-
777
+
649
778
  specify "#default_schema= should change the default schema used from public" do
650
779
  POSTGRES_DB.create_table(:schema_test__schema_test){primary_key :i}
651
780
  POSTGRES_DB.default_schema = :schema_test
@@ -661,13 +790,12 @@ describe "Postgres::Database schema qualified tables and eager graphing" do
661
790
  @db = POSTGRES_DB
662
791
  @db.run "DROP SCHEMA s CASCADE" rescue nil
663
792
  @db.run "CREATE SCHEMA s"
664
- @db.quote_identifiers = true
665
-
793
+
666
794
  @db.create_table(:s__bands){primary_key :id; String :name}
667
795
  @db.create_table(:s__albums){primary_key :id; String :name; foreign_key :band_id, :s__bands}
668
796
  @db.create_table(:s__tracks){primary_key :id; String :name; foreign_key :album_id, :s__albums}
669
797
  @db.create_table(:s__members){primary_key :id; String :name; foreign_key :band_id, :s__bands}
670
-
798
+
671
799
  @Band = Class.new(Sequel::Model(:s__bands))
672
800
  @Album = Class.new(Sequel::Model(:s__albums))
673
801
  @Track = Class.new(Sequel::Model(:s__tracks))
@@ -676,17 +804,17 @@ describe "Postgres::Database schema qualified tables and eager graphing" do
676
804
  def @Album.name; :Album; end
677
805
  def @Track.name; :Track; end
678
806
  def @Member.name; :Member; end
679
-
807
+
680
808
  @Band.one_to_many :albums, :class=>@Album, :order=>:name
681
809
  @Band.one_to_many :members, :class=>@Member, :order=>:name
682
810
  @Album.many_to_one :band, :class=>@Band, :order=>:name
683
811
  @Album.one_to_many :tracks, :class=>@Track, :order=>:name
684
812
  @Track.many_to_one :album, :class=>@Album, :order=>:name
685
813
  @Member.many_to_one :band, :class=>@Band, :order=>:name
686
-
814
+
687
815
  @Member.many_to_many :members, :class=>@Member, :join_table=>:s__bands, :right_key=>:id, :left_key=>:id, :left_primary_key=>:band_id, :right_primary_key=>:band_id, :order=>:name
688
816
  @Band.many_to_many :tracks, :class=>@Track, :join_table=>:s__albums, :right_key=>:id, :right_primary_key=>:album_id, :order=>:name
689
-
817
+
690
818
  @b1 = @Band.create(:name=>"BM")
691
819
  @b2 = @Band.create(:name=>"J")
692
820
  @a1 = @Album.create(:name=>"BM1", :band=>@b1)
@@ -703,10 +831,9 @@ describe "Postgres::Database schema qualified tables and eager graphing" do
703
831
  @m4 = @Member.create(:name=>"JC", :band=>@b2)
704
832
  end
705
833
  after(:all) do
706
- @db.quote_identifiers = false
707
834
  @db.run "DROP SCHEMA s CASCADE"
708
835
  end
709
-
836
+
710
837
  specify "should return all eager graphs correctly" do
711
838
  bands = @Band.order(:bands__name).eager_graph(:albums).all
712
839
  bands.should == [@b1, @b2]
@@ -886,9 +1013,9 @@ if POSTGRES_DB.dataset.supports_window_functions?
886
1013
  @ds.insert(:id=>6, :group_id=>2, :amount=>100000)
887
1014
  end
888
1015
  after do
889
- @db.drop_table(:i1)
1016
+ @db.drop_table?(:i1)
890
1017
  end
891
-
1018
+
892
1019
  specify "should give correct results for window functions" do
893
1020
  @ds.window(:win, :partition=>:group_id, :order=>:id).select(:id){sum(:over, :args=>amount, :window=>win){}}.all.should ==
894
1021
  [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1000, :id=>4}, {:sum=>11000, :id=>5}, {:sum=>111000, :id=>6}]
@@ -902,15 +1029,16 @@ if POSTGRES_DB.dataset.supports_window_functions?
902
1029
  end
903
1030
  end
904
1031
 
905
- describe "Postgres::Database functions, languages, and triggers" do
1032
+ describe "Postgres::Database functions, languages, schemas, and triggers" do
906
1033
  before do
907
1034
  @d = POSTGRES_DB
908
1035
  end
909
1036
  after do
910
1037
  @d.drop_function('tf', :if_exists=>true, :cascade=>true)
911
1038
  @d.drop_function('tf', :if_exists=>true, :cascade=>true, :args=>%w'integer integer')
912
- @d.drop_language(:plpgsql, :if_exists=>true, :cascade=>true)
913
- @d.drop_table(:test) rescue nil
1039
+ @d.drop_language(:plpgsql, :if_exists=>true, :cascade=>true) if @d.server_version < 90000
1040
+ @d.drop_schema(:sequel, :if_exists=>true, :cascade=>true)
1041
+ @d.drop_table?(:test)
914
1042
  end
915
1043
 
916
1044
  specify "#create_function and #drop_function should create and drop functions" do
@@ -923,7 +1051,7 @@ describe "Postgres::Database functions, languages, and triggers" do
923
1051
  @d.drop_function('tf')
924
1052
  proc{@d['SELECT tf()'].all}.should raise_error(Sequel::DatabaseError)
925
1053
  end
926
-
1054
+
927
1055
  specify "#create_function and #drop_function should support options" do
928
1056
  args = ['tf', 'SELECT $1 + $2', {:args=>[[:integer, :a], :integer], :replace=>true, :returns=>:integer, :language=>'SQL', :behavior=>:immutable, :strict=>true, :security_definer=>true, :cost=>2, :set=>{:search_path => 'public'}}]
929
1057
  @d.send(:create_function_sql,*args).should =~ /\A\s*CREATE OR REPLACE FUNCTION tf\(a integer, integer\)\s+RETURNS integer\s+LANGUAGE SQL\s+IMMUTABLE\s+STRICT\s+SECURITY DEFINER\s+COST 2\s+SET search_path = public\s+AS 'SELECT \$1 \+ \$2'\s*\z/
@@ -937,24 +1065,33 @@ describe "Postgres::Database functions, languages, and triggers" do
937
1065
  # Make sure if exists works
938
1066
  @d.drop_function(*args)
939
1067
  end
940
-
1068
+
941
1069
  specify "#create_language and #drop_language should create and drop languages" do
942
1070
  @d.send(:create_language_sql, :plpgsql).should == 'CREATE LANGUAGE plpgsql'
943
- @d.create_language(:plpgsql, :replace=>true)
1071
+ @d.create_language(:plpgsql, :replace=>true) if @d.server_version < 90000
944
1072
  proc{@d.create_language(:plpgsql)}.should raise_error(Sequel::DatabaseError)
945
1073
  @d.send(:drop_language_sql, :plpgsql).should == 'DROP LANGUAGE plpgsql'
946
- @d.drop_language(:plpgsql)
947
- proc{@d.drop_language(:plpgsql)}.should raise_error(Sequel::DatabaseError)
1074
+ @d.drop_language(:plpgsql) if @d.server_version < 90000
1075
+ proc{@d.drop_language(:plpgsql)}.should raise_error(Sequel::DatabaseError) if @d.server_version < 90000
948
1076
  @d.send(:create_language_sql, :plpgsql, :replace=>true, :trusted=>true, :handler=>:a, :validator=>:b).should == (@d.server_version >= 90000 ? 'CREATE OR REPLACE TRUSTED LANGUAGE plpgsql HANDLER a VALIDATOR b' : 'CREATE TRUSTED LANGUAGE plpgsql HANDLER a VALIDATOR b')
949
1077
  @d.send(:drop_language_sql, :plpgsql, :if_exists=>true, :cascade=>true).should == 'DROP LANGUAGE IF EXISTS plpgsql CASCADE'
950
1078
  # Make sure if exists works
951
- @d.drop_language(:plpgsql, :if_exists=>true, :cascade=>true)
1079
+ @d.drop_language(:plpgsql, :if_exists=>true, :cascade=>true) if @d.server_version < 90000
952
1080
  end
953
-
1081
+
1082
+ specify "#create_schema and #drop_schema should create and drop schemas" do
1083
+ @d.send(:create_schema_sql, :sequel).should == 'CREATE SCHEMA "sequel"'
1084
+ @d.send(:drop_schema_sql, :sequel).should == 'DROP SCHEMA "sequel"'
1085
+ @d.send(:drop_schema_sql, :sequel, :if_exists=>true, :cascade=>true).should == 'DROP SCHEMA IF EXISTS "sequel" CASCADE'
1086
+ @d.create_schema(:sequel)
1087
+ @d.create_table(:sequel__test){Integer :a}
1088
+ @d.drop_schema(:sequel, :if_exists=>true, :cascade=>true)
1089
+ end
1090
+
954
1091
  specify "#create_trigger and #drop_trigger should create and drop triggers" do
955
- @d.create_language(:plpgsql)
1092
+ @d.create_language(:plpgsql) if @d.server_version < 90000
956
1093
  @d.create_function(:tf, 'BEGIN IF NEW.value IS NULL THEN RAISE EXCEPTION \'Blah\'; END IF; RETURN NEW; END;', :language=>:plpgsql, :returns=>:trigger)
957
- @d.send(:create_trigger_sql, :test, :identity, :tf, :each_row=>true).should == 'CREATE TRIGGER identity BEFORE INSERT OR UPDATE OR DELETE ON test FOR EACH ROW EXECUTE PROCEDURE tf()'
1094
+ @d.send(:create_trigger_sql, :test, :identity, :tf, :each_row=>true).should == 'CREATE TRIGGER identity BEFORE INSERT OR UPDATE OR DELETE ON "test" FOR EACH ROW EXECUTE PROCEDURE tf()'
958
1095
  @d.create_table(:test){String :name; Integer :value}
959
1096
  @d.create_trigger(:test, :identity, :tf, :each_row=>true)
960
1097
  @d[:test].insert(:name=>'a', :value=>1)
@@ -963,32 +1100,32 @@ describe "Postgres::Database functions, languages, and triggers" do
963
1100
  @d[:test].filter(:name=>'a').all.should == [{:name=>'a', :value=>1}]
964
1101
  @d[:test].filter(:name=>'a').update(:value=>3)
965
1102
  @d[:test].filter(:name=>'a').all.should == [{:name=>'a', :value=>3}]
966
- @d.send(:drop_trigger_sql, :test, :identity).should == 'DROP TRIGGER identity ON test'
1103
+ @d.send(:drop_trigger_sql, :test, :identity).should == 'DROP TRIGGER identity ON "test"'
967
1104
  @d.drop_trigger(:test, :identity)
968
- @d.send(:create_trigger_sql, :test, :identity, :tf, :after=>true, :events=>:insert, :args=>[1, 'a']).should == 'CREATE TRIGGER identity AFTER INSERT ON test EXECUTE PROCEDURE tf(1, \'a\')'
969
- @d.send(:drop_trigger_sql, :test, :identity, :if_exists=>true, :cascade=>true).should == 'DROP TRIGGER IF EXISTS identity ON test CASCADE'
1105
+ @d.send(:create_trigger_sql, :test, :identity, :tf, :after=>true, :events=>:insert, :args=>[1, 'a']).should == 'CREATE TRIGGER identity AFTER INSERT ON "test" EXECUTE PROCEDURE tf(1, \'a\')'
1106
+ @d.send(:drop_trigger_sql, :test, :identity, :if_exists=>true, :cascade=>true).should == 'DROP TRIGGER IF EXISTS identity ON "test" CASCADE'
970
1107
  # Make sure if exists works
971
1108
  @d.drop_trigger(:test, :identity, :if_exists=>true, :cascade=>true)
972
1109
  end
973
1110
  end
974
1111
 
975
1112
  if POSTGRES_DB.adapter_scheme == :postgres
976
- describe "Postgres::Dataset #use_cursor" do
977
- before(:all) do
978
- @db = POSTGRES_DB
979
- @db.create_table!(:test_cursor){Integer :x}
980
- @db.sqls.clear
981
- @ds = @db[:test_cursor]
982
- @db.transaction{1001.times{|i| @ds.insert(i)}}
983
- end
984
- after(:all) do
985
- @db.drop_table(:test) rescue nil
986
- end
987
-
1113
+ describe "Postgres::Dataset #use_cursor" do
1114
+ before(:all) do
1115
+ @db = POSTGRES_DB
1116
+ @db.create_table!(:test_cursor){Integer :x}
1117
+ @db.sqls.clear
1118
+ @ds = @db[:test_cursor]
1119
+ @db.transaction{1001.times{|i| @ds.insert(i)}}
1120
+ end
1121
+ after(:all) do
1122
+ @db.drop_table?(:test_cursor)
1123
+ end
1124
+
988
1125
  specify "should return the same results as the non-cursor use" do
989
1126
  @ds.all.should == @ds.use_cursor.all
990
1127
  end
991
-
1128
+
992
1129
  specify "should respect the :rows_per_fetch option" do
993
1130
  @db.sqls.clear
994
1131
  @ds.use_cursor.all
@@ -997,7 +1134,7 @@ describe "Postgres::Dataset #use_cursor" do
997
1134
  @ds.use_cursor(:rows_per_fetch=>100).all
998
1135
  @db.sqls.length.should == 15
999
1136
  end
1000
-
1137
+
1001
1138
  specify "should handle returning inside block" do
1002
1139
  def @ds.check_return
1003
1140
  use_cursor.each{|r| return}
@@ -1005,5 +1142,706 @@ describe "Postgres::Dataset #use_cursor" do
1005
1142
  @ds.check_return
1006
1143
  @ds.all.should == @ds.use_cursor.all
1007
1144
  end
1145
+ end
1146
+
1147
+ describe "Postgres::PG_NAMED_TYPES" do
1148
+ before do
1149
+ @db = POSTGRES_DB
1150
+ Sequel::Postgres::PG_NAMED_TYPES[:interval] = lambda{|v| v.reverse}
1151
+ @db.reset_conversion_procs
1152
+ end
1153
+ after do
1154
+ Sequel::Postgres::PG_NAMED_TYPES.delete(:interval)
1155
+ @db.reset_conversion_procs
1156
+ @db.drop_table?(:foo)
1157
+ end
1158
+
1159
+ specify "should look up conversion procs by name" do
1160
+ @db.create_table!(:foo){interval :bar}
1161
+ @db[:foo].insert('21 days'.cast(:interval))
1162
+ @db[:foo].get(:bar).should == 'syad 12'
1163
+ end
1164
+ end
1008
1165
  end
1166
+
1167
+ if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG && POSTGRES_DB.server_version >= 90000
1168
+ describe "Postgres::Database#copy_table" do
1169
+ before(:all) do
1170
+ @db = POSTGRES_DB
1171
+ @db.create_table!(:test_copy){Integer :x; Integer :y}
1172
+ ds = @db[:test_copy]
1173
+ ds.insert(1, 2)
1174
+ ds.insert(3, 4)
1175
+ end
1176
+ after(:all) do
1177
+ @db.drop_table?(:test_copy)
1178
+ end
1179
+
1180
+ specify "without a block or options should return a text version of the table as a single string" do
1181
+ @db.copy_table(:test_copy).should == "1\t2\n3\t4\n"
1182
+ end
1183
+
1184
+ specify "without a block and with :format=>:csv should return a csv version of the table as a single string" do
1185
+ @db.copy_table(:test_copy, :format=>:csv).should == "1,2\n3,4\n"
1186
+ end
1187
+
1188
+ specify "should treat string as SQL code" do
1189
+ @db.copy_table('COPY "test_copy" TO STDOUT').should == "1\t2\n3\t4\n"
1190
+ end
1191
+
1192
+ specify "should respect given :options options" do
1193
+ @db.copy_table(:test_copy, :options=>"FORMAT csv, HEADER TRUE").should == "x,y\n1,2\n3,4\n"
1194
+ end
1195
+
1196
+ specify "should respect given :options options when :format is used" do
1197
+ @db.copy_table(:test_copy, :format=>:csv, :options=>"QUOTE '''', FORCE_QUOTE *").should == "'1','2'\n'3','4'\n"
1198
+ end
1199
+
1200
+ specify "should accept dataset as first argument" do
1201
+ @db.copy_table(@db[:test_copy].cross_join(:test_copy___tc).order(:test_copy__x, :test_copy__y, :tc__x, :tc__y)).should == "1\t2\t1\t2\n1\t2\t3\t4\n3\t4\t1\t2\n3\t4\t3\t4\n"
1202
+ end
1203
+
1204
+ specify "with a block and no options should yield each row as a string in text format" do
1205
+ buf = []
1206
+ @db.copy_table(:test_copy){|b| buf << b}
1207
+ buf.should == ["1\t2\n", "3\t4\n"]
1208
+ end
1209
+
1210
+ specify "with a block and :format=>:csv should yield each row as a string in csv format" do
1211
+ buf = []
1212
+ @db.copy_table(:test_copy, :format=>:csv){|b| buf << b}
1213
+ buf.should == ["1,2\n", "3,4\n"]
1214
+ end
1215
+
1216
+ specify "should work fine when using a block that is terminated early with a following copy_table" do
1217
+ buf = []
1218
+ proc{@db.copy_table(:test_copy, :format=>:csv){|b| buf << b; break}}.should raise_error(Sequel::DatabaseDisconnectError)
1219
+ buf.should == ["1,2\n"]
1220
+ buf.clear
1221
+ proc{@db.copy_table(:test_copy, :format=>:csv){|b| buf << b; raise ArgumentError}}.should raise_error(Sequel::DatabaseDisconnectError)
1222
+ buf.should == ["1,2\n"]
1223
+ buf.clear
1224
+ @db.copy_table(:test_copy){|b| buf << b}
1225
+ buf.should == ["1\t2\n", "3\t4\n"]
1226
+ end
1227
+
1228
+ specify "should work fine when using a block that is terminated early with a following regular query" do
1229
+ buf = []
1230
+ proc{@db.copy_table(:test_copy, :format=>:csv){|b| buf << b; break}}.should raise_error(Sequel::DatabaseDisconnectError)
1231
+ buf.should == ["1,2\n"]
1232
+ buf.clear
1233
+ proc{@db.copy_table(:test_copy, :format=>:csv){|b| buf << b; raise ArgumentError}}.should raise_error(Sequel::DatabaseDisconnectError)
1234
+ buf.should == ["1,2\n"]
1235
+ @db[:test_copy].select_order_map(:x).should == [1, 3]
1236
+ end
1237
+ end
1238
+
1239
+ describe "Postgres::Database LISTEN/NOTIFY" do
1240
+ before(:all) do
1241
+ @db = POSTGRES_DB
1242
+ end
1243
+
1244
+ specify "should support listen and notify" do
1245
+ notify_pid = @db.synchronize{|conn| conn.backend_pid}
1246
+
1247
+ called = false
1248
+ @db.listen('foo', :after_listen=>proc{@db.notify('foo')}) do |ev, pid, payload|
1249
+ ev.should == 'foo'
1250
+ pid.should == notify_pid
1251
+ ['', nil].should include(payload)
1252
+ called = true
1253
+ end.should == 'foo'
1254
+ called.should be_true
1255
+
1256
+ called = false
1257
+ @db.listen('foo', :after_listen=>proc{@db.notify('foo', :payload=>'bar')}) do |ev, pid, payload|
1258
+ ev.should == 'foo'
1259
+ pid.should == notify_pid
1260
+ payload.should == 'bar'
1261
+ called = true
1262
+ end.should == 'foo'
1263
+ called.should be_true
1264
+
1265
+ @db.listen('foo', :after_listen=>proc{@db.notify('foo')}).should == 'foo'
1266
+
1267
+ called = false
1268
+ called2 = false
1269
+ i = 0
1270
+ @db.listen(['foo', 'bar'], :after_listen=>proc{@db.notify('foo', :payload=>'bar'); @db.notify('bar', :payload=>'foo')}, :loop=>proc{i+=1}) do |ev, pid, payload|
1271
+ if !called
1272
+ ev.should == 'foo'
1273
+ pid.should == notify_pid
1274
+ payload.should == 'bar'
1275
+ called = true
1276
+ else
1277
+ ev.should == 'bar'
1278
+ pid.should == notify_pid
1279
+ payload.should == 'foo'
1280
+ called2 = true
1281
+ break
1282
+ end
1283
+ end.should be_nil
1284
+ called.should be_true
1285
+ called2.should be_true
1286
+ i.should == 1
1287
+ end
1288
+
1289
+ specify "should accept a :timeout option in listen" do
1290
+ @db.listen('foo2', :timeout=>0.001).should == nil
1291
+ called = false
1292
+ @db.listen('foo2', :timeout=>0.001){|ev, pid, payload| called = true}.should == nil
1293
+ called.should be_false
1294
+ i = 0
1295
+ @db.listen('foo2', :timeout=>0.001, :loop=>proc{i+=1; throw :stop if i > 3}){|ev, pid, payload| called = true}.should == nil
1296
+ i.should == 4
1297
+ end unless RUBY_PLATFORM =~ /mingw/ # Ruby freezes on this spec on this platform/version
1298
+ end
1009
1299
  end
1300
+
1301
+ describe 'PostgreSQL special float handling' do
1302
+ before do
1303
+ @db = POSTGRES_DB
1304
+ @db.create_table!(:test5){Float :value}
1305
+ @db.sqls.clear
1306
+ @ds = @db[:test5]
1307
+ end
1308
+ after do
1309
+ @db.drop_table?(:test5)
1310
+ end
1311
+
1312
+ if check_sqls
1313
+ specify 'should quote NaN' do
1314
+ nan = 0.0/0.0
1315
+ @ds.insert_sql(:value => nan).should == %q{INSERT INTO "test5" ("value") VALUES ('NaN')}
1316
+ end
1317
+
1318
+ specify 'should quote +Infinity' do
1319
+ inf = 1.0/0.0
1320
+ @ds.insert_sql(:value => inf).should == %q{INSERT INTO "test5" ("value") VALUES ('Infinity')}
1321
+ end
1322
+
1323
+ specify 'should quote -Infinity' do
1324
+ inf = -1.0/0.0
1325
+ @ds.insert_sql(:value => inf).should == %q{INSERT INTO "test5" ("value") VALUES ('-Infinity')}
1326
+ end
1327
+ end
1328
+
1329
+ if POSTGRES_DB.adapter_scheme == :postgres
1330
+ specify 'inserts NaN' do
1331
+ nan = 0.0/0.0
1332
+ @ds.insert(:value=>nan)
1333
+ @ds.all[0][:value].nan?.should be_true
1334
+ end
1335
+
1336
+ specify 'inserts +Infinity' do
1337
+ inf = 1.0/0.0
1338
+ @ds.insert(:value=>inf)
1339
+ @ds.all[0][:value].infinite?.should > 0
1340
+ end
1341
+
1342
+ specify 'inserts -Infinity' do
1343
+ inf = -1.0/0.0
1344
+ @ds.insert(:value=>inf)
1345
+ @ds.all[0][:value].infinite?.should < 0
1346
+ end
1347
+ end
1348
+ end
1349
+
1350
+ describe 'PostgreSQL array handling' do
1351
+ before(:all) do
1352
+ Sequel.extension :pg_array
1353
+ @db = POSTGRES_DB
1354
+ @db.extend Sequel::Postgres::PGArray::DatabaseMethods
1355
+ @ds = @db[:items]
1356
+ @native = POSTGRES_DB.adapter_scheme == :postgres
1357
+ end
1358
+ after do
1359
+ @db.drop_table?(:items)
1360
+ end
1361
+
1362
+ specify 'insert and retrieve integer and float arrays of various sizes' do
1363
+ @db.create_table!(:items) do
1364
+ column :i2, 'int2[]'
1365
+ column :i4, 'int4[]'
1366
+ column :i8, 'int8[]'
1367
+ column :r, 'real[]'
1368
+ column :dp, 'double precision[]'
1369
+ end
1370
+ @ds.insert([1].pg_array(:int2), [nil, 2].pg_array(:int4), [3, nil].pg_array(:int8), [4, nil, 4.5].pg_array(:real), [5, nil, 5.5].pg_array("double precision"))
1371
+ @ds.count.should == 1
1372
+ if @native
1373
+ rs = @ds.all
1374
+ rs.should == [{:i2=>[1], :i4=>[nil, 2], :i8=>[3, nil], :r=>[4.0, nil, 4.5], :dp=>[5.0, nil, 5.5]}]
1375
+ rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
1376
+ rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
1377
+ @ds.delete
1378
+ @ds.insert(rs.first)
1379
+ @ds.all.should == rs
1380
+
1381
+ @ds.delete
1382
+ @ds.insert([[1], [2]].pg_array(:int2), [[nil, 2], [3, 4]].pg_array(:int4), [[3, nil], [nil, nil]].pg_array(:int8), [[4, nil], [nil, 4.5]].pg_array(:real), [[5, nil], [nil, 5.5]].pg_array("double precision"))
1383
+ rs = @ds.all
1384
+ rs.should == [{:i2=>[[1], [2]], :i4=>[[nil, 2], [3, 4]], :i8=>[[3, nil], [nil, nil]], :r=>[[4, nil], [nil, 4.5]], :dp=>[[5, nil], [nil, 5.5]]}]
1385
+ rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
1386
+ rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
1387
+ @ds.delete
1388
+ @ds.insert(rs.first)
1389
+ @ds.all.should == rs
1390
+ end
1391
+ end
1392
+
1393
+ specify 'insert and retrieve decimal arrays' do
1394
+ @db.create_table!(:items) do
1395
+ column :n, 'numeric[]'
1396
+ end
1397
+ @ds.insert([BigDecimal.new('1.000000000000000000001'), nil, BigDecimal.new('1')].pg_array(:numeric))
1398
+ @ds.count.should == 1
1399
+ if @native
1400
+ rs = @ds.all
1401
+ rs.should == [{:n=>[BigDecimal.new('1.000000000000000000001'), nil, BigDecimal.new('1')]}]
1402
+ rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
1403
+ rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
1404
+ @ds.delete
1405
+ @ds.insert(rs.first)
1406
+ @ds.all.should == rs
1407
+
1408
+ @ds.delete
1409
+ @ds.insert([[BigDecimal.new('1.0000000000000000000000000000001'), nil], [nil, BigDecimal.new('1')]].pg_array(:numeric))
1410
+ rs = @ds.all
1411
+ rs.should == [{:n=>[[BigDecimal.new('1.0000000000000000000000000000001'), nil], [nil, BigDecimal.new('1')]]}]
1412
+ rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
1413
+ rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
1414
+ @ds.delete
1415
+ @ds.insert(rs.first)
1416
+ @ds.all.should == rs
1417
+ end
1418
+ end
1419
+
1420
+ specify 'insert and retrieve string arrays' do
1421
+ @db.create_table!(:items) do
1422
+ column :c, 'char(4)[]'
1423
+ column :vc, 'varchar[]'
1424
+ column :t, 'text[]'
1425
+ end
1426
+ @ds.insert(['a', nil, 'NULL', 'b"\'c'].pg_array('char(4)'), ['a', nil, 'NULL', 'b"\'c'].pg_array(:varchar), ['a', nil, 'NULL', 'b"\'c'].pg_array(:text))
1427
+ @ds.count.should == 1
1428
+ if @native
1429
+ rs = @ds.all
1430
+ rs.should == [{:c=>['a ', nil, 'NULL', 'b"\'c'], :vc=>['a', nil, 'NULL', 'b"\'c'], :t=>['a', nil, 'NULL', 'b"\'c']}]
1431
+ rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
1432
+ rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
1433
+ @ds.delete
1434
+ @ds.insert(rs.first)
1435
+ @ds.all.should == rs
1436
+
1437
+ @ds.delete
1438
+ @ds.insert([[['a'], [nil]], [['NULL'], ['b"\'c']]].pg_array('char(4)'), [[['a'], ['']], [['NULL'], ['b"\'c']]].pg_array(:varchar), [[['a'], [nil]], [['NULL'], ['b"\'c']]].pg_array(:text))
1439
+ rs = @ds.all
1440
+ rs.should == [{:c=>[[['a '], [nil]], [['NULL'], ['b"\'c']]], :vc=>[[['a'], ['']], [['NULL'], ['b"\'c']]], :t=>[[['a'], [nil]], [['NULL'], ['b"\'c']]]}]
1441
+ rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
1442
+ rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
1443
+ @ds.delete
1444
+ @ds.insert(rs.first)
1445
+ @ds.all.should == rs
1446
+ end
1447
+ end
1448
+
1449
+ specify 'use arrays in bound variables' do
1450
+ @db.create_table!(:items) do
1451
+ column :i, 'int4[]'
1452
+ end
1453
+ @ds.call(:insert, {:i=>[1,2]}, {:i=>:$i})
1454
+ @ds.get(:i).should == [1, 2]
1455
+ @ds.filter(:i=>:$i).call(:first, :i=>[1,2]).should == {:i=>[1,2]}
1456
+ @ds.filter(:i=>:$i).call(:first, :i=>[1,3]).should == nil
1457
+
1458
+ @db.create_table!(:items) do
1459
+ column :i, 'text[]'
1460
+ end
1461
+ a = ["\"\\\\\"{}\n\t\r \v\b123afP", 'NULL', nil, '']
1462
+ @ds.call(:insert, {:i=>:$i}, :i=>a.pg_array)
1463
+ @ds.get(:i).should == a
1464
+ @ds.filter(:i=>:$i).call(:first, :i=>a).should == {:i=>a}
1465
+ @ds.filter(:i=>:$i).call(:first, :i=>['', nil, nil, 'a']).should == nil
1466
+ end if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
1467
+
1468
+ specify 'with models' do
1469
+ @db.create_table!(:items) do
1470
+ primary_key :id
1471
+ column :i, 'integer[]'
1472
+ column :f, 'double precision[]'
1473
+ column :d, 'numeric[]'
1474
+ column :t, 'text[]'
1475
+ end
1476
+ c = Class.new(Sequel::Model(@db[:items]))
1477
+ c.plugin :typecast_on_load, :i, :f, :d, :t unless @native
1478
+ o = c.create(:i=>[1,2, nil], :f=>[[1, 2.5], [3, 4.5]], :d=>[1, BigDecimal.new('1.000000000000000000001')], :t=>[%w'a b c', ['NULL', nil, '1']])
1479
+ o.i.should == [1, 2, nil]
1480
+ o.f.should == [[1, 2.5], [3, 4.5]]
1481
+ o.d.should == [BigDecimal.new('1'), BigDecimal.new('1.000000000000000000001')]
1482
+ o.t.should == [%w'a b c', ['NULL', nil, '1']]
1483
+ end
1484
+
1485
+ specify 'operations/functions with pg_array_ops' do
1486
+ Sequel.extension :pg_array_ops
1487
+ @db.create_table!(:items){column :i, 'integer[]'; column :i2, 'integer[]'; column :i3, 'integer[]'; column :i4, 'integer[]'; column :i5, 'integer[]'}
1488
+ @ds.insert([1, 2, 3].pg_array, [2, 1].pg_array, [4, 4].pg_array, [[5, 5], [4, 3]].pg_array, [1, nil, 5].pg_array)
1489
+
1490
+ @ds.get(:i.pg_array > :i3).should be_false
1491
+ @ds.get(:i3.pg_array > :i).should be_true
1492
+
1493
+ @ds.get(:i.pg_array >= :i3).should be_false
1494
+ @ds.get(:i.pg_array >= :i).should be_true
1495
+
1496
+ @ds.get(:i3.pg_array < :i).should be_false
1497
+ @ds.get(:i.pg_array < :i3).should be_true
1498
+
1499
+ @ds.get(:i3.pg_array <= :i).should be_false
1500
+ @ds.get(:i.pg_array <= :i).should be_true
1501
+
1502
+ @ds.get({5=>:i.pg_array.any}.sql_expr).should be_false
1503
+ @ds.get({1=>:i.pg_array.any}.sql_expr).should be_true
1504
+
1505
+ @ds.get({1=>:i3.pg_array.all}.sql_expr).should be_false
1506
+ @ds.get({4=>:i3.pg_array.all}.sql_expr).should be_true
1507
+
1508
+ @ds.get(:i2.pg_array[1]).should == 2
1509
+ @ds.get(:i2.pg_array[2]).should == 1
1510
+
1511
+ @ds.get(:i4.pg_array[2][1]).should == 4
1512
+ @ds.get(:i4.pg_array[2][2]).should == 3
1513
+
1514
+ @ds.get(:i.pg_array.contains(:i2)).should be_true
1515
+ @ds.get(:i.pg_array.contains(:i3)).should be_false
1516
+
1517
+ @ds.get(:i2.pg_array.contained_by(:i)).should be_true
1518
+ @ds.get(:i.pg_array.contained_by(:i2)).should be_false
1519
+
1520
+ @ds.get(:i.pg_array.overlaps(:i2)).should be_true
1521
+ @ds.get(:i2.pg_array.overlaps(:i3)).should be_false
1522
+
1523
+ @ds.get(:i.pg_array.dims).should == '[1:3]'
1524
+ @ds.get(:i.pg_array.length).should == 3
1525
+ @ds.get(:i.pg_array.lower).should == 1
1526
+
1527
+ if @db.server_version >= 90000
1528
+ @ds.get(:i5.pg_array.join).should == '15'
1529
+ @ds.get(:i5.pg_array.join(':')).should == '1:5'
1530
+ @ds.get(:i5.pg_array.join(':', '*')).should == '1:*:5'
1531
+ end
1532
+ @ds.select(:i.pg_array.unnest).from_self.count.should == 3 if @db.server_version >= 80400
1533
+
1534
+ if @native
1535
+ @ds.get(:i.pg_array.push(4)).should == [1, 2, 3, 4]
1536
+ @ds.get(:i.pg_array.unshift(4)).should == [4, 1, 2, 3]
1537
+ @ds.get(:i.pg_array.concat(:i2)).should == [1, 2, 3, 2, 1]
1538
+ end
1539
+ end
1540
+ end
1541
+
1542
+ describe 'PostgreSQL hstore handling' do
1543
+ before(:all) do
1544
+ Sequel.extension :pg_hstore
1545
+ @db = POSTGRES_DB
1546
+ @db.extend Sequel::Postgres::HStore::DatabaseMethods
1547
+ @ds = @db[:items]
1548
+ @h = {'a'=>'b', 'c'=>nil, 'd'=>'NULL', 'e'=>'\\\\" \\\' ,=>'}
1549
+ @native = POSTGRES_DB.adapter_scheme == :postgres
1550
+ end
1551
+ after do
1552
+ @db.drop_table?(:items)
1553
+ end
1554
+
1555
+ specify 'insert and retrieve hstore values' do
1556
+ @db.create_table!(:items) do
1557
+ column :h, :hstore
1558
+ end
1559
+ @ds.insert(@h.hstore)
1560
+ @ds.count.should == 1
1561
+ if @native
1562
+ rs = @ds.all
1563
+ v = rs.first[:h]
1564
+ v.should_not be_a_kind_of(Hash)
1565
+ v.to_hash.should be_a_kind_of(Hash)
1566
+ v.to_hash.should == @h
1567
+ @ds.delete
1568
+ @ds.insert(rs.first)
1569
+ @ds.all.should == rs
1570
+ end
1571
+ end
1572
+
1573
+ specify 'use hstore in bound variables' do
1574
+ @db.create_table!(:items) do
1575
+ column :i, :hstore
1576
+ end
1577
+ @ds.call(:insert, {:i=>@h.hstore}, {:i=>:$i})
1578
+ @ds.get(:i).should == @h
1579
+ @ds.filter(:i=>:$i).call(:first, :i=>@h.hstore).should == {:i=>@h}
1580
+ @ds.filter(:i=>:$i).call(:first, :i=>{}.hstore).should == nil
1581
+
1582
+ @ds.delete
1583
+ @ds.call(:insert, {:i=>@h}, {:i=>:$i})
1584
+ @ds.get(:i).should == @h
1585
+ @ds.filter(:i=>:$i).call(:first, :i=>@h).should == {:i=>@h}
1586
+ @ds.filter(:i=>:$i).call(:first, :i=>{}).should == nil
1587
+ end if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
1588
+
1589
+ specify 'with models' do
1590
+ @db.create_table!(:items) do
1591
+ primary_key :id
1592
+ column :h, :hstore
1593
+ end
1594
+ c = Class.new(Sequel::Model(@db[:items]))
1595
+ c.plugin :typecast_on_load, :h unless @native
1596
+ c.create(:h=>@h.hstore).h.should == @h
1597
+ end
1598
+
1599
+ specify 'operations/functions with pg_hstore_ops' do
1600
+ Sequel.extension :pg_hstore_ops
1601
+ Sequel.extension :pg_array
1602
+ Sequel.extension :pg_array_ops
1603
+ @db.create_table!(:items){hstore :h1; hstore :h2; hstore :h3; String :t}
1604
+ @ds.insert({'a'=>'b', 'c'=>nil}.hstore, {'a'=>'b'}.hstore, {'d'=>'e'}.hstore)
1605
+ h1 = :h1.hstore
1606
+ h2 = :h2.hstore
1607
+ h3 = :h3.hstore
1608
+
1609
+ @ds.get(h1['a']).should == 'b'
1610
+ @ds.get(h1['d']).should == nil
1611
+
1612
+ @ds.get(h2.concat(h3).keys.pg_array.length).should == 2
1613
+ @ds.get(h1.concat(h3).keys.pg_array.length).should == 3
1614
+ @ds.get(h2.merge(h3).keys.pg_array.length).should == 2
1615
+ @ds.get(h1.merge(h3).keys.pg_array.length).should == 3
1616
+
1617
+ unless @db.adapter_scheme == :do
1618
+ # Broken DataObjects thinks operators with ? represent placeholders
1619
+ @ds.get(h1.contain_all(%w'a c'.pg_array)).should == true
1620
+ @ds.get(h1.contain_all(%w'a d'.pg_array)).should == false
1621
+
1622
+ @ds.get(h1.contain_any(%w'a d'.pg_array)).should == true
1623
+ @ds.get(h1.contain_any(%w'e d'.pg_array)).should == false
1624
+ end
1625
+
1626
+ @ds.get(h1.contains(h2)).should == true
1627
+ @ds.get(h1.contains(h3)).should == false
1628
+
1629
+ @ds.get(h2.contained_by(h1)).should == true
1630
+ @ds.get(h2.contained_by(h3)).should == false
1631
+
1632
+ @ds.get(h1.defined('a')).should == true
1633
+ @ds.get(h1.defined('c')).should == false
1634
+ @ds.get(h1.defined('d')).should == false
1635
+
1636
+ @ds.get(h1.delete('a')['c']).should == nil
1637
+ @ds.get(h1.delete(%w'a d'.pg_array)['c']).should == nil
1638
+ @ds.get(h1.delete(h2)['c']).should == nil
1639
+
1640
+ @ds.from({'a'=>'b', 'c'=>nil}.hstore.op.each).order(:key).all.should == [{:key=>'a', :value=>'b'}, {:key=>'c', :value=>nil}]
1641
+
1642
+ unless @db.adapter_scheme == :do
1643
+ @ds.get(h1.has_key?('c')).should == true
1644
+ @ds.get(h1.include?('c')).should == true
1645
+ @ds.get(h1.key?('c')).should == true
1646
+ @ds.get(h1.member?('c')).should == true
1647
+ @ds.get(h1.exist?('c')).should == true
1648
+ @ds.get(h1.has_key?('d')).should == false
1649
+ @ds.get(h1.include?('d')).should == false
1650
+ @ds.get(h1.key?('d')).should == false
1651
+ @ds.get(h1.member?('d')).should == false
1652
+ @ds.get(h1.exist?('d')).should == false
1653
+ end
1654
+
1655
+ @ds.get(h1.hstore.hstore.hstore.keys.pg_array.length).should == 2
1656
+ @ds.get(h1.keys.pg_array.length).should == 2
1657
+ @ds.get(h2.keys.pg_array.length).should == 1
1658
+ @ds.get(h1.akeys.pg_array.length).should == 2
1659
+ @ds.get(h2.akeys.pg_array.length).should == 1
1660
+
1661
+ @ds.from({'t'=>'s'}.hstore.op.populate(Sequel::SQL::Cast.new(nil, :items))).select_map(:t).should == ['s']
1662
+ @ds.from(:items___i).select({'t'=>'s'}.hstore.op.record_set(:i).as(:r)).from_self(:alias=>:s).select('(r).*'.lit).from_self.select_map(:t).should == ['s']
1663
+
1664
+ @ds.from({'t'=>'s', 'a'=>'b'}.hstore.op.skeys.as(:s)).select_order_map(:s).should == %w'a t'
1665
+
1666
+ @ds.get(h1.slice(%w'a c'.pg_array).keys.pg_array.length).should == 2
1667
+ @ds.get(h1.slice(%w'd c'.pg_array).keys.pg_array.length).should == 1
1668
+ @ds.get(h1.slice(%w'd e'.pg_array).keys.pg_array.length).should == nil
1669
+
1670
+ @ds.from({'t'=>'s', 'a'=>'b'}.hstore.op.svals.as(:s)).select_order_map(:s).should == %w'b s'
1671
+
1672
+ @ds.get(h1.to_array.pg_array.length).should == 4
1673
+ @ds.get(h2.to_array.pg_array.length).should == 2
1674
+
1675
+ @ds.get(h1.to_matrix.pg_array.length).should == 2
1676
+ @ds.get(h2.to_matrix.pg_array.length).should == 1
1677
+
1678
+ @ds.get(h1.values.pg_array.length).should == 2
1679
+ @ds.get(h2.values.pg_array.length).should == 1
1680
+ @ds.get(h1.avals.pg_array.length).should == 2
1681
+ @ds.get(h2.avals.pg_array.length).should == 1
1682
+ end
1683
+ end if POSTGRES_DB.type_supported?(:hstore)
1684
+
1685
+ describe 'PostgreSQL json type' do
1686
+ before(:all) do
1687
+ Sequel.extension :pg_json
1688
+ @db = POSTGRES_DB
1689
+ @db.extend Sequel::Postgres::JSONDatabaseMethods
1690
+ @ds = @db[:items]
1691
+ @a = [1, 2, {'a'=>'b'}, 3.0]
1692
+ @h = {'a'=>'b', '1'=>[3, 4, 5]}
1693
+ @native = POSTGRES_DB.adapter_scheme == :postgres
1694
+ end
1695
+ after do
1696
+ @db.drop_table?(:items)
1697
+ end
1698
+
1699
+ specify 'insert and retrieve json values' do
1700
+ @db.create_table!(:items){json :j}
1701
+ @ds.insert(@h.pg_json)
1702
+ @ds.count.should == 1
1703
+ if @native
1704
+ rs = @ds.all
1705
+ v = rs.first[:j]
1706
+ v.should_not be_a_kind_of(Hash)
1707
+ v.to_hash.should be_a_kind_of(Hash)
1708
+ v.should == @h
1709
+ v.to_hash.should == @h
1710
+ @ds.delete
1711
+ @ds.insert(rs.first)
1712
+ @ds.all.should == rs
1713
+ end
1714
+
1715
+ @ds.delete
1716
+ @ds.insert(@a.pg_json)
1717
+ @ds.count.should == 1
1718
+ if @native
1719
+ rs = @ds.all
1720
+ v = rs.first[:j]
1721
+ v.should_not be_a_kind_of(Array)
1722
+ v.to_a.should be_a_kind_of(Array)
1723
+ v.should == @a
1724
+ v.to_a.should == @a
1725
+ @ds.delete
1726
+ @ds.insert(rs.first)
1727
+ @ds.all.should == rs
1728
+ end
1729
+ end
1730
+
1731
+ specify 'use json in bound variables' do
1732
+ @db.create_table!(:items){json :i}
1733
+ @ds.call(:insert, {:i=>@h.pg_json}, {:i=>:$i})
1734
+ @ds.get(:i).should == @h
1735
+ @ds.filter(:i.cast(String)=>:$i).call(:first, :i=>@h.pg_json).should == {:i=>@h}
1736
+ @ds.filter(:i.cast(String)=>:$i).call(:first, :i=>{}.pg_json).should == nil
1737
+ @ds.filter(:i.cast(String)=>:$i).call(:delete, :i=>@h.pg_json).should == 1
1738
+
1739
+ @ds.call(:insert, {:i=>@a.pg_json}, {:i=>:$i})
1740
+ @ds.get(:i).should == @a
1741
+ @ds.filter(:i.cast(String)=>:$i).call(:first, :i=>@a.pg_json).should == {:i=>@a}
1742
+ @ds.filter(:i.cast(String)=>:$i).call(:first, :i=>[].pg_json).should == nil
1743
+ end if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
1744
+
1745
+ specify 'with models' do
1746
+ @db.create_table!(:items) do
1747
+ primary_key :id
1748
+ json :h
1749
+ end
1750
+ c = Class.new(Sequel::Model(@db[:items]))
1751
+ c.plugin :typecast_on_load, :h unless @native
1752
+ c.create(:h=>@h.pg_json).h.should == @h
1753
+ c.create(:h=>@a.pg_json).h.should == @a
1754
+ end
1755
+ end if POSTGRES_DB.server_version >= 90200
1756
+
1757
+ describe 'PostgreSQL inet/cidr types' do
1758
+ ipv6_broken = (IPAddr.new('::1'); false) rescue true
1759
+
1760
+ before(:all) do
1761
+ Sequel.extension :pg_inet
1762
+ @db = POSTGRES_DB
1763
+ @db.extend Sequel::Postgres::InetDatabaseMethods
1764
+ @ds = @db[:items]
1765
+ @v4 = '127.0.0.1'
1766
+ @v4nm = '127.0.0.0/8'
1767
+ @v6 = '2001:4f8:3:ba:2e0:81ff:fe22:d1f1'
1768
+ @v6nm = '2001:4f8:3:ba::/64'
1769
+ @ipv4 = IPAddr.new(@v4)
1770
+ @ipv4nm = IPAddr.new(@v4nm)
1771
+ unless ipv6_broken
1772
+ @ipv6 = IPAddr.new(@v6)
1773
+ @ipv6nm = IPAddr.new(@v6nm)
1774
+ end
1775
+ @native = POSTGRES_DB.adapter_scheme == :postgres
1776
+ end
1777
+ after do
1778
+ @db.drop_table?(:items)
1779
+ end
1780
+
1781
+ specify 'insert and retrieve inet/cidr values' do
1782
+ @db.create_table!(:items){inet :i; cidr :c}
1783
+ @ds.insert(@ipv4, @ipv4nm)
1784
+ @ds.count.should == 1
1785
+ if @native
1786
+ rs = @ds.all
1787
+ rs.first[:i].should == @ipv4
1788
+ rs.first[:c].should == @ipv4nm
1789
+ rs.first[:i].should be_a_kind_of(IPAddr)
1790
+ rs.first[:c].should be_a_kind_of(IPAddr)
1791
+ @ds.delete
1792
+ @ds.insert(rs.first)
1793
+ @ds.all.should == rs
1794
+ end
1795
+
1796
+ unless ipv6_broken
1797
+ @ds.delete
1798
+ @ds.insert(@ipv6, @ipv6nm)
1799
+ @ds.count.should == 1
1800
+ if @native
1801
+ rs = @ds.all
1802
+ v = rs.first[:j]
1803
+ rs.first[:i].should == @ipv6
1804
+ rs.first[:c].should == @ipv6nm
1805
+ rs.first[:i].should be_a_kind_of(IPAddr)
1806
+ rs.first[:c].should be_a_kind_of(IPAddr)
1807
+ @ds.delete
1808
+ @ds.insert(rs.first)
1809
+ @ds.all.should == rs
1810
+ end
1811
+ end
1812
+ end
1813
+
1814
+ specify 'use ipaddr in bound variables' do
1815
+ @db.create_table!(:items){inet :i; cidr :c}
1816
+
1817
+ @ds.call(:insert, {:i=>@ipv4, :c=>@ipv4nm}, {:i=>:$i, :c=>:$c})
1818
+ @ds.get(:i).should == @ipv4
1819
+ @ds.get(:c).should == @ipv4nm
1820
+ @ds.filter(:i=>:$i, :c=>:$c).call(:first, :i=>@ipv4, :c=>@ipv4nm).should == {:i=>@ipv4, :c=>@ipv4nm}
1821
+ @ds.filter(:i=>:$i, :c=>:$c).call(:first, :i=>@ipv6, :c=>@ipv6nm).should == nil
1822
+ @ds.filter(:i=>:$i, :c=>:$c).call(:delete, :i=>@ipv4, :c=>@ipv4nm).should == 1
1823
+
1824
+ unless ipv6_broken
1825
+ @ds.call(:insert, {:i=>@ipv6, :c=>@ipv6nm}, {:i=>:$i, :c=>:$c})
1826
+ @ds.get(:i).should == @ipv6
1827
+ @ds.get(:c).should == @ipv6nm
1828
+ @ds.filter(:i=>:$i, :c=>:$c).call(:first, :i=>@ipv6, :c=>@ipv6nm).should == {:i=>@ipv6, :c=>@ipv6nm}
1829
+ @ds.filter(:i=>:$i, :c=>:$c).call(:first, :i=>@ipv4, :c=>@ipv4nm).should == nil
1830
+ @ds.filter(:i=>:$i, :c=>:$c).call(:delete, :i=>@ipv6, :c=>@ipv6nm).should == 1
1831
+ end
1832
+ end if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
1833
+
1834
+ specify 'with models' do
1835
+ @db.create_table!(:items) do
1836
+ primary_key :id
1837
+ inet :i
1838
+ cidr :c
1839
+ end
1840
+ c = Class.new(Sequel::Model(@db[:items]))
1841
+ c.plugin :typecast_on_load, :i, :c unless @native
1842
+ c.create(:i=>@v4, :c=>@v4nm).values.values_at(:i, :c).should == [@ipv4, @ipv4nm]
1843
+ unless ipv6_broken
1844
+ c.create(:i=>@ipv6, :c=>@ipv6nm).values.values_at(:i, :c).should == [@ipv6, @ipv6nm]
1845
+ end
1846
+ end
1847
+ end if POSTGRES_DB.server_version >= 90200