sequel 3.21.0 → 3.31.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 (225) hide show
  1. data/CHANGELOG +485 -3
  2. data/README.rdoc +20 -6
  3. data/Rakefile +37 -19
  4. data/bin/sequel +1 -5
  5. data/doc/association_basics.rdoc +249 -43
  6. data/doc/dataset_basics.rdoc +4 -4
  7. data/doc/dataset_filtering.rdoc +8 -0
  8. data/doc/mass_assignment.rdoc +54 -0
  9. data/doc/migration.rdoc +15 -538
  10. data/doc/model_hooks.rdoc +72 -27
  11. data/doc/opening_databases.rdoc +89 -50
  12. data/doc/prepared_statements.rdoc +40 -13
  13. data/doc/reflection.rdoc +8 -2
  14. data/doc/release_notes/3.22.0.txt +39 -0
  15. data/doc/release_notes/3.23.0.txt +172 -0
  16. data/doc/release_notes/3.24.0.txt +420 -0
  17. data/doc/release_notes/3.25.0.txt +88 -0
  18. data/doc/release_notes/3.26.0.txt +88 -0
  19. data/doc/release_notes/3.27.0.txt +82 -0
  20. data/doc/release_notes/3.28.0.txt +304 -0
  21. data/doc/release_notes/3.29.0.txt +459 -0
  22. data/doc/release_notes/3.30.0.txt +135 -0
  23. data/doc/release_notes/3.31.0.txt +146 -0
  24. data/doc/schema_modification.rdoc +547 -0
  25. data/doc/sharding.rdoc +7 -1
  26. data/doc/testing.rdoc +129 -0
  27. data/doc/transactions.rdoc +137 -0
  28. data/doc/virtual_rows.rdoc +2 -2
  29. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  30. data/lib/sequel/adapters/ado.rb +40 -18
  31. data/lib/sequel/adapters/amalgalite.rb +17 -9
  32. data/lib/sequel/adapters/db2.rb +175 -86
  33. data/lib/sequel/adapters/dbi.rb +15 -15
  34. data/lib/sequel/adapters/do/mysql.rb +5 -7
  35. data/lib/sequel/adapters/do/postgres.rb +0 -5
  36. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  37. data/lib/sequel/adapters/do.rb +17 -36
  38. data/lib/sequel/adapters/firebird.rb +27 -208
  39. data/lib/sequel/adapters/ibmdb.rb +448 -0
  40. data/lib/sequel/adapters/informix.rb +6 -23
  41. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  42. data/lib/sequel/adapters/jdbc/db2.rb +45 -0
  43. data/lib/sequel/adapters/jdbc/derby.rb +247 -0
  44. data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
  45. data/lib/sequel/adapters/jdbc/h2.rb +56 -29
  46. data/lib/sequel/adapters/jdbc/hsqldb.rb +170 -0
  47. data/lib/sequel/adapters/jdbc/informix.rb +26 -0
  48. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  49. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  50. data/lib/sequel/adapters/jdbc/mysql.rb +11 -11
  51. data/lib/sequel/adapters/jdbc/oracle.rb +81 -25
  52. data/lib/sequel/adapters/jdbc/postgresql.rb +9 -13
  53. data/lib/sequel/adapters/jdbc/sqlite.rb +2 -6
  54. data/lib/sequel/adapters/jdbc/sqlserver.rb +71 -0
  55. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  56. data/lib/sequel/adapters/jdbc.rb +231 -78
  57. data/lib/sequel/adapters/mock.rb +335 -0
  58. data/lib/sequel/adapters/mysql.rb +82 -184
  59. data/lib/sequel/adapters/mysql2.rb +26 -25
  60. data/lib/sequel/adapters/odbc/db2.rb +17 -0
  61. data/lib/sequel/adapters/odbc/mssql.rb +1 -6
  62. data/lib/sequel/adapters/odbc.rb +28 -9
  63. data/lib/sequel/adapters/openbase.rb +3 -5
  64. data/lib/sequel/adapters/oracle.rb +351 -53
  65. data/lib/sequel/adapters/postgres.rb +194 -33
  66. data/lib/sequel/adapters/shared/access.rb +32 -9
  67. data/lib/sequel/adapters/shared/db2.rb +321 -0
  68. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  69. data/lib/sequel/adapters/shared/informix.rb +53 -0
  70. data/lib/sequel/adapters/shared/mssql.rb +231 -106
  71. data/lib/sequel/adapters/shared/mysql.rb +181 -63
  72. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
  73. data/lib/sequel/adapters/shared/oracle.rb +231 -41
  74. data/lib/sequel/adapters/shared/postgres.rb +214 -82
  75. data/lib/sequel/adapters/shared/progress.rb +4 -7
  76. data/lib/sequel/adapters/shared/sqlite.rb +201 -57
  77. data/lib/sequel/adapters/sqlite.rb +29 -21
  78. data/lib/sequel/adapters/swift/mysql.rb +12 -7
  79. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  80. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  81. data/lib/sequel/adapters/swift.rb +5 -5
  82. data/lib/sequel/adapters/tinytds.rb +160 -19
  83. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  84. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  85. data/lib/sequel/ast_transformer.rb +190 -0
  86. data/lib/sequel/connection_pool/threaded.rb +3 -2
  87. data/lib/sequel/connection_pool.rb +1 -1
  88. data/lib/sequel/core.rb +46 -5
  89. data/lib/sequel/database/connecting.rb +5 -6
  90. data/lib/sequel/database/dataset.rb +4 -4
  91. data/lib/sequel/database/dataset_defaults.rb +59 -1
  92. data/lib/sequel/database/logging.rb +1 -1
  93. data/lib/sequel/database/misc.rb +104 -19
  94. data/lib/sequel/database/query.rb +153 -58
  95. data/lib/sequel/database/schema_generator.rb +17 -7
  96. data/lib/sequel/database/schema_methods.rb +59 -29
  97. data/lib/sequel/dataset/actions.rb +231 -76
  98. data/lib/sequel/dataset/features.rb +91 -8
  99. data/lib/sequel/dataset/graph.rb +38 -11
  100. data/lib/sequel/dataset/misc.rb +50 -23
  101. data/lib/sequel/dataset/mutation.rb +5 -6
  102. data/lib/sequel/dataset/prepared_statements.rb +48 -27
  103. data/lib/sequel/dataset/query.rb +236 -43
  104. data/lib/sequel/dataset/sql.rb +665 -253
  105. data/lib/sequel/dataset.rb +8 -0
  106. data/lib/sequel/exceptions.rb +4 -0
  107. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  108. data/lib/sequel/extensions/migration.rb +6 -4
  109. data/lib/sequel/extensions/named_timezones.rb +5 -0
  110. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  111. data/lib/sequel/extensions/to_dot.rb +95 -83
  112. data/lib/sequel/model/associations.rb +1020 -337
  113. data/lib/sequel/model/base.rb +414 -132
  114. data/lib/sequel/model/errors.rb +1 -1
  115. data/lib/sequel/model/exceptions.rb +5 -1
  116. data/lib/sequel/model.rb +15 -8
  117. data/lib/sequel/plugins/association_pks.rb +22 -4
  118. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  119. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  120. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  121. data/lib/sequel/plugins/force_encoding.rb +6 -6
  122. data/lib/sequel/plugins/identity_map.rb +114 -7
  123. data/lib/sequel/plugins/many_through_many.rb +65 -7
  124. data/lib/sequel/plugins/prepared_statements.rb +151 -0
  125. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  126. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  127. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  128. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  129. data/lib/sequel/plugins/serialization.rb +88 -44
  130. data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
  131. data/lib/sequel/plugins/sharding.rb +21 -54
  132. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  133. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  134. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  135. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  136. data/lib/sequel/sql.rb +109 -52
  137. data/lib/sequel/timezones.rb +57 -38
  138. data/lib/sequel/version.rb +1 -1
  139. data/spec/adapters/db2_spec.rb +146 -0
  140. data/spec/adapters/mssql_spec.rb +111 -29
  141. data/spec/adapters/mysql_spec.rb +135 -123
  142. data/spec/adapters/oracle_spec.rb +49 -77
  143. data/spec/adapters/postgres_spec.rb +267 -78
  144. data/spec/adapters/spec_helper.rb +6 -5
  145. data/spec/adapters/sqlite_spec.rb +35 -21
  146. data/spec/core/connection_pool_spec.rb +71 -92
  147. data/spec/core/core_sql_spec.rb +21 -30
  148. data/spec/core/database_spec.rb +788 -512
  149. data/spec/core/dataset_spec.rb +1073 -1053
  150. data/spec/core/expression_filters_spec.rb +159 -42
  151. data/spec/core/mock_adapter_spec.rb +419 -0
  152. data/spec/core/object_graph_spec.rb +86 -114
  153. data/spec/core/schema_generator_spec.rb +3 -3
  154. data/spec/core/schema_spec.rb +298 -38
  155. data/spec/core/spec_helper.rb +6 -48
  156. data/spec/extensions/association_autoreloading_spec.rb +1 -10
  157. data/spec/extensions/association_dependencies_spec.rb +2 -12
  158. data/spec/extensions/association_pks_spec.rb +66 -32
  159. data/spec/extensions/caching_spec.rb +23 -50
  160. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  161. data/spec/extensions/columns_introspection_spec.rb +91 -0
  162. data/spec/extensions/composition_spec.rb +18 -13
  163. data/spec/extensions/dataset_associations_spec.rb +199 -0
  164. data/spec/extensions/defaults_setter_spec.rb +64 -0
  165. data/spec/extensions/hook_class_methods_spec.rb +65 -91
  166. data/spec/extensions/identity_map_spec.rb +114 -22
  167. data/spec/extensions/instance_filters_spec.rb +11 -21
  168. data/spec/extensions/instance_hooks_spec.rb +53 -0
  169. data/spec/extensions/json_serializer_spec.rb +4 -5
  170. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  171. data/spec/extensions/list_spec.rb +17 -39
  172. data/spec/extensions/many_through_many_spec.rb +283 -250
  173. data/spec/extensions/migration_spec.rb +35 -32
  174. data/spec/extensions/named_timezones_spec.rb +23 -3
  175. data/spec/extensions/nested_attributes_spec.rb +97 -88
  176. data/spec/extensions/optimistic_locking_spec.rb +9 -20
  177. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  178. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  179. data/spec/extensions/prepared_statements_spec.rb +53 -0
  180. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  181. data/spec/extensions/pretty_table_spec.rb +1 -6
  182. data/spec/extensions/rcte_tree_spec.rb +41 -43
  183. data/spec/extensions/schema_dumper_spec.rb +5 -8
  184. data/spec/extensions/schema_spec.rb +13 -21
  185. data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
  186. data/spec/extensions/serialization_spec.rb +72 -43
  187. data/spec/extensions/sharding_spec.rb +66 -140
  188. data/spec/extensions/single_table_inheritance_spec.rb +18 -29
  189. data/spec/extensions/spec_helper.rb +13 -61
  190. data/spec/extensions/sql_expr_spec.rb +20 -60
  191. data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
  192. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  193. data/spec/extensions/timestamps_spec.rb +6 -6
  194. data/spec/extensions/to_dot_spec.rb +4 -7
  195. data/spec/extensions/touch_spec.rb +13 -14
  196. data/spec/extensions/tree_spec.rb +11 -26
  197. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  198. data/spec/extensions/update_primary_key_spec.rb +30 -24
  199. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  200. data/spec/extensions/validation_helpers_spec.rb +16 -35
  201. data/spec/extensions/xml_serializer_spec.rb +16 -4
  202. data/spec/integration/associations_test.rb +537 -21
  203. data/spec/integration/database_test.rb +4 -2
  204. data/spec/integration/dataset_test.rb +461 -65
  205. data/spec/integration/eager_loader_test.rb +19 -21
  206. data/spec/integration/model_test.rb +80 -1
  207. data/spec/integration/plugin_test.rb +304 -116
  208. data/spec/integration/prepared_statement_test.rb +212 -122
  209. data/spec/integration/schema_test.rb +161 -30
  210. data/spec/integration/spec_helper.rb +39 -30
  211. data/spec/integration/timezone_test.rb +38 -12
  212. data/spec/integration/transaction_test.rb +172 -5
  213. data/spec/integration/type_test.rb +17 -3
  214. data/spec/model/association_reflection_spec.rb +201 -7
  215. data/spec/model/associations_spec.rb +865 -662
  216. data/spec/model/base_spec.rb +275 -95
  217. data/spec/model/dataset_methods_spec.rb +7 -27
  218. data/spec/model/eager_loading_spec.rb +677 -676
  219. data/spec/model/hooks_spec.rb +309 -67
  220. data/spec/model/model_spec.rb +222 -168
  221. data/spec/model/plugins_spec.rb +24 -13
  222. data/spec/model/record_spec.rb +381 -218
  223. data/spec/model/spec_helper.rb +13 -71
  224. data/spec/model/validations_spec.rb +11 -0
  225. metadata +115 -73
