sequel 3.21.0 → 3.41.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 (363) hide show
  1. data/CHANGELOG +1131 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +106 -65
  4. data/Rakefile +59 -27
  5. data/bin/sequel +50 -26
  6. data/doc/active_record.rdoc +68 -52
  7. data/doc/advanced_associations.rdoc +201 -75
  8. data/doc/association_basics.rdoc +327 -79
  9. data/doc/bin_sequel.rdoc +144 -0
  10. data/doc/cheat_sheet.rdoc +21 -21
  11. data/doc/core_extensions.rdoc +374 -0
  12. data/doc/dataset_basics.rdoc +10 -10
  13. data/doc/dataset_filtering.rdoc +54 -42
  14. data/doc/mass_assignment.rdoc +56 -0
  15. data/doc/migration.rdoc +89 -557
  16. data/doc/model_hooks.rdoc +80 -29
  17. data/doc/object_model.rdoc +576 -0
  18. data/doc/opening_databases.rdoc +121 -53
  19. data/doc/prepared_statements.rdoc +41 -15
  20. data/doc/querying.rdoc +137 -56
  21. data/doc/reflection.rdoc +36 -10
  22. data/doc/release_notes/3.22.0.txt +39 -0
  23. data/doc/release_notes/3.23.0.txt +172 -0
  24. data/doc/release_notes/3.24.0.txt +420 -0
  25. data/doc/release_notes/3.25.0.txt +88 -0
  26. data/doc/release_notes/3.26.0.txt +88 -0
  27. data/doc/release_notes/3.27.0.txt +82 -0
  28. data/doc/release_notes/3.28.0.txt +304 -0
  29. data/doc/release_notes/3.29.0.txt +459 -0
  30. data/doc/release_notes/3.30.0.txt +135 -0
  31. data/doc/release_notes/3.31.0.txt +146 -0
  32. data/doc/release_notes/3.32.0.txt +202 -0
  33. data/doc/release_notes/3.33.0.txt +157 -0
  34. data/doc/release_notes/3.34.0.txt +671 -0
  35. data/doc/release_notes/3.35.0.txt +144 -0
  36. data/doc/release_notes/3.36.0.txt +245 -0
  37. data/doc/release_notes/3.37.0.txt +338 -0
  38. data/doc/release_notes/3.38.0.txt +234 -0
  39. data/doc/release_notes/3.39.0.txt +237 -0
  40. data/doc/release_notes/3.40.0.txt +73 -0
  41. data/doc/release_notes/3.41.0.txt +155 -0
  42. data/doc/schema_modification.rdoc +585 -0
  43. data/doc/sharding.rdoc +99 -8
  44. data/doc/sql.rdoc +154 -112
  45. data/doc/testing.rdoc +169 -0
  46. data/doc/thread_safety.rdoc +17 -0
  47. data/doc/transactions.rdoc +137 -0
  48. data/doc/validations.rdoc +1 -1
  49. data/doc/virtual_rows.rdoc +78 -43
  50. data/lib/sequel/adapters/ado/access.rb +334 -0
  51. data/lib/sequel/adapters/ado/mssql.rb +18 -12
  52. data/lib/sequel/adapters/ado.rb +69 -21
  53. data/lib/sequel/adapters/amalgalite.rb +17 -14
  54. data/lib/sequel/adapters/cubrid.rb +143 -0
  55. data/lib/sequel/adapters/db2.rb +178 -85
  56. data/lib/sequel/adapters/dbi.rb +19 -21
  57. data/lib/sequel/adapters/do/mysql.rb +17 -11
  58. data/lib/sequel/adapters/do/postgres.rb +2 -61
  59. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  60. data/lib/sequel/adapters/do.rb +20 -39
  61. data/lib/sequel/adapters/firebird.rb +33 -210
  62. data/lib/sequel/adapters/ibmdb.rb +452 -0
  63. data/lib/sequel/adapters/informix.rb +6 -29
  64. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  65. data/lib/sequel/adapters/jdbc/cubrid.rb +52 -0
  66. data/lib/sequel/adapters/jdbc/db2.rb +78 -0
  67. data/lib/sequel/adapters/jdbc/derby.rb +325 -0
  68. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  69. data/lib/sequel/adapters/jdbc/h2.rb +68 -31
  70. data/lib/sequel/adapters/jdbc/hsqldb.rb +189 -0
  71. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  72. data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
  73. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  74. data/lib/sequel/adapters/jdbc/mysql.rb +16 -28
  75. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  76. data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
  77. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  78. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  79. data/lib/sequel/adapters/jdbc/sqlserver.rb +72 -0
  80. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  81. data/lib/sequel/adapters/jdbc.rb +308 -114
  82. data/lib/sequel/adapters/mock.rb +372 -0
  83. data/lib/sequel/adapters/mysql.rb +130 -230
  84. data/lib/sequel/adapters/mysql2.rb +64 -63
  85. data/lib/sequel/adapters/odbc/mssql.rb +17 -9
  86. data/lib/sequel/adapters/odbc.rb +29 -10
  87. data/lib/sequel/adapters/openbase.rb +7 -11
  88. data/lib/sequel/adapters/oracle.rb +364 -57
  89. data/lib/sequel/adapters/postgres.rb +379 -145
  90. data/lib/sequel/adapters/shared/access.rb +236 -11
  91. data/lib/sequel/adapters/shared/cubrid.rb +216 -0
  92. data/lib/sequel/adapters/shared/db2.rb +353 -0
  93. data/lib/sequel/adapters/shared/firebird.rb +231 -0
  94. data/lib/sequel/adapters/shared/informix.rb +53 -0
  95. data/lib/sequel/adapters/shared/mssql.rb +412 -116
  96. data/lib/sequel/adapters/shared/mysql.rb +396 -89
  97. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +166 -0
  98. data/lib/sequel/adapters/shared/oracle.rb +262 -45
  99. data/lib/sequel/adapters/shared/postgres.rb +688 -279
  100. data/lib/sequel/adapters/shared/progress.rb +5 -8
  101. data/lib/sequel/adapters/shared/sqlite.rb +317 -96
  102. data/lib/sequel/adapters/sqlite.rb +109 -46
  103. data/lib/sequel/adapters/swift/mysql.rb +10 -12
  104. data/lib/sequel/adapters/swift/postgres.rb +13 -79
  105. data/lib/sequel/adapters/swift/sqlite.rb +9 -6
  106. data/lib/sequel/adapters/swift.rb +31 -27
  107. data/lib/sequel/adapters/tinytds.rb +177 -25
  108. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
  109. data/lib/sequel/adapters/utils/pg_types.rb +81 -0
  110. data/lib/sequel/adapters/utils/split_alter_table.rb +36 -0
  111. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  112. data/lib/sequel/ast_transformer.rb +194 -0
  113. data/lib/sequel/connection_pool/sharded_single.rb +23 -13
  114. data/lib/sequel/connection_pool/sharded_threaded.rb +64 -13
  115. data/lib/sequel/connection_pool/single.rb +11 -3
  116. data/lib/sequel/connection_pool/threaded.rb +69 -20
  117. data/lib/sequel/connection_pool.rb +20 -18
  118. data/lib/sequel/core.rb +143 -11
  119. data/lib/sequel/database/connecting.rb +66 -29
  120. data/lib/sequel/database/dataset.rb +4 -4
  121. data/lib/sequel/database/dataset_defaults.rb +63 -2
  122. data/lib/sequel/database/logging.rb +7 -2
  123. data/lib/sequel/database/misc.rb +192 -51
  124. data/lib/sequel/database/query.rb +299 -114
  125. data/lib/sequel/database/schema_generator.rb +103 -36
  126. data/lib/sequel/database/schema_methods.rb +310 -69
  127. data/lib/sequel/dataset/actions.rb +344 -90
  128. data/lib/sequel/dataset/features.rb +101 -8
  129. data/lib/sequel/dataset/graph.rb +42 -15
  130. data/lib/sequel/dataset/misc.rb +52 -25
  131. data/lib/sequel/dataset/mutation.rb +12 -7
  132. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  133. data/lib/sequel/dataset/query.rb +322 -68
  134. data/lib/sequel/dataset/sql.rb +716 -293
  135. data/lib/sequel/dataset.rb +8 -0
  136. data/lib/sequel/exceptions.rb +4 -0
  137. data/lib/sequel/extensions/_pretty_table.rb +83 -0
  138. data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
  139. data/lib/sequel/extensions/blank.rb +4 -0
  140. data/lib/sequel/extensions/columns_introspection.rb +72 -0
  141. data/lib/sequel/extensions/connection_validator.rb +109 -0
  142. data/lib/sequel/extensions/constraint_validations.rb +451 -0
  143. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
  144. data/lib/sequel/extensions/eval_inspect.rb +173 -0
  145. data/lib/sequel/extensions/inflector.rb +4 -0
  146. data/lib/sequel/extensions/looser_typecasting.rb +5 -4
  147. data/lib/sequel/extensions/migration.rb +100 -11
  148. data/lib/sequel/extensions/named_timezones.rb +9 -0
  149. data/lib/sequel/extensions/null_dataset.rb +94 -0
  150. data/lib/sequel/extensions/pagination.rb +4 -0
  151. data/lib/sequel/extensions/pg_array.rb +539 -0
  152. data/lib/sequel/extensions/pg_array_ops.rb +263 -0
  153. data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
  154. data/lib/sequel/extensions/pg_hstore.rb +334 -0
  155. data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
  156. data/lib/sequel/extensions/pg_inet.rb +113 -0
  157. data/lib/sequel/extensions/pg_interval.rb +191 -0
  158. data/lib/sequel/extensions/pg_json.rb +212 -0
  159. data/lib/sequel/extensions/pg_range.rb +513 -0
  160. data/lib/sequel/extensions/pg_range_ops.rb +150 -0
  161. data/lib/sequel/extensions/pg_row.rb +576 -0
  162. data/lib/sequel/extensions/pg_row_ops.rb +182 -0
  163. data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
  164. data/lib/sequel/extensions/pretty_table.rb +17 -72
  165. data/lib/sequel/extensions/query.rb +8 -4
  166. data/lib/sequel/extensions/query_literals.rb +79 -0
  167. data/lib/sequel/extensions/schema_caching.rb +76 -0
  168. data/lib/sequel/extensions/schema_dumper.rb +282 -76
  169. data/lib/sequel/extensions/select_remove.rb +39 -0
  170. data/lib/sequel/extensions/server_block.rb +140 -0
  171. data/lib/sequel/extensions/split_array_nil.rb +65 -0
  172. data/lib/sequel/extensions/sql_expr.rb +8 -110
  173. data/lib/sequel/extensions/string_date_time.rb +4 -0
  174. data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
  175. data/lib/sequel/extensions/to_dot.rb +99 -83
  176. data/lib/sequel/model/associations.rb +1263 -409
  177. data/lib/sequel/model/base.rb +624 -171
  178. data/lib/sequel/model/errors.rb +1 -1
  179. data/lib/sequel/model/exceptions.rb +24 -2
  180. data/lib/sequel/model/inflections.rb +1 -1
  181. data/lib/sequel/model.rb +30 -11
  182. data/lib/sequel/no_core_ext.rb +2 -0
  183. data/lib/sequel/plugins/active_model.rb +13 -1
  184. data/lib/sequel/plugins/association_pks.rb +137 -16
  185. data/lib/sequel/plugins/caching.rb +25 -18
  186. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  187. data/lib/sequel/plugins/composition.rb +44 -12
  188. data/lib/sequel/plugins/constraint_validations.rb +198 -0
  189. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  190. data/lib/sequel/plugins/defaults_setter.rb +72 -0
  191. data/lib/sequel/plugins/dirty.rb +214 -0
  192. data/lib/sequel/plugins/eager_each.rb +59 -0
  193. data/lib/sequel/plugins/force_encoding.rb +6 -6
  194. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  195. data/lib/sequel/plugins/identity_map.rb +134 -15
  196. data/lib/sequel/plugins/instance_filters.rb +10 -0
  197. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  198. data/lib/sequel/plugins/json_serializer.rb +77 -6
  199. data/lib/sequel/plugins/list.rb +13 -2
  200. data/lib/sequel/plugins/many_through_many.rb +103 -51
  201. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  202. data/lib/sequel/plugins/nested_attributes.rb +150 -66
  203. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  204. data/lib/sequel/plugins/pg_row.rb +121 -0
  205. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  206. data/lib/sequel/plugins/prepared_statements.rb +168 -0
  207. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  208. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  209. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  210. data/lib/sequel/plugins/rcte_tree.rb +31 -17
  211. data/lib/sequel/plugins/schema.rb +8 -3
  212. data/lib/sequel/plugins/serialization.rb +98 -49
  213. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  214. data/lib/sequel/plugins/sharding.rb +21 -54
  215. data/lib/sequel/plugins/single_table_inheritance.rb +57 -12
  216. data/lib/sequel/plugins/static_cache.rb +99 -0
  217. data/lib/sequel/plugins/subclasses.rb +29 -3
  218. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  219. data/lib/sequel/plugins/timestamps.rb +1 -1
  220. data/lib/sequel/plugins/touch.rb +18 -6
  221. data/lib/sequel/plugins/tree.rb +3 -3
  222. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  223. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  224. data/lib/sequel/plugins/validation_class_methods.rb +2 -1
  225. data/lib/sequel/plugins/validation_helpers.rb +57 -4
  226. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  227. data/lib/sequel/sql.rb +721 -141
  228. data/lib/sequel/timezones.rb +67 -40
  229. data/lib/sequel/version.rb +1 -1
  230. data/spec/adapters/db2_spec.rb +146 -0
  231. data/spec/adapters/firebird_spec.rb +53 -39
  232. data/spec/adapters/mssql_spec.rb +261 -66
  233. data/spec/adapters/mysql_spec.rb +597 -352
  234. data/spec/adapters/oracle_spec.rb +92 -117
  235. data/spec/adapters/postgres_spec.rb +2103 -341
  236. data/spec/adapters/spec_helper.rb +16 -6
  237. data/spec/adapters/sqlite_spec.rb +237 -76
  238. data/spec/core/connection_pool_spec.rb +270 -141
  239. data/spec/core/database_spec.rb +980 -524
  240. data/spec/core/dataset_spec.rb +1658 -1243
  241. data/spec/core/expression_filters_spec.rb +741 -334
  242. data/spec/core/mock_adapter_spec.rb +453 -0
  243. data/spec/core/object_graph_spec.rb +88 -129
  244. data/spec/core/schema_generator_spec.rb +4 -4
  245. data/spec/core/schema_spec.rb +556 -53
  246. data/spec/core/spec_helper.rb +7 -48
  247. data/spec/core_extensions_spec.rb +626 -0
  248. data/spec/extensions/active_model_spec.rb +13 -0
  249. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  250. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  251. data/spec/extensions/association_dependencies_spec.rb +15 -25
  252. data/spec/extensions/association_pks_spec.rb +227 -33
  253. data/spec/extensions/association_proxies_spec.rb +4 -4
  254. data/spec/extensions/boolean_readers_spec.rb +25 -25
  255. data/spec/extensions/caching_spec.rb +47 -51
  256. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  257. data/spec/extensions/columns_introspection_spec.rb +91 -0
  258. data/spec/extensions/composition_spec.rb +18 -13
  259. data/spec/extensions/connection_validator_spec.rb +118 -0
  260. data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
  261. data/spec/extensions/constraint_validations_spec.rb +316 -0
  262. data/spec/extensions/dataset_associations_spec.rb +199 -0
  263. data/spec/extensions/defaults_setter_spec.rb +88 -0
  264. data/spec/extensions/dirty_spec.rb +155 -0
  265. data/spec/extensions/eager_each_spec.rb +34 -0
  266. data/spec/extensions/eval_inspect_spec.rb +67 -0
  267. data/spec/extensions/force_encoding_spec.rb +4 -2
  268. data/spec/extensions/hook_class_methods_spec.rb +97 -128
  269. data/spec/extensions/identity_map_spec.rb +142 -24
  270. data/spec/extensions/inflector_spec.rb +0 -4
  271. data/spec/extensions/instance_filters_spec.rb +11 -21
  272. data/spec/extensions/instance_hooks_spec.rb +72 -0
  273. data/spec/extensions/json_serializer_spec.rb +49 -12
  274. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  275. data/spec/extensions/list_spec.rb +49 -40
  276. data/spec/extensions/looser_typecasting_spec.rb +7 -7
  277. data/spec/extensions/many_through_many_spec.rb +368 -254
  278. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  279. data/spec/extensions/migration_spec.rb +140 -35
  280. data/spec/extensions/named_timezones_spec.rb +29 -11
  281. data/spec/extensions/nested_attributes_spec.rb +268 -89
  282. data/spec/extensions/null_dataset_spec.rb +85 -0
  283. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  284. data/spec/extensions/pg_array_ops_spec.rb +112 -0
  285. data/spec/extensions/pg_array_spec.rb +324 -0
  286. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  287. data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
  288. data/spec/extensions/pg_hstore_spec.rb +200 -0
  289. data/spec/extensions/pg_inet_spec.rb +47 -0
  290. data/spec/extensions/pg_interval_spec.rb +72 -0
  291. data/spec/extensions/pg_json_spec.rb +99 -0
  292. data/spec/extensions/pg_range_ops_spec.rb +56 -0
  293. data/spec/extensions/pg_range_spec.rb +395 -0
  294. data/spec/extensions/pg_row_ops_spec.rb +58 -0
  295. data/spec/extensions/pg_row_plugin_spec.rb +49 -0
  296. data/spec/extensions/pg_row_spec.rb +340 -0
  297. data/spec/extensions/pg_statement_cache_spec.rb +208 -0
  298. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  299. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  300. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  301. data/spec/extensions/prepared_statements_spec.rb +87 -0
  302. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  303. data/spec/extensions/pretty_table_spec.rb +7 -6
  304. data/spec/extensions/query_literals_spec.rb +167 -0
  305. data/spec/extensions/query_spec.rb +9 -3
  306. data/spec/extensions/rcte_tree_spec.rb +50 -43
  307. data/spec/extensions/schema_caching_spec.rb +41 -0
  308. data/spec/extensions/schema_dumper_spec.rb +434 -49
  309. data/spec/extensions/schema_spec.rb +30 -49
  310. data/spec/extensions/select_remove_spec.rb +38 -0
  311. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  312. data/spec/extensions/serialization_spec.rb +86 -48
  313. data/spec/extensions/server_block_spec.rb +90 -0
  314. data/spec/extensions/sharding_spec.rb +69 -143
  315. data/spec/extensions/single_table_inheritance_spec.rb +65 -29
  316. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  317. data/spec/extensions/spec_helper.rb +34 -67
  318. data/spec/extensions/split_array_nil_spec.rb +24 -0
  319. data/spec/extensions/sql_expr_spec.rb +29 -60
  320. data/spec/extensions/static_cache_spec.rb +145 -0
  321. data/spec/extensions/subclasses_spec.rb +14 -0
  322. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  323. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  324. data/spec/extensions/timestamps_spec.rb +6 -6
  325. data/spec/extensions/to_dot_spec.rb +8 -11
  326. data/spec/extensions/touch_spec.rb +53 -14
  327. data/spec/extensions/tree_spec.rb +11 -26
  328. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  329. data/spec/extensions/update_primary_key_spec.rb +30 -24
  330. data/spec/extensions/validation_class_methods_spec.rb +77 -68
  331. data/spec/extensions/validation_helpers_spec.rb +48 -37
  332. data/spec/extensions/xml_serializer_spec.rb +29 -4
  333. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  334. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  335. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  336. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  337. data/spec/integration/associations_test.rb +659 -21
  338. data/spec/integration/database_test.rb +25 -5
  339. data/spec/integration/dataset_test.rb +729 -141
  340. data/spec/integration/eager_loader_test.rb +38 -54
  341. data/spec/integration/migrator_test.rb +2 -3
  342. data/spec/integration/model_test.rb +81 -2
  343. data/spec/integration/plugin_test.rb +871 -160
  344. data/spec/integration/prepared_statement_test.rb +272 -128
  345. data/spec/integration/schema_test.rb +377 -57
  346. data/spec/integration/spec_helper.rb +49 -32
  347. data/spec/integration/timezone_test.rb +38 -12
  348. data/spec/integration/transaction_test.rb +183 -8
  349. data/spec/integration/type_test.rb +40 -8
  350. data/spec/model/association_reflection_spec.rb +207 -8
  351. data/spec/model/associations_spec.rb +1206 -693
  352. data/spec/model/base_spec.rb +334 -100
  353. data/spec/model/dataset_methods_spec.rb +45 -27
  354. data/spec/model/eager_loading_spec.rb +828 -692
  355. data/spec/model/hooks_spec.rb +355 -67
  356. data/spec/model/model_spec.rb +251 -179
  357. data/spec/model/plugins_spec.rb +24 -13
  358. data/spec/model/record_spec.rb +506 -232
  359. data/spec/model/spec_helper.rb +16 -77
  360. data/spec/model/validations_spec.rb +38 -2
  361. data/spec/rcov.opts +2 -0
  362. metadata +223 -81
  363. data/spec/core/core_sql_spec.rb +0 -451
