sequel 3.21.0 → 3.36.0

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