@@ -93,13 +93,14 @@ module Sequel
93
93
  def bytea(s) ::Sequel::SQL::Blob.new(Adapter.unescape_bytea(s)) end
94
94
  def integer(s) s.to_i end
95
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
96
+ def date(s) ::Date.new(*s.split("-").map{|x| x.to_i}) end
101
97
  end.new
102
98
 
99
+ # Hash with type name symbols and callable values for converting PostgreSQL types.
100
+ # Non-builtin types that don't have fixed numbers should use this to register
101
+ # conversion procs.
102
+ PG_NAMED_TYPES = {}
103
+
103
104
  # Hash with integer keys and callable values for converting PostgreSQL types.
104
105
  PG_TYPES = {}
105
106
  {
@@ -107,9 +108,8 @@ module Sequel
107
108
  [17] => tt.method(:bytea),
108
109
  [20, 21, 22, 23, 26] => tt.method(:integer),
109
110
  [700, 701] => tt.method(:float),
110
- [790, 1700] => tt.method(:numeric),
111
- [1083, 1266] => tt.method(:time),
112
- [1114, 1184] => tt.method(:timestamp)
111
+ [790, 1700] => ::BigDecimal.method(:new),
112
+ [1083, 1266] => ::Sequel.method(:string_to_time),
113
113
  }.each do |k,v|