@@ -12,7 +12,7 @@ rescue LoadError => e
12
12
  class PGconn
13
13
  unless method_defined?(:escape_string)
14
14
  if self.respond_to?(:escape)
15
- # If there is no escape_string instead method, but there is an
15
+ # If there is no escape_string instance method, but there is an
16
16
  # escape class method, use that instead.
17
17
  def escape_string(str)
18
18
  Sequel::Postgres.force_standard_strings ? str.gsub("'", "''") : self.class.escape(str)
@@ -87,51 +87,16 @@ module Sequel
87
87
  Dataset::NON_SQL_OPTIONS << :cursor
88
88
  module Postgres
89
89
  CONVERTED_EXCEPTIONS << PGError
90
-
91
- TYPE_TRANSLATOR = tt = Class.new do
92
- def boolean(s) s == 't' end
90
+
91
+ PG_TYPES[17] = Class.new do
93
92
  def bytea(s) ::Sequel::SQL::Blob.new(Adapter.unescape_bytea(s)) end
94
- def integer(s) s.to_i end
95
- def float(s) s.to_f end
96
- def numeric(s) ::BigDecimal.new(s) end
97
- def date(s) ::Sequel.string_to_date(s) end
98
- def date_iso(s) ::Date.new(*s.split("-").map{|x| x.to_i}) end
99
- def time(s) ::Sequel.string_to_time(s) end
100
- def timestamp(s) ::Sequel.database_to_application_timestamp(s) end
101
- end.new
102
-
103
- # Hash with integer keys and callable values for converting PostgreSQL types.
104
- PG_TYPES = {}
105
- {
106
- [16] => tt.method(:boolean),
107
- [17] => tt.method(:bytea),
108
- [20, 21, 22, 23, 26] => tt.method(:integer),
109
- [700, 701] => tt.method(:float),
110
- [790, 1700] => tt.method(:numeric),
111
- [1083, 1266] => tt.method(:time),
112
- [1114, 1184] => tt.method(:timestamp)
113
- }.each do |k,v|
114
- k.each{|n| PG_TYPES[n] = v}
115
- end
116
-
117
- class << self
118
- # As an optimization, Sequel sets the date style to ISO, so that PostgreSQL provides
119
- # the date in a known format that Sequel can parse faster. This can be turned off
120
- # if you require a date style other than ISO.
121
- attr_reader :use_iso_date_format
122
- end
93
+ end.new.method(:bytea)
123
94
 
