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