114
114
  k.each{|n| PG_TYPES[n] = v}
115
115
  end
@@ -123,7 +123,7 @@ module Sequel
123
123
 
124
124
  # Modify the type translator for the date type depending on the value given.
125
125
  def self.use_iso_date_format=(v)
126
- PG_TYPES[1082] = TYPE_TRANSLATOR.method(v ? :date_iso : :date)
126
+ PG_TYPES[1082] = v ? TYPE_TRANSLATOR.method(:date) : Sequel.method(:string_to_date)
127
127
  @use_iso_date_format = v
128
128
  end
129
129
  self.use_iso_date_format = true
@@ -131,7 +131,10 @@ module Sequel
131
131
  # PGconn subclass for connection specific methods used with the
132
132
  # pg, postgres, or postgres-pr driver.
133
133
  class Adapter < ::PGconn
134
+ DISCONNECT_ERROR_RE = /\Acould not receive data from server: Software caused connection abort/
135
+
134
136
  include Sequel::Postgres::AdapterMethods
137
+
135
138
  self.translate_results = false if respond_to?(:translate_results=)
136
139
 
137
140
  # Hash of prepared statements for this connection. Keys are
@@ -150,20 +153,23 @@ module Sequel
150
153
  end
151
154
  @prepared_statements = {} if SEQUEL_POSTGRES_USES_PG