124
- # Modify the type translator for the date type depending on the value given.
125
- def self.use_iso_date_format=(v)
126
- PG_TYPES[1082] = TYPE_TRANSLATOR.method(v ? :date_iso : :date)
127
- @use_iso_date_format = v
128
- end
129
- self.use_iso_date_format = true
130
-
131
95
  # PGconn subclass for connection specific methods used with the
132
96
  # pg, postgres, or postgres-pr driver.
133
97
  class Adapter < ::PGconn
134
- include Sequel::Postgres::AdapterMethods
98
+ DISCONNECT_ERROR_RE = /\Acould not receive data from server/
99
+
135
100
  self.translate_results = false if respond_to?(:translate_results=)
136
101
 
137
102
  # Hash of prepared statements for this connection. Keys are
@@ -139,52 +104,45 @@ module Sequel
139
104
  # are SQL strings.
140
105
  attr_reader(:prepared_statements) if SEQUEL_POSTGRES_USES_PG
141
106
 
142
- # Apply connection settings for this connection. Current sets
143
- # the date style to ISO in order make Date object creation in ruby faster,
144
- # if Postgres.use_iso_date_format is true.
145
- def apply_connection_settings
146
- super
147
- if Postgres.use_iso_date_format
148
- sql = "SET DateStyle = 'ISO'"
149
- execute(sql)
150
- end
151
- @prepared_statements = {} if SEQUEL_POSTGRES_USES_PG
152
- end
153
-
154
107
  # Raise a Sequel::DatabaseDisconnectError if a PGError is raised and
155
108
  # the connection status cannot be determined or it is not OK.
156
109
  def check_disconnect_errors
157
110
  begin
158
111
  yield
159
- rescue PGError =>e
112
+ rescue PGError => e
113
+ disconnect = false
160
114
  begin
161
115
  s = status
162
116
  rescue PGError
163
- raise Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError)
117
+ disconnect = true
164
118
  end
165
119
  status_ok = (s == Adapter::CONNECTION_OK)
166
- status_ok ? raise : raise(Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError))
120
+ disconnect ||= !status_ok
121
+ disconnect ||= e.message =~ DISCONNECT_ERROR_RE
122
+ disconnect ? raise(Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError)) : raise
167
123
  ensure
168
- block if status_ok
124
+ block if status_ok && !disconnect
169
125
  end
170
126
  end
171
127
 
172
128
  # Execute the given SQL with this connection. If a block is given,
173
129
  # yield the results, otherwise, return the number of changed rows.
174
130
  def execute(sql, args=nil)
175
- q = check_disconnect_errors{@db.log_yield(sql, args){args ? async_exec(sql, args) : async_exec(sql)}}
131
+ args = args.map{|v| @db.bound_variable_arg(v, self)} if args
132
+ q = check_disconnect_errors{execute_query(sql, args)}
176
133
  begin