152
155
  end
153
-
156
+
154
157
  # Raise a Sequel::DatabaseDisconnectError if a PGError is raised and
155
158
  # the connection status cannot be determined or it is not OK.
156
159
  def check_disconnect_errors
157
160
  begin
158
161
  yield
159
162
  rescue PGError =>e
163
+ disconnect = false
160
164
  begin
161
165
  s = status
162
166
  rescue PGError
163
- raise Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError)
167
+ disconnect = true
164
168
  end
165
169
  status_ok = (s == Adapter::CONNECTION_OK)
166
- status_ok ? raise : raise(Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError))
170
+ disconnect ||= !status_ok
171
+ disconnect ||= e.message =~ DISCONNECT_ERROR_RE
172
+ disconnect ? raise(Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError)) : raise
167
173
  ensure
168
174
  block if status_ok
169
175
  end
@@ -176,7 +182,7 @@ module Sequel
176
182
  begin
177
183
  block_given? ? yield(q) : q.cmd_tuples
178
184
  ensure
179
- q.clear
185
+ q.clear if q
180
186
  end
181
187
  end
182
188
 
@@ -194,6 +200,10 @@ module Sequel
194
200
  include Sequel::Postgres::DatabaseMethods
195
201
 
196
202
  set_adapter_scheme :postgres
203
+
204
+ # A hash of conversion procs, keyed by type integer (oid) and
205
+ # having callable values for the conversion proc for that type.
206
+ attr_reader :conversion_procs
197
207
 
198
208
  # Add the primary_keys and primary_key_sequences instance variables,
199
209
  # so we can get the correct return values for inserted rows.
@@ -208,14 +218,27 @@ module Sequel
208
218
  # client encoding for the connection.
209
219
  def connect(server)
210
220
  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
- )
221
+ conn = if SEQUEL_POSTGRES_USES_PG
222
+ connection_params = {
223
+ :host => opts[:host],
224
+ :port => opts[:port] || 5432,
225
+ :tty => '',
226
+ :dbname => opts[:database],
227
+ :user => opts[:user],
228
+ :password => opts[:password],
229
+ :connect_timeout => opts[:connect_timeout] || 20
230
+ }.delete_if { |key, value| blank_object?(value) }
231
+ Adapter.connect(connection_params)
232
+ else
233
+ Adapter.connect(
234
+ (opts[:host] unless blank_object?(opts[:host])),
235
+ opts[:port] || 5432,
236
+ nil, '',
237
+ opts[:database],
238
+ opts[:user],
239
+ opts[:password]
240
+ )
241
+ end
219
242
  if encoding = opts[:encoding] || opts[:charset]
220
243
  if conn.respond_to?(:set_client_encoding)
221
244
  conn.set_client_encoding(encoding)
@@ -225,14 +248,10 @@ module Sequel
225
248
  end
226
249
  conn.db = self
227
250
  conn.apply_connection_settings
251
+ @conversion_procs ||= get_conversion_procs(conn)
228
252
  conn
229
253
  end
230
254
 
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
255
  # Execute the given SQL with the given args on an available connection.
237
256
  def execute(sql, opts={}, &block)
238
257
  check_database_errors do
@@ -253,6 +272,119 @@ module Sequel
253
272
  end
254
273
  end
255
274
 