177
134
  block_given? ? yield(q) : q.cmd_tuples
178
135
  ensure
179
- q.clear
136
+ q.clear if q && q.respond_to?(:clear)
180
137
  end
181
138
  end
182
139
 
183
140
  private
184
-
185
- # Return the requested values for the given row.
186
- def single_value(r)
187
- r.getvalue(0, 0) unless r.nil? || (r.ntuples == 0)
141
+
142
+ # Return the PGResult object that is returned by executing the given
143
+ # sql and args.
144
+ def execute_query(sql, args)
145
+ @db.log_yield(sql, args){args ? async_exec(sql, args) : async_exec(sql)}
188
146
  end
189
147
  end
190
148
 
@@ -192,30 +150,72 @@ module Sequel
192
150
  # pg, postgres, or postgres-pr driver.
193
151
  class Database < Sequel::Database
194
152
  include Sequel::Postgres::DatabaseMethods
153
+
154
+ INFINITE_TIMESTAMP_STRINGS = ['infinity'.freeze, '-infinity'.freeze].freeze
155
+ INFINITE_DATETIME_VALUES = ([PLUS_INFINITY, MINUS_INFINITY] + INFINITE_TIMESTAMP_STRINGS).freeze
195
156
 
196
157
  set_adapter_scheme :postgres
158
+
159
+ # Whether infinite timestamps should be converted on retrieval. By default, no
160
+ # conversion is done, so an error is raised if you attempt to retrieve an infinite
161
+ # timestamp. You can set this to :nil to convert to nil, :string to leave
162
+ # as a string, or :float to convert to an infinite float.
163
+ attr_accessor :convert_infinite_timestamps
197
164
 
198
165
  # Add the primary_keys and primary_key_sequences instance variables,
199
166
  # so we can get the correct return values for inserted rows.
200
167
  def initialize(*args)
201
168
  super
202
- @primary_keys = {}
203
- @primary_key_sequences = {}
169
+ @convert_infinite_timestamps = false
170
+ initialize_postgres_adapter
171
+ end
172
+
173
+ # Convert given argument so that it can be used directly by pg. Currently, pg doesn't
174
+ # handle fractional seconds in Time/DateTime or blobs with "\0", and it won't ever
175
+ # handle Sequel::SQLTime values correctly. Only public for use by the adapter, shouldn't
176
+ # be used by external code.
177
+ def bound_variable_arg(arg, conn)
178
+ case arg
179
+ when Sequel::SQL::Blob
180
+ conn.escape_bytea(arg)
181
+ when Sequel::SQLTime
182
+ literal(arg)
183
+ when DateTime, Time
184
+ literal(arg)
185
+ else
186
+ arg
187
+ end
204
188
  end
205
189
 
206
190
  # Connects to the database. In addition to the standard database
207
191
  # options, using the :encoding or :charset option changes the
208
- # client encoding for the connection.
192
+ # client encoding for the connection, :connect_timeout is a
193
+ # connection timeout in seconds, and :sslmode sets whether postgres's
194
+ # sslmode. :connect_timeout and :ssl_mode are only supported if the pg
195
+ # driver is used.
209
196
  def connect(server)
210
197
  opts = server_opts(server)
211
- conn = Adapter.connect(
212
- (opts[:host] unless blank_object?(opts[:host])),
213
- opts[:port] || 5432,
214
- nil, '',
215
- opts[:database],
216
- opts[:user],
217
- opts[:password]
218
- )
198
+ conn = if SEQUEL_POSTGRES_USES_PG
199
+ connection_params = {
200
+ :host => opts[:host],
201
+ :port => opts[:port] || 5432,
202
+ :dbname => opts[:database],
203
+ :user => opts[:user],
204
+ :password => opts[:password],
205
+ :connect_timeout => opts[:connect_timeout] || 20,
206
+ :sslmode => opts[:sslmode]
207
+ }.delete_if { |key, value| blank_object?(value) }
208
+ Adapter.connect(connection_params)
209
+ else
210
+ Adapter.connect(
211
+ (opts[:host] unless blank_object?(opts[:host])),
212
+ opts[:port] || 5432,
213
+ nil, '',
214
+ opts[:database],
215
+ opts[:user],
216
+ opts[:password]
217
+ )
218
+ end
219
219
  if encoding = opts[:encoding] || opts[:charset]
220
220
  if conn.respond_to?(:set_client_encoding)
221
221
  conn.set_client_encoding(encoding)
@@ -223,38 +223,228 @@ module Sequel
223
223
  conn.async_exec("set client_encoding to '#{encoding}'")
224
224
  end
225
225
  end
226
- conn.db = self
227
- conn.apply_connection_settings
226
+ conn.instance_variable_set(:@db, self)
227
+ conn.instance_variable_set(:@prepared_statements, {}) if SEQUEL_POSTGRES_USES_PG
228
+ connection_configuration_sqls.each{|sql| conn.execute(sql)}
228
229
  conn
229
230
  end
230
231
 
231
- # Return instance of Sequel::Postgres::Dataset with the given options.
232
- def dataset(opts = nil)
233
- Postgres::Dataset.new(self, opts)
232
+ # Disconnect given connection
233
+ def disconnect_connection(conn)
234
+ begin
235
+ conn.finish
236
+ rescue PGError
237
+ end
234
238
  end
235
239
 
236
240
  # Execute the given SQL with the given args on an available connection.
237
241
  def execute(sql, opts={}, &block)
238
- check_database_errors do
239
- return execute_prepared_statement(sql, opts, &block) if Symbol === sql
240
- synchronize(opts[:server]){|conn| conn.execute(sql, opts[:arguments], &block)}
242
+ synchronize(opts[:server]){|conn| check_database_errors{_execute(conn, sql, opts, &block)}}
243
+ end
244
+
245
+ if SEQUEL_POSTGRES_USES_PG
246
+ # +copy_table+ uses PostgreSQL's +COPY TO STDOUT+ SQL statement to return formatted
247
+ # results directly to the caller. This method is only supported if pg is the
248
+ # underlying ruby driver. This method should only be called if you want
249
+ # results returned to the client. If you are using +COPY TO+
250
+ # with a filename, you should just use +run+ instead of this method.
251
+ #
252
+ # The table argument supports the following types:
253
+ #
254
+ # String :: Uses the first argument directly as literal SQL. If you are using
255
+ # a version of PostgreSQL before 9.0, you will probably want to
256
+ # use a string if you are using any options at all, as the syntax
257
+ # Sequel uses for options is only compatible with PostgreSQL 9.0+.
258
+ # Dataset :: Uses a query instead of a table name when copying.
259
+ # other :: Uses a table name (usually a symbol) when copying.
260
+ #
261
+ # The following options are respected:
262
+ #
263
+ # :format :: The format to use. text is the default, so this should be :csv or :binary.
264
+ # :options :: An options SQL string to use, which should contain comma separated options.
265
+ # :server :: The server on which to run the query.
266
+ #
267
+ # If a block is provided, the method continually yields to the block, one yield
268
+ # per row. If a block is not provided, a single string is returned with all
269
+ # of the data.
270
+ def copy_table(table, opts={})
271
+ sql = if table.is_a?(String)
272
+ sql = table
273
+ else
274
+ if opts[:options] || opts[:format]
275
+ options = " ("
276
+ options << "FORMAT #{opts[:format]}" if opts[:format]
277
+ options << "#{', ' if opts[:format]}#{opts[:options]}" if opts[:options]
278
+ options << ')'
279
+ end
280
+ table = if table.is_a?(::Sequel::Dataset)
281
+ "(#{table.sql})"
282
+ else
283
+ literal(table)
284
+ end
285
+ sql = "COPY #{table} TO STDOUT#{options}"
286
+ end
287
+ synchronize(opts[:server]) do |conn|
288
+ conn.execute(sql)
289
+ begin
290
+ if block_given?
291
+ while buf = conn.get_copy_data
292
+ yield buf
293
+ end
294
+ nil
295
+ else
296
+ b = ''
297
+ b << buf while buf = conn.get_copy_data
298
+ b
299
+ end
300
+ ensure
301
+ raise DatabaseDisconnectError, "disconnecting as a partial COPY may leave the connection in an unusable state" if buf
302
+ end
303
+ end
304
+ end
305
+
306
+ # +copy_into+ uses PostgreSQL's +COPY FROM STDIN+ SQL statement to do very fast inserts
307
+ # into a table using input preformatting in either CSV or PostgreSQL text format.
308
+ # This method is only supported if pg is the underlying ruby driver. This method should only be called if you want
309
+ # results returned to the client. If you are using +COPY FROM+
310
+ # with a filename, you should just use +run+ instead of this method.
311
+ #
312
+ # The following options are respected:
313
+ #
314
+ # :columns :: The columns to insert into, with the same order as the columns in the
315
+ # input data. If this isn't given, uses all columns in the table.
316
+ # :data :: The data to copy to PostgreSQL, which should already be in CSV or PostgreSQL
317
+ # text format. This can be either a string, or any object that responds to
318
+ # each and yields string.
319
+ # :format :: The format to use. text is the default, so this should be :csv or :binary.
320
+ # :options :: An options SQL string to use, which should contain comma separated options.
321
+ # :server :: The server on which to run the query.
322
+ #
323
+ # If a block is provided and :data option is not, this will yield to the block repeatedly.
324
+ # The block should return a string, or nil to signal that it is finished.
325
+ def copy_into(table, opts={})
326
+ sql = "COPY #{literal(table)}"
327
+ if cols = opts[:columns]
328
+ sql << literal(Array(cols))
329
+ end
330
+ sql << " FROM STDIN"
331
+ if opts[:options] || opts[:format]
332
+ sql << " ("
333
+ sql << "FORMAT #{opts[:format]}" if opts[:format]
334
+ sql << "#{', ' if opts[:format]}#{opts[:options]}" if opts[:options]
335
+ sql << ')'
336
+ end
337
+
338
+ data = opts[:data]
339
+ data = Array(data) if data.is_a?(String)
340
+
341
+ if block_given? && data
342
+ raise Error, "Cannot provide both a :data option and a block to copy_into"
343
+ elsif !block_given? && !data
344
+ raise Error, "Must provide either a :data option or a block to copy_into"
345
+ end
346
+
347
+ synchronize(opts[:server]) do |conn|
348
+ conn.execute(sql)
349
+ begin
350
+ if block_given?
351
+ while buf = yield
352
+ conn.put_copy_data(buf)
353
+ end
354
+ else
355
+ data.each{|buf| conn.put_copy_data(buf)}
356
+ end
357
+ rescue Exception => e
358
+ conn.put_copy_end("ruby exception occurred while copying data into PostgreSQL")
359
+ raise
360
+ ensure
361
+ conn.put_copy_end unless e
362
+ conn.get_result
363
+ end
364
+ end
365
+ end
366
+
367
+ # Listens on the given channel (or multiple channels if channel is an array), waiting for notifications.
368
+ # After a notification is received, or the timeout has passed, stops listening to the channel. Options:
369
+ #
370
+ # :after_listen :: An object that responds to +call+ that is called with the underlying connection after the LISTEN
371
+ # statement is sent, but before the connection starts waiting for notifications.
372
+ # :loop :: Whether to continually wait for notifications, instead of just waiting for a single
373
+ # notification. If this option is given, a block must be provided. If this object responds to call, it is
374
+ # called with the underlying connection after each notification is received (after the block is called).
375
+ # If a :timeout option is used, and a callable object is given, the object will also be called if the
376
+ # timeout expires. If :loop is used and you want to stop listening, you can either break from inside the
377
+ # block given to #listen, or you can throw :stop from inside the :loop object's call method or the block.
378
+ # :server :: The server on which to listen, if the sharding support is being used.
379
+ # :timeout :: How long to wait for a notification, in seconds (can provide a float value for
380
+ # fractional seconds). If not given or nil, waits indefinitely.
381
+ #
382
+ # This method is only supported if pg is used as the underlying ruby driver. It returns the
383
+ # channel the notification was sent to (as a string), unless :loop was used, in which case it returns nil.
384
+ # If a block is given, it is yielded 3 arguments:
385
+ # * the channel the notification was sent to (as a string)
386
+ # * the backend pid of the notifier (as an integer),
387
+ # * and the payload of the notification (as a string or nil).
388
+ def listen(channels, opts={}, &block)
389
+ check_database_errors do
390
+ synchronize(opts[:server]) do |conn|
391
+ begin
392
+ channels = Array(channels)
393
+ channels.each{|channel| conn.execute("LISTEN #{dataset.send(:table_ref, channel)}")}
394
+ opts[:after_listen].call(conn) if opts[:after_listen]
395
+ timeout = opts[:timeout] ? [opts[:timeout]] : []
396
+ if l = opts[:loop]
397
+ raise Error, 'calling #listen with :loop requires a block' unless block
398
+ loop_call = l.respond_to?(:call)
399
+ catch(:stop) do
400
+ loop do
401
+ conn.wait_for_notify(*timeout, &block)
402
+ l.call(conn) if loop_call
403
+ end
404
+ end
405
+ nil
406
+ else
407
+ conn.wait_for_notify(*timeout, &block)
408
+ end
409
+ ensure
410
+ conn.execute("UNLISTEN *")
411
+ end
412
+ end
413
+ end
241
414
  end