275
+ if SEQUEL_POSTGRES_USES_PG
276
+ # +copy_table+ uses PostgreSQL's +COPY+ SQL statement to return formatted
277
+ # results directly to the caller. This method is only supported if pg is the
278
+ # underlying ruby driver. This method should only be called if you want
279
+ # results returned to the client. If you are using +COPY FROM+ or +COPY TO+
280
+ # with a filename, you should just use +run+ instead of this method. This
281
+ # method does not currently support +COPY FROM STDIN+, but that may be supported
282
+ # in the future.
283
+ #
284
+ # The table argument supports the following types:
285
+ #
286
+ # String :: Uses the first argument directly as literal SQL. If you are using
287
+ # a version of PostgreSQL before 9.0, you will probably want to
288
+ # use a string if you are using any options at all, as the syntax
289
+ # Sequel uses for options is only compatible with PostgreSQL 9.0+.
290
+ # Dataset :: Uses a query instead of a table name when copying.
291
+ # other :: Uses a table name (usually a symbol) when copying.
292
+ #
293
+ # The following options are respected:
294
+ #
295
+ # :format :: The format to use. text is the default, so this should be :csv or :binary.
296
+ # :options :: An options SQL string to use, which should contain comma separated options.
297
+ # :server :: The server on which to run the query.
298
+ #
299
+ # If a block is provided, the method continually yields to the block, one yield
300
+ # per row. If a block is not provided, a single string is returned with all
301
+ # of the data.
302
+ def copy_table(table, opts={})
303
+ sql = if table.is_a?(String)
304
+ sql = table
305
+ else
306
+ if opts[:options] || opts[:format]
307
+ options = " ("
308
+ options << "FORMAT #{opts[:format]}" if opts[:format]
309
+ options << "#{', ' if opts[:format]}#{opts[:options]}" if opts[:options]
310
+ options << ')'
311
+ end
312
+ table = if table.is_a?(::Sequel::Dataset)
313
+ "(#{table.sql})"
314
+ else
315
+ literal(table)
316
+ end
317
+ sql = "COPY #{table} TO STDOUT#{options}"
318
+ end
319
+ synchronize(opts[:server]) do |conn|
320
+ conn.execute(sql)
321
+ begin
322
+ if block_given?
323
+ while buf = conn.get_copy_data
324
+ yield buf
325
+ end
326
+ nil
327
+ else
328
+ b = ''
329
+ b << buf while buf = conn.get_copy_data
330
+ b
331
+ end
332
+ ensure
333
+ raise DatabaseDisconnectError, "disconnecting as a partial COPY may leave the connection in an unusable state" if buf
334
+ end
335
+ end
336
+ end
337
+
338
+ # Listens on the given channel (or multiple channels if channel is an array), waiting for notifications.
339
+ # After a notification is received, or the timeout has passed, stops listening to the channel. Options:
340
+ #
341
+ # :after_listen :: An object that responds to +call+ that is called with the underlying connection after the LISTEN
342
+ # statement is sent, but before the connection starts waiting for notifications.
343
+ # :loop :: Whether to continually wait for notifications, instead of just waiting for a single
344
+ # notification. If this option is given, a block must be provided. If this object responds to call, it is
345
+ # called with the underlying connection after each notification is received (after the block is called).
346
+ # If a :timeout option is used, and a callable object is given, the object will also be called if the
347
+ # timeout expires. If :loop is used and you want to stop listening, you can either break from inside the
348
+ # block given to #listen, or you can throw :stop from inside the :loop object's call method or the block.
349
+ # :server :: The server on which to listen, if the sharding support is being used.
350
+ # :timeout :: How long to wait for a notification, in seconds (can provide a float value for
351
+ # fractional seconds). If not given or nil, waits indefinitely.
352
+ #
353
+ # This method is only supported if pg is used as the underlying ruby driver. It returns the
354
+ # channel the notification was sent to (as a string), unless :loop was used, in which case it returns nil.
355
+ # If a block is given, it is yielded 3 arguments:
356
+ # * the channel the notification was sent to (as a string)
357
+ # * the backend pid of the notifier (as an integer),
358
+ # * and the payload of the notification (as a string or nil).
359
+ def listen(channels, opts={}, &block)
360
+ check_database_errors do
361
+ synchronize(opts[:server]) do |conn|
362
+ begin
363
+ channels = Array(channels)
364
+ channels.each{|channel| conn.execute("LISTEN #{channel}")}
365
+ opts[:after_listen].call(conn) if opts[:after_listen]
366
+ timeout = opts[:timeout] ? [opts[:timeout]] : []
367
+ if l = opts[:loop]
368
+ raise Error, 'calling #listen with :loop requires a block' unless block
369
+ loop_call = l.respond_to?(:call)
370
+ catch(:stop) do
371
+ loop do
372
+ conn.wait_for_notify(*timeout, &block)
373
+ l.call(conn) if loop_call
374
+ end
375
+ end
376
+ nil
377
+ else
378
+ conn.wait_for_notify(*timeout, &block)
379
+ end
380
+ ensure
381
+ conn.execute("UNLISTEN *")
382
+ end
383
+ end
384
+ end
385
+ end
386
+ end
387
+
256
388
  private