242
415
  end
243
-
244
- # Insert the values into the table and return the primary key (if
245
- # automatically generated).
246
- def execute_insert(sql, opts={})
247
- return execute(sql, opts) if Symbol === sql
248
- check_database_errors do
249
- synchronize(opts[:server]) do |conn|
250
- conn.execute(sql, opts[:arguments])
251
- insert_result(conn, opts[:table], opts[:values])
416
+
417
+ # If convert_infinite_timestamps is true and the value is infinite, return an appropriate
418
+ # value based on the convert_infinite_timestamps setting.
419
+ def to_application_timestamp(value)
420
+ if c = convert_infinite_timestamps
421
+ case value
422
+ when *INFINITE_TIMESTAMP_STRINGS
423
+ infinite_timestamp_value(value)
424
+ else
425
+ super
252
426
  end
427
+ else
428
+ super
253
429
  end
254
430
  end
255
-
431
+
256
432
  private
257
-
433
+
434
+ # Execute the given SQL string or prepared statement on the connection object.
435
+ def _execute(conn, sql, opts, &block)
436
+ if sql.is_a?(Symbol)
437
+ execute_prepared_statement(conn, sql, opts, &block)
438
+ else
439
+ conn.execute(sql, opts[:arguments], &block)
440
+ end
441
+ end
442
+
443
+ # Execute the prepared statement name with the given arguments on the connection.
444
+ def _execute_prepared_statement(conn, ps_name, args, opts)
445
+ conn.exec_prepared(ps_name, args)
446
+ end
447
+
258
448
  # Convert exceptions raised from the block into DatabaseErrors.
259
449
  def check_database_errors
260
450
  begin
@@ -264,45 +454,84 @@ module Sequel
264
454
  end
265
455
  end
266
456
 
267
- # Disconnect given connection
268
- def disconnect_connection(conn)
269
- begin
270
- conn.finish
271
- rescue PGError
272
- end
457
+ # Set the DateStyle to ISO if configured, for faster date parsing.
458
+ def connection_configuration_sqls
459
+ sqls = super
460
+ sqls << "SET DateStyle = 'ISO'" if Postgres.use_iso_date_format
461
+ sqls
273
462
  end
274
-
463
+
464
+ def database_error_classes
465
+ [PGError]
466
+ end
467
+
275
468
  # Execute the prepared statement with the given name on an available
276
469
  # connection, using the given args. If the connection has not prepared
277
470
  # a statement with the given name yet, prepare it. If the connection
278
471
  # has prepared a statement with the same name and different SQL,
279
472
  # deallocate that statement first and then prepare this statement.
280
473
  # If a block is given, yield the result, otherwise, return the number
281
- # of rows changed. If the :insert option is passed, return the value
282
- # of the primary key for the last inserted row.
283
- def execute_prepared_statement(name, opts={})
284
- ps = prepared_statements[name]
474
+ # of rows changed.
475
+ def execute_prepared_statement(conn, name, opts={}, &block)
476
+ ps = prepared_statement(name)
285
477
  sql = ps.prepared_sql
286
478
  ps_name = name.to_s
287
- args = opts[:arguments]
288
- synchronize(opts[:server]) do |conn|
289
- unless conn.prepared_statements[ps_name] == sql
290
- if conn.prepared_statements.include?(ps_name)
291
- conn.execute("DEALLOCATE #{ps_name}") unless conn.prepared_statements[ps_name] == sql
292
- end
293
- conn.prepared_statements[ps_name] = sql
294
- conn.check_disconnect_errors{log_yield("PREPARE #{ps_name} AS #{sql}"){conn.prepare(ps_name, sql)}}
295
- end
296
- q = conn.check_disconnect_errors{log_yield("EXECUTE #{ps_name}", args){conn.exec_prepared(ps_name, args)}}
297
- if opts[:table] && opts[:values]
298
- insert_result(conn, opts[:table], opts[:values])
479
+
480
+ if args = opts[:arguments]
481
+ args = args.map{|arg| bound_variable_arg(arg, conn)}
482
+ end
483
+
484
+ unless conn.prepared_statements[ps_name] == sql
485
+ conn.execute("DEALLOCATE #{ps_name}") if conn.prepared_statements.include?(ps_name)
486
+ conn.check_disconnect_errors{log_yield("PREPARE #{ps_name} AS #{sql}"){conn.prepare(ps_name, sql)}}
487
+ conn.prepared_statements[ps_name] = sql
488
+ end
489
+
490
+ log_sql = "EXECUTE #{ps_name}"
491
+ if ps.log_sql
492
+ log_sql << " ("
493
+ log_sql << sql
494
+ log_sql << ")"
495
+ end
496
+
497
+ q = conn.check_disconnect_errors{log_yield(log_sql, args){_execute_prepared_statement(conn, ps_name, args, opts)}}
498
+ begin
499
+ block_given? ? yield(q) : q.cmd_tuples
500
+ ensure
501
+ q.clear if q && q.respond_to?(:clear)
502
+ end
503
+ end
504
+
505
+ # Return an appropriate value for the given infinite timestamp string.
506
+ def infinite_timestamp_value(value)
507
+ case convert_infinite_timestamps
508
+ when :nil
509
+ nil
510
+ when :string
511
+ value
512
+ else
513
+ value == 'infinity' ? PLUS_INFINITY : MINUS_INFINITY
514
+ end
515
+ end
516
+
517
+ # Don't log, since logging is done by the underlying connection.
518
+ def log_connection_execute(conn, sql)
519
+ conn.execute(sql)
520
+ end
521
+
522
+ # If the value is an infinite value (either an infinite float or a string returned by
523
+ # by PostgreSQL for an infinite timestamp), return it without converting it if
524
+ # convert_infinite_timestamps is set.
525
+ def typecast_value_datetime(value)
526
+ if convert_infinite_timestamps
527
+ case value
528
+ when *INFINITE_DATETIME_VALUES
529
+ value
299
530
  else