257
389
 
258
390
  # Convert exceptions raised from the block into DatabaseErrors.
@@ -305,12 +437,29 @@ module Sequel
305
437
  end
306
438
  end
307
439
  end
440
+
441
+ # Return the conversion procs hash to use for this database
442
+ def get_conversion_procs(conn)
443
+ procs = PG_TYPES.dup
444
+ procs[1114] = method(:to_application_timestamp)
445
+ procs[1184] = method(:to_application_timestamp)
446
+ conn.execute("SELECT oid, typname FROM pg_type where typtype = 'b'") do |res|
447
+ res.ntuples.times do |recnum|
448
+ if pr = PG_NAMED_TYPES[res.getvalue(recnum, 1).untaint.to_sym]
449
+ procs[res.getvalue(recnum, 0).to_i] ||= pr
450
+ end
451
+ end
452
+ end
453
+ procs
454
+ end
308
455
  end
309
456
 
310
457
  # Dataset class for PostgreSQL datasets that use the pg, postgres, or
311
458
  # postgres-pr driver.
312
459
  class Dataset < Sequel::Dataset
313
460
  include Sequel::Postgres::DatasetMethods
461
+
462
+ Database::DatasetClass = self
314
463
 
315
464
  # Yield all rows returned by executing the given SQL and converting
316
465
  # the types.
@@ -372,11 +521,17 @@ module Sequel
372
521
  end
373
522
  LiteralString.new("#{prepared_arg_placeholder}#{i}#{"::#{type}" if type}")
374
523
  end
524
+
525
+ # Always assume a prepared argument.
526
+ def prepared_arg?(k)
527
+ true
528
+ end
375
529
  end
376
530
 
377
531
  # Allow use of bind arguments for PostgreSQL using the pg driver.
378
532
  module BindArgumentMethods
379
533
  include ArgumentMapper
534
+ include ::Sequel::Postgres::DatasetMethods::PreparedStatementMethods
380
535
 
381
536
  private
382
537
 
@@ -400,7 +555,12 @@ module Sequel
400
555
  # pg driver.
401
556
  module PreparedStatementMethods
402
557
  include BindArgumentMethods
403
- include ::Sequel::Postgres::DatasetMethods::PreparedStatementMethods
558
+
559
+ # Raise a more obvious error if you attempt to call a unnamed prepared statement.
560
+ def call(*)
561
+ raise Error, "Cannot call prepared statement without a name" if prepared_statement_name.nil?
562
+ super
563
+ end
404
564
 
405
565
  private
406
566
 
@@ -427,7 +587,7 @@ module Sequel
427
587
  ps.extend(BindArgumentMethods)
428
588
  ps.call(bind_vars, &block)
429
589
  end
430
-
590
+
431
591
  # Prepare the given type of statement with the given name, and store
432
592
  # it in the database to be called later.
433
593
  def prepare(type, name=nil, *values)
@@ -483,21 +643,22 @@ module Sequel
483
643
  # field numers, type conversion procs, and name symbol arrays.
484
644
  def fetch_rows_set_cols(res)
485
645
  cols = []
646
+ procs = db.conversion_procs
486
647
  res.nfields.times do |fieldnum|
487
- cols << [fieldnum, PG_TYPES[res.ftype(fieldnum)], output_identifier(res.fname(fieldnum))]
648
+ cols << [fieldnum, procs[res.ftype(fieldnum)], output_identifier(res.fname(fieldnum))]
488
649
  end
489
650
  @columns = cols.map{|c| c.at(2)}
490
651
  cols
491
652
  end
492
653
 
493
654
  # Use the driver's escape_bytea
494
- def literal_blob(v)
495
- db.synchronize{|c| "'#{c.escape_bytea(v)}'"}
655
+ def literal_blob_append(sql, v)
656
+ sql << "'" << db.synchronize{|c| c.escape_bytea(v)} << "'"
496
657
  end
497
658
 
498
659
  # Use the driver's escape_string
499
- def literal_string(v)
500
- db.synchronize{|c| "'#{c.escape_string(v)}'"}
660
+ def literal_string_append(sql, v)
661
+ sql << "'" << db.synchronize{|c| c.escape_string(v)} << "'"
501
662
  end
502
663
 
503
664
  # For each row in the result set, yield a hash with column name symbol
@@ -1,21 +1,17 @@
1
1
  module Sequel
2
2
  module Access
3
3
  module DatabaseMethods
4
+ # Access uses type :access as the database_type
4
5
  def database_type
5
6
  :access
6
7
  end
7
8
 
8
- def dataset(opts = nil)
9
- ds = super
10
- ds.extend(DatasetMethods)
11
- ds
12
- end
13
-
14
9
  # Doesn't work, due to security restrictions on MSysObjects
15
10
  def tables
16
11
  from(:MSysObjects).filter(:Type=>1, :Flags=>0).select_map(:Name).map{|x| x.to_sym}
17
12
  end
18
13
 
14
+ # Access uses type Counter for an autoincrementing keys
19
15
  def serial_primary_key_options
20
16
  {:primary_key => true, :type=>:Counter}
21
17
  end
@@ -32,18 +28,45 @@ module Sequel
32
28
  end
33
29
 
34
30
  module DatasetMethods
35
- SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'limit distinct columns from join where group order having compounds')
31
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select limit distinct columns from join where group order having compounds')
32
+ DATE_FORMAT = '#%Y-%m-%d#'.freeze
33
+ TIMESTAMP_FORMAT = '#%Y-%m-%d %H:%M:%S#'.freeze
34
+ TOP = " TOP ".freeze
35
+ BRACKET_CLOSE = Dataset::BRACKET_CLOSE
36
+ BRACKET_OPEN = Dataset::BRACKET_OPEN
36
37
 
38
+ # Access doesn't support INTERSECT or EXCEPT
37
39
  def supports_intersect_except?
38
40
  false
39
41
  end
40
42
 
41
43
  private
42
44
 
43
- def quoted_identifier(v)
44
- "[#{v}]"
45
+ # Access uses # to quote dates
46
+ def literal_date(d)
47
+ d.strftime(DATE_FORMAT)
48
+ end
49
+
50
+ # Access uses # to quote datetimes
51
+ def literal_datetime(t)
52
+ t.strftime(TIMESTAMP_FORMAT)
53
+ end
54
+ alias literal_time literal_datetime
55
+
56
+ # Access uses TOP for limits
57
+ def select_limit_sql(sql)
58
+ if l = @opts[:limit]
59
+ sql << TOP
60
+ literal_append(sql, l)
61
+ end
62
+ end
63
+
64
+ # Access uses [] for quoting identifiers
65
+ def quoted_identifier_append(sql, v)
66
+ sql << BRACKET_OPEN << v.to_s << BRACKET_CLOSE
45
67
  end
46
68
 
69
+ # Access requires the limit clause come before other clauses
47
70
  def select_clause_methods
48
71
  SELECT_CLAUSE_METHODS
49
72
  end