300
- begin
301
- block_given? ? yield(q) : q.cmd_tuples
302
- ensure
303
- q.clear
304
- end
531
+ super
305
532
  end
533
+ else
534
+ super
306
535
  end
307
536
  end
308
537
  end
@@ -311,12 +540,15 @@ module Sequel
311
540
  # postgres-pr driver.
312
541
  class Dataset < Sequel::Dataset
313
542
  include Sequel::Postgres::DatasetMethods
543
+
544
+ Database::DatasetClass = self
545
+ APOS = Sequel::Dataset::APOS
314
546
 
315
547
  # Yield all rows returned by executing the given SQL and converting
316
548
  # the types.
317
- def fetch_rows(sql, &block)
318
- return cursor_fetch_rows(sql, &block) if @opts[:cursor]
319
- execute(sql){|res| yield_hash_rows(res, fetch_rows_set_cols(res), &block)}
549
+ def fetch_rows(sql)
550
+ return cursor_fetch_rows(sql){|h| yield h} if @opts[:cursor]
551
+ execute(sql){|res| yield_hash_rows(res, fetch_rows_set_cols(res)){|h| yield h}}
320
552
  end
321
553
 
322
554
  # Uses a cursor for fetching records, instead of fetching the entire result
@@ -372,11 +604,17 @@ module Sequel
372
604
  end
373
605
  LiteralString.new("#{prepared_arg_placeholder}#{i}#{"::#{type}" if type}")
374
606
  end
607
+
608
+ # Always assume a prepared argument.
609
+ def prepared_arg?(k)
610
+ true
611
+ end
375
612
  end
376
613
 
377
614
  # Allow use of bind arguments for PostgreSQL using the pg driver.
378
615
  module BindArgumentMethods
379
616
  include ArgumentMapper
617
+ include ::Sequel::Postgres::DatasetMethods::PreparedStatementMethods
380
618
 
381
619
  private
382
620
 
@@ -389,18 +627,18 @@ module Sequel
389
627
  def execute_dui(sql, opts={}, &block)
390
628
  super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
391
629
  end
392
-
393
- # Same as execute, explicit due to intricacies of alias and super.
394
- def execute_insert(sql, opts={}, &block)
395
- super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
396
- end
397
630
  end
398
631
 
399
632
  # Allow use of server side prepared statements for PostgreSQL using the
400
633
  # pg driver.
401
634
  module PreparedStatementMethods
402
635
  include BindArgumentMethods
403
- include ::Sequel::Postgres::DatasetMethods::PreparedStatementMethods
636
+
637
+ # Raise a more obvious error if you attempt to call a unnamed prepared statement.
638
+ def call(*)
639
+ raise Error, "Cannot call prepared statement without a name" if prepared_statement_name.nil?
640
+ super
641
+ end
404
642
 
405
643
  private
406
644
 
@@ -414,11 +652,6 @@ module Sequel
414
652
  def execute_dui(sql, opts={}, &block)
415
653
  super(prepared_statement_name, opts, &block)
416
654
  end
417
-
418
- # Same as execute, explicit due to intricacies of alias and super.
419
- def execute_insert(sql, opts={}, &block)
420
- super(prepared_statement_name, opts, &block)
421
- end
422
655
  end
423
656
 
424
657
  # Execute the given type of statement with the hash of values.
@@ -427,7 +660,7 @@ module Sequel
427
660
  ps.extend(BindArgumentMethods)
428
661
  ps.call(bind_vars, &block)
429
662
  end
430
-
663
+
431
664
  # Prepare the given type of statement with the given name, and store
432
665
  # it in the database to be called later.
433
666
  def prepare(type, name=nil, *values)
@@ -435,7 +668,7 @@ module Sequel
435
668
  ps.extend(PreparedStatementMethods)
436
669
  if name
437
670
  ps.prepared_statement_name = name
438
- db.prepared_statements[name] = ps
671
+ db.set_prepared_statement(name, ps)
439
672
  end
440
673
  ps
441
674
  end
@@ -452,7 +685,7 @@ module Sequel
452
685
  private
453
686
 
454
687
  # Use a cursor to fetch groups of records at a time, yielding them to the block.
455
- def cursor_fetch_rows(sql, &block)
688
+ def cursor_fetch_rows(sql)
456
689
  server_opts = {:server=>@opts[:server] || :read_only}
457
690
  db.transaction(server_opts) do
458
691
  begin
@@ -464,12 +697,12 @@ module Sequel
464
697
  # Load columns only in the first fetch, so subsequent fetches are faster
465
698
  execute(fetch_sql) do |res|
466
699
  cols = fetch_rows_set_cols(res)
467
- yield_hash_rows(res, cols, &block)
700
+ yield_hash_rows(res, cols){|h| yield h}
468
701
  return if res.ntuples < rows_per_fetch
469
702
  end
470
703
  loop do
471
704
  execute(fetch_sql) do |res|
472
- yield_hash_rows(res, cols, &block)
705
+ yield_hash_rows(res, cols){|h| yield h}
473
706
  return if res.ntuples < rows_per_fetch
474
707
  end
475
708
  end
@@ -483,21 +716,22 @@ module Sequel
483
716
  # field numers, type conversion procs, and name symbol arrays.
484
717
  def fetch_rows_set_cols(res)
485
718
  cols = []
719
+ procs = db.conversion_procs
486
720
  res.nfields.times do |fieldnum|
487
- cols << [fieldnum, PG_TYPES[res.ftype(fieldnum)], output_identifier(res.fname(fieldnum))]
721
+ cols << [fieldnum, procs[res.ftype(fieldnum)], output_identifier(res.fname(fieldnum))]
488
722
  end
489
723
  @columns = cols.map{|c| c.at(2)}
490
724
  cols
491
725
  end
492
726
 
493
727
  # Use the driver's escape_bytea
494
- def literal_blob(v)
495
- db.synchronize{|c| "'#{c.escape_bytea(v)}'"}
728
+ def literal_blob_append(sql, v)
729
+ sql << APOS << db.synchronize{|c| c.escape_bytea(v)} << APOS
496
730
  end
497
731
 
498
732
  # Use the driver's escape_string
499
- def literal_string(v)
500
- db.synchronize{|c| "'#{c.escape_string(v)}'"}
733
+ def literal_string_append(sql, v)
734
+ sql << APOS << db.synchronize{|c| c.escape_string(v)} << APOS
501
735
  end
502
736
 
503
737
  # For each row in the result set, yield a hash with column name symbol