sequel 3.4.0 → 3.5.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 (93) hide show
  1. data/CHANGELOG +84 -0
  2. data/Rakefile +1 -1
  3. data/doc/cheat_sheet.rdoc +5 -2
  4. data/doc/opening_databases.rdoc +2 -0
  5. data/doc/release_notes/3.5.0.txt +510 -0
  6. data/lib/sequel/adapters/ado.rb +3 -1
  7. data/lib/sequel/adapters/ado/mssql.rb +2 -2
  8. data/lib/sequel/adapters/do.rb +2 -11
  9. data/lib/sequel/adapters/do/mysql.rb +7 -0
  10. data/lib/sequel/adapters/do/postgres.rb +2 -2
  11. data/lib/sequel/adapters/firebird.rb +3 -3
  12. data/lib/sequel/adapters/informix.rb +3 -3
  13. data/lib/sequel/adapters/jdbc/h2.rb +3 -3
  14. data/lib/sequel/adapters/jdbc/mssql.rb +7 -0
  15. data/lib/sequel/adapters/mysql.rb +60 -21
  16. data/lib/sequel/adapters/odbc.rb +1 -1
  17. data/lib/sequel/adapters/openbase.rb +3 -3
  18. data/lib/sequel/adapters/oracle.rb +1 -5
  19. data/lib/sequel/adapters/postgres.rb +3 -3
  20. data/lib/sequel/adapters/shared/mssql.rb +142 -33
  21. data/lib/sequel/adapters/shared/mysql.rb +54 -31
  22. data/lib/sequel/adapters/shared/oracle.rb +17 -6
  23. data/lib/sequel/adapters/shared/postgres.rb +7 -7
  24. data/lib/sequel/adapters/shared/progress.rb +3 -3
  25. data/lib/sequel/adapters/shared/sqlite.rb +3 -17
  26. data/lib/sequel/connection_pool.rb +4 -6
  27. data/lib/sequel/core.rb +29 -113
  28. data/lib/sequel/database.rb +14 -12
  29. data/lib/sequel/dataset.rb +8 -21
  30. data/lib/sequel/dataset/convenience.rb +1 -1
  31. data/lib/sequel/dataset/graph.rb +9 -2
  32. data/lib/sequel/dataset/sql.rb +170 -104
  33. data/lib/sequel/exceptions.rb +3 -0
  34. data/lib/sequel/extensions/looser_typecasting.rb +21 -0
  35. data/lib/sequel/extensions/named_timezones.rb +61 -0
  36. data/lib/sequel/extensions/schema_dumper.rb +7 -1
  37. data/lib/sequel/extensions/sql_expr.rb +122 -0
  38. data/lib/sequel/extensions/string_date_time.rb +4 -4
  39. data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
  40. data/lib/sequel/model/associations.rb +105 -45
  41. data/lib/sequel/model/base.rb +37 -28
  42. data/lib/sequel/plugins/active_model.rb +35 -0
  43. data/lib/sequel/plugins/association_dependencies.rb +96 -0
  44. data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
  45. data/lib/sequel/plugins/force_encoding.rb +61 -0
  46. data/lib/sequel/plugins/many_through_many.rb +32 -11
  47. data/lib/sequel/plugins/nested_attributes.rb +7 -2
  48. data/lib/sequel/plugins/subclasses.rb +45 -0
  49. data/lib/sequel/plugins/touch.rb +118 -0
  50. data/lib/sequel/plugins/typecast_on_load.rb +61 -0
  51. data/lib/sequel/sql.rb +31 -30
  52. data/lib/sequel/timezones.rb +161 -0
  53. data/lib/sequel/version.rb +1 -1
  54. data/spec/adapters/mssql_spec.rb +262 -0
  55. data/spec/adapters/mysql_spec.rb +46 -8
  56. data/spec/adapters/postgres_spec.rb +6 -3
  57. data/spec/adapters/spec_helper.rb +21 -0
  58. data/spec/adapters/sqlite_spec.rb +1 -1
  59. data/spec/core/connection_pool_spec.rb +1 -1
  60. data/spec/core/database_spec.rb +27 -1
  61. data/spec/core/dataset_spec.rb +63 -1
  62. data/spec/core/object_graph_spec.rb +1 -1
  63. data/spec/core/schema_spec.rb +1 -0
  64. data/spec/extensions/active_model_spec.rb +47 -0
  65. data/spec/extensions/association_dependencies_spec.rb +108 -0
  66. data/spec/extensions/class_table_inheritance_spec.rb +252 -0
  67. data/spec/extensions/force_encoding_spec.rb +75 -0
  68. data/spec/extensions/looser_typecasting_spec.rb +39 -0
  69. data/spec/extensions/many_through_many_spec.rb +60 -2
  70. data/spec/extensions/named_timezones_spec.rb +72 -0
  71. data/spec/extensions/nested_attributes_spec.rb +29 -1
  72. data/spec/extensions/schema_dumper_spec.rb +10 -0
  73. data/spec/extensions/spec_helper.rb +1 -1
  74. data/spec/extensions/sql_expr_spec.rb +89 -0
  75. data/spec/extensions/subclasses_spec.rb +52 -0
  76. data/spec/extensions/thread_local_timezones_spec.rb +45 -0
  77. data/spec/extensions/touch_spec.rb +155 -0
  78. data/spec/extensions/typecast_on_load_spec.rb +60 -0
  79. data/spec/integration/database_test.rb +8 -0
  80. data/spec/integration/dataset_test.rb +9 -9
  81. data/spec/integration/plugin_test.rb +139 -0
  82. data/spec/integration/schema_test.rb +7 -7
  83. data/spec/integration/spec_helper.rb +32 -1
  84. data/spec/integration/timezone_test.rb +3 -3
  85. data/spec/integration/transaction_test.rb +1 -1
  86. data/spec/integration/type_test.rb +6 -6
  87. data/spec/model/association_reflection_spec.rb +18 -0
  88. data/spec/model/associations_spec.rb +169 -9
  89. data/spec/model/base_spec.rb +2 -0
  90. data/spec/model/eager_loading_spec.rb +82 -2
  91. data/spec/model/model_spec.rb +8 -1
  92. data/spec/model/record_spec.rb +52 -9
  93. metadata +33 -23
@@ -97,7 +97,7 @@ module Sequel
97
97
  opts[:null] = o == :set_column_null ? op[:null] : opts[:allow_null]
98
98
  opts[:default] = o == :set_column_default ? op[:default] : opts[:ruby_default]
99
99
  opts.delete(:default) if opts[:default] == nil
100
- "ALTER TABLE #{quote_schema_table(table)} CHANGE COLUMN #{quote_identifier(op[:name])} #{column_definition_sql(opts)}"
100
+ "ALTER TABLE #{quote_schema_table(table)} CHANGE COLUMN #{quote_identifier(op[:name])} #{column_definition_sql(op.merge(opts))}"
101
101
  when :drop_index
102
102
  "#{drop_index_sql(table, op)} ON #{quote_schema_table(table)}"
103
103
  else
@@ -199,7 +199,10 @@ module Sequel
199
199
  BOOL_TRUE = '1'.freeze
200
200
  BOOL_FALSE = '0'.freeze
201
201
  COMMA_SEPARATOR = ', '.freeze
202
- SELECT_CLAUSE_ORDER = %w'distinct columns from join where group having compounds order limit'.freeze
202
+ DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'from where order limit')
203
+ INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'ignore into columns values on_duplicate_key_update')
204
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'distinct columns from join where group having compounds order limit')
205
+ UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'table set where order limit')
203
206
 
204
207
  # MySQL specific syntax for LIKE/REGEXP searches, as well as
205
208
  # string concatenation.
@@ -217,14 +220,6 @@ module Sequel
217
220
  super(op, args)
218
221
  end
219
222
  end
220
-
221
- # MySQL supports ORDER and LIMIT clauses in DELETE statements.
222
- def delete_sql
223
- sql = super
224
- sql << " ORDER BY #{expression_list(opts[:order])}" if opts[:order]
225
- sql << " LIMIT #{opts[:limit]}" if opts[:limit]
226
- sql
227
- end
228
223
 
229
224
  # Adds full text filter
230
225
  def full_text_search(cols, terms, opts = {})
@@ -305,8 +300,7 @@ module Sequel
305
300
 
306
301
  # MySQL specific syntax for inserting multiple values at once.
307
302
  def multi_insert_sql(columns, values)
308
- values = values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)
309
- ["#{insert_sql_base}#{source_list(@opts[:from])} (#{identifier_list(columns)}) VALUES #{values}#{insert_sql_suffix}"]
303
+ [insert_sql(columns, LiteralString.new('VALUES ' + values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)))]
310
304
  end
311
305
 
312
306
  # MySQL uses the nonstandard ` (backtick) for quoting identifiers.
@@ -367,31 +361,55 @@ module Sequel
367
361
  false
368
362
  end
369
363
 
370
- # MySQL supports ORDER and LIMIT clauses in UPDATE statements.
371
- def update_sql(values)
372
- sql = super
373
- sql << " ORDER BY #{expression_list(opts[:order])}" if opts[:order]
374
- sql << " LIMIT #{opts[:limit]}" if opts[:limit]
375
- sql
364
+ private
365
+
366
+ # MySQL supports the ORDER BY and LIMIT clauses for DELETE statements
367
+ def delete_clause_methods
368
+ DELETE_CLAUSE_METHODS
376
369
  end
377
370
 
378
- private
371
+ # MySQL supports the IGNORE and ON DUPLICATE KEY UPDATE clauses for INSERT statements
372
+ def insert_clause_methods
373
+ INSERT_CLAUSE_METHODS
374
+ end
375
+
376
+ # MySQL doesn't use the SQL standard DEFAULT VALUES.
377
+ def insert_columns_sql(sql)
378
+ values = opts[:values]
379
+ if values.is_a?(Array) && values.empty?
380
+ sql << " ()"
381
+ else
382
+ super
383
+ end
384
+ end
379
385
 
380
386
  # MySQL supports INSERT IGNORE INTO
381
- def insert_sql_base
382
- "INSERT #{'IGNORE ' if opts[:insert_ignore]}INTO "
387
+ def insert_ignore_sql(sql)
388
+ sql << " IGNORE" if opts[:insert_ignore]
383
389
  end
384
390
 
385
391
  # MySQL supports INSERT ... ON DUPLICATE KEY UPDATE
386
- def insert_sql_suffix
387
- on_duplicate_key_update_sql if opts[:on_duplicate_key_update]
392
+ def insert_on_duplicate_key_update_sql(sql)
393
+ sql << on_duplicate_key_update_sql if opts[:on_duplicate_key_update]
388
394
  end
389
395
 
390
- # MySQL doesn't use the SQL standard DEFAULT VALUES.
391
- def insert_default_values_sql
392
- "#{insert_sql_base}#{source_list(@opts[:from])} () VALUES ()"
396
+ # MySQL doesn't use the standard DEFAULT VALUES for empty values.
397
+ def insert_values_sql(sql)
398
+ values = opts[:values]
399
+ if values.is_a?(Array) && values.empty?
400
+ sql << " VALUES ()"
401
+ else
402
+ super
403
+ end
393
404
  end
394
-
405
+
406
+ # MySQL allows a LIMIT in DELETE and UPDATE statements.
407
+ def limit_sql(sql)
408
+ sql << " LIMIT #{@opts[:limit]}" if @opts[:limit]
409
+ end
410
+ alias delete_limit_sql limit_sql
411
+ alias update_limit_sql limit_sql
412
+
395
413
  # Use 0 for false on MySQL
396
414
  def literal_false
397
415
  BOOL_FALSE
@@ -420,10 +438,15 @@ module Sequel
420
438
  " ON DUPLICATE KEY UPDATE #{updating.join(COMMA_SEPARATOR)}"
421
439
  end
422
440
  end
423
-
424
- # MySQL does not support the SQL WITH clause
425
- def select_clause_order
426
- SELECT_CLAUSE_ORDER
441
+
442
+ # MySQL does not support the SQL WITH clause for SELECT statements
443
+ def select_clause_methods
444
+ SELECT_CLAUSE_METHODS
445
+ end
446
+
447
+ # MySQL supports the ORDER BY and LIMIT clauses for UPDATE statements
448
+ def update_clause_methods
449
+ UPDATE_CLAUSE_METHODS
427
450
  end
428
451
  end
429
452
  end
@@ -94,12 +94,13 @@ module Sequel
94
94
  end
95
95
 
96
96
  module DatasetMethods
97
- SELECT_CLAUSE_ORDER = %w'with distinct columns from join where group having compounds order limit'.freeze
97
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with distinct columns from join where group having compounds order limit')
98
98
 
99
99
  # Oracle uses MINUS instead of EXCEPT, and doesn't support EXCEPT ALL
100
- def except(dataset, all = false)
101
- raise(Sequel::Error, "EXCEPT ALL not supported") if all
102
- compound_clone(:minus, dataset, all)
100
+ def except(dataset, opts={})
101
+ opts = {:all=>opts} unless opts.is_a?(Hash)
102
+ raise(Sequel::Error, "EXCEPT ALL not supported") if opts[:all]
103
+ compound_clone(:minus, dataset, opts)
103
104
  end
104
105
 
105
106
  def empty?
@@ -139,13 +140,23 @@ module Sequel
139
140
  "#{expression} #{quote_identifier(aliaz)}"
140
141
  end
141
142
 
143
+ # The strftime format to use when literalizing the time.
144
+ def default_timestamp_format
145
+ "TIMESTAMP '%Y-%m-%d %H:%M:%S%N %z'".freeze
146
+ end
147
+
148
+ # Use a colon for the timestamp offset, since Oracle appears to require it.
149
+ def format_timestamp_offset(hour, minute)
150
+ sprintf("%+03i:%02i", hour, minute)
151
+ end
152
+
142
153
  # Oracle uses the SQL standard of only doubling ' inside strings.
143
154
  def literal_string(v)
144
155
  "'#{v.gsub("'", "''")}'"
145
156
  end
146
157
 
147
- def select_clause_order
148
- SELECT_CLAUSE_ORDER
158
+ def select_clause_methods
159
+ SELECT_CLAUSE_METHODS
149
160
  end
150
161
 
151
162
  # Modify the SQL to add the list of tables to select FROM
@@ -587,8 +587,8 @@ module Sequel
587
587
  QUERY_PLAN = 'QUERY PLAN'.to_sym
588
588
  ROW_EXCLUSIVE = 'ROW EXCLUSIVE'.freeze
589
589
  ROW_SHARE = 'ROW SHARE'.freeze
590
- SELECT_CLAUSE_ORDER = %w'distinct columns from join where group having compounds order limit lock'.freeze
591
- SELECT_CLAUSE_ORDER_84 = %w'with distinct columns from join where group having window compounds order limit lock'.freeze
590
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'distinct columns from join where group having compounds order limit lock')
591
+ SELECT_CLAUSE_METHODS_84 = Dataset.clause_methods(:select, %w'with distinct columns from join where group having window compounds order limit lock')
592
592
  SHARE = 'SHARE'.freeze
593
593
  SHARE_ROW_EXCLUSIVE = 'SHARE ROW EXCLUSIVE'.freeze
594
594
  SHARE_UPDATE_EXCLUSIVE = 'SHARE UPDATE EXCLUSIVE'.freeze
@@ -668,7 +668,8 @@ module Sequel
668
668
 
669
669
  # Insert a record returning the record inserted
670
670
  def insert_select(*values)
671
- naked.clone(default_server_opts(:sql=>insert_returning_sql(nil, *values))).single_record if server_version >= 80200
671
+ return if opts[:disable_insert_returning] || server_version < 80200
672
+ naked.clone(default_server_opts(:sql=>insert_returning_sql(nil, *values))).single_record
672
673
  end
673
674
 
674
675
  # Locks the table with the specified mode.
@@ -689,8 +690,7 @@ module Sequel
689
690
  return super if server_version < 80200
690
691
 
691
692
  # postgresql 8.2 introduces support for multi-row insert
692
- values = values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)
693
- ["#{insert_sql_base}#{source_list(@opts[:from])} (#{identifier_list(columns)}) VALUES #{values}"]
693
+ [insert_sql(columns, LiteralString.new('VALUES ' + values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)))]
694
694
  end
695
695
 
696
696
  # PostgreSQL supports timezones in literal timestamps
@@ -737,8 +737,8 @@ module Sequel
737
737
  end
738
738
 
739
739
  # The order of clauses in the SELECT SQL statement
740
- def select_clause_order
741
- server_version >= 80400 ? SELECT_CLAUSE_ORDER_84 : SELECT_CLAUSE_ORDER
740
+ def select_clause_methods
741
+ server_version >= 80400 ? SELECT_CLAUSE_METHODS_84 : SELECT_CLAUSE_METHODS
742
742
  end
743
743
 
744
744
  # SQL fragment for named window specifications
@@ -15,7 +15,7 @@ module Sequel
15
15
  end
16
16
 
17
17
  module DatasetMethods
18
- SELECT_CLAUSE_ORDER = %w'limit distinct columns from join where group order having compounds'.freeze
18
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'limit distinct columns from join where group order having compounds')
19
19
 
20
20
  # Progress requires SQL standard datetimes
21
21
  def requires_sql_standard_datetimes?
@@ -29,8 +29,8 @@ module Sequel
29
29
 
30
30
  private
31
31
 
32
- def select_clause_order
33
- SELECT_CLAUSE_ORDER
32
+ def select_clause_methods
33
+ SELECT_CLAUSE_METHODS
34
34
  end
35
35
 
36
36
  # Progress uses TOP for limit, but it is only supported in Progress 10.
@@ -235,7 +235,7 @@ module Sequel
235
235
 
236
236
  # Instance methods for datasets that connect to an SQLite database
237
237
  module DatasetMethods
238
- SELECT_CLAUSE_ORDER = %w'distinct columns from join where group having compounds order limit'.freeze
238
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'distinct columns from join where group having compounds order limit')
239
239
  CONSTANT_MAP = {:CURRENT_DATE=>"date(CURRENT_TIMESTAMP, 'localtime')".freeze, :CURRENT_TIMESTAMP=>"datetime(CURRENT_TIMESTAMP, 'localtime')".freeze, :CURRENT_TIME=>"time(CURRENT_TIMESTAMP, 'localtime')".freeze}
240
240
 
241
241
  # SQLite does not support pattern matching via regular expressions.
@@ -265,20 +265,6 @@ module Sequel
265
265
  @opts[:where] ? super : filter(1=>1).delete
266
266
  end
267
267
 
268
- # Insert the values into the database.
269
- def insert(*values)
270
- execute_insert(insert_sql(*values))
271
- end
272
-
273
- # Allow inserting of values directly from a dataset.
274
- def insert_sql(*values)
275
- if (values.size == 1) && values.first.is_a?(Sequel::Dataset)
276
- "#{insert_sql_base}#{source_list(@opts[:from])} #{values.first.sql};"
277
- else
278
- super(*values)
279
- end
280
- end
281
-
282
268
  # SQLite uses the nonstandard ` (backtick) for quoting identifiers.
283
269
  def quoted_identifier(c)
284
270
  "`#{c}`"
@@ -316,8 +302,8 @@ module Sequel
316
302
  end
317
303
 
318
304
  # SQLite does not support the SQL WITH clause
319
- def select_clause_order
320
- SELECT_CLAUSE_ORDER
305
+ def select_clause_methods
306
+ SELECT_CLAUSE_METHODS
321
307
  end
322
308
 
323
309
  # SQLite treats a DELETE with no WHERE clause as a TRUNCATE
@@ -116,8 +116,8 @@ class Sequel::ConnectionPool
116
116
  ensure
117
117
  @mutex.synchronize{release(t, server)} if conn && !dde
118
118
  end
119
- rescue StandardError => e
120
- raise e
119
+ rescue StandardError
120
+ raise
121
121
  rescue Exception => e
122
122
  raise(@convert_exceptions ? RuntimeError.new(e.message) : e)
123
123
  end
@@ -168,9 +168,7 @@ class Sequel::ConnectionPool
168
168
  begin
169
169
  conn = @connection_proc.call(server)
170
170
  rescue Exception=>exception
171
- e = Sequel::DatabaseConnectionError.new("#{exception.class} #{exception.message}")
172
- e.set_backtrace(exception.backtrace)
173
- raise e
171
+ raise Sequel.convert_exception_class(exception, Sequel::DatabaseConnectionError)
174
172
  end
175
173
  raise(Sequel::DatabaseConnectionError, "Connection parameters not valid") unless conn
176
174
  conn
@@ -236,7 +234,7 @@ class Sequel::SingleThreadedPool
236
234
  begin
237
235
  begin
238
236
  yield(c = (@conns[server] ||= @connection_proc.call(server)))
239
- rescue Sequel::DatabaseDisconnectError => dde
237
+ rescue Sequel::DatabaseDisconnectError
240
238
  @conns.delete(server)
241
239
  @disconnection_proc.call(c) if @disconnection_proc
242
240
  raise
data/lib/sequel/core.rb CHANGED
@@ -59,28 +59,12 @@
59
59
  # You can set the SEQUEL_NO_CORE_EXTENSIONS constant or environment variable to have
60
60
  # Sequel not extend the core classes.
61
61
  module Sequel
62
- # The offset of the current time zone from UTC, in seconds.
63
- LOCAL_DATETIME_OFFSET_SECS = Time.now.utc_offset
64
-
65
- # The offset of the current time zone from UTC, as a fraction of a day.
66
- LOCAL_DATETIME_OFFSET = respond_to?(:Rational, true) ? Rational(LOCAL_DATETIME_OFFSET_SECS, 60*60*24) : LOCAL_DATETIME_OFFSET_SECS/60/60/24.0
67
-
68
- @application_timezone = nil
69
62
  @convert_two_digit_years = true
70
- @database_timezone = nil
71
63
  @datetime_class = Time
72
- @typecast_timezone = nil
73
64
  @virtual_row_instance_eval = true
74
65
 
75
66
  class << self
76
67
  attr_accessor :convert_two_digit_years, :datetime_class, :virtual_row_instance_eval
77
- attr_accessor :application_timezone, :database_timezone, :typecast_timezone
78
- end
79
-
80
- # Convert the given Time/DateTime object into the database timezone, used when
81
- # literalizing objects in an SQL string.
82
- def self.application_to_database_timestamp(v)
83
- convert_output_timestamp(v, Sequel.database_timezone)
84
68
  end
85
69
 
86
70
  # Returns true if the passed object could be a specifier of conditions, false otherwise.
@@ -116,20 +100,17 @@ module Sequel
116
100
  Database.connect(*args, &block)
117
101
  end
118
102
 
119
- # Convert the given object into an object of Sequel.datetime_class in the
120
- # application_timezone. Used when coverting datetime/timestamp columns
121
- # returned by the database.
122
- def self.database_to_application_timestamp(v)
123
- convert_timestamp(v, Sequel.database_timezone)
124
- end
125
-
126
- # Sets the database, application, and typecasting timezones to the given timezone.
127
- def self.default_timezone=(tz)
128
- self.database_timezone = tz
129
- self.application_timezone = tz
130
- self.typecast_timezone = tz
103
+ # Convert the exception to the given class. The given class should be
104
+ # Sequel::Error or a subclass. Returns an instance of klass with
105
+ # the message and backtrace of exception.
106
+ def self.convert_exception_class(exception, klass)
107
+ return exception if exception.is_a?(klass)
108
+ e = klass.new("#{exception.class}: #{exception.message}")
109
+ e.wrapped_exception = exception
110
+ e.set_backtrace(exception.backtrace)
111
+ e
131
112
  end
132
-
113
+
133
114
  # Load all Sequel extensions given. Only loads extensions included in this
134
115
  # release of Sequel, doesn't load external extensions.
135
116
  #
@@ -199,7 +180,7 @@ module Sequel
199
180
  begin
200
181
  Date.parse(s, Sequel.convert_two_digit_years)
201
182
  rescue => e
202
- raise InvalidValue, "Invalid Date value #{s.inspect} (#{e.message})"
183
+ raise convert_exception_class(e, InvalidValue)
203
184
  end
204
185
  end
205
186
 
@@ -213,7 +194,7 @@ module Sequel
213
194
  datetime_class.parse(s)
214
195
  end
215
196
  rescue => e
216
- raise InvalidValue, "Invalid #{datetime_class} value #{s.inspect} (#{e.message})"
197
+ raise convert_exception_class(e, InvalidValue)
217
198
  end
218
199
  end
219
200
 
@@ -222,15 +203,22 @@ module Sequel
222
203
  begin
223
204
  Time.parse(s)
224
205
  rescue => e
225
- raise InvalidValue, "Invalid Time value #{s.inspect} (#{e.message})"
206
+ raise convert_exception_class(e, InvalidValue)
226
207
  end
227
208
  end
228
-
229
- # Convert the given object into an object of Sequel.datetime_class in the
230
- # application_timezone. Used when typecasting values when assigning them
231
- # to model datetime attributes.
232
- def self.typecast_to_application_timestamp(v)
233
- convert_timestamp(v, Sequel.typecast_timezone)
209
+
210
+ # If the supplied block takes a single argument,
211
+ # yield a new SQL::VirtualRow instance to the block
212
+ # argument. Otherwise, evaluate the block in the context of a new
213
+ # SQL::VirtualRow instance.
214
+ def self.virtual_row(&block)
215
+ vr = SQL::VirtualRow.new
216
+ case block.arity
217
+ when -1, 0
218
+ vr.instance_eval(&block)
219
+ else
220
+ block.call(vr)
221
+ end
234
222
  end
235
223
 
236
224
  ### Private Class Methods ###
@@ -248,90 +236,18 @@ module Sequel
248
236
  end
249
237
  connect(opts, &block)
250
238
  end
251
-
252
- # Converts the object from a String, Array, Date, DateTime, or Time into an
253
- # instance of Sequel.datetime_class. If a string and an offset is not given,
254
- # assume that the string is already in the given input_timezone.
255
- def self.convert_input_timestamp(v, input_timezone) # :nodoc:
256
- case v
257
- when String
258
- v2 = Sequel.string_to_datetime(v)
259
- if !input_timezone || Date._parse(v).has_key?(:offset)
260
- v2
261
- else
262
- # Correct for potentially wrong offset if offset is given
263
- if v2.is_a?(DateTime)
264
- # DateTime assumes UTC if no offset is given
265
- v2 = v2.new_offset(LOCAL_DATETIME_OFFSET) - LOCAL_DATETIME_OFFSET if input_timezone == :local
266
- else
267
- # Time assumes local time if no offset is given
268
- v2 = v2.getutc + LOCAL_DATETIME_OFFSET_SECS if input_timezone == :utc
269
- end
270
- v2
271
- end
272
- when Array
273
- y, mo, d, h, mi, s = v
274
- if datetime_class == DateTime
275
- DateTime.civil(y, mo, d, h, mi, s, input_timezone == :utc ? 0 : LOCAL_DATETIME_OFFSET)
276
- else
277
- Time.send(input_timezone == :utc ? :utc : :local, y, mo, d, h, mi, s)
278
- end
279
- when Time
280
- if datetime_class == DateTime
281
- v.respond_to?(:to_datetime) ? v.to_datetime : string_to_datetime(v.iso8601)
282
- else
283
- v
284
- end
285
- when DateTime
286
- if datetime_class == DateTime
287
- v
288
- else
289
- v.respond_to?(:to_time) ? v.to_time : string_to_datetime(v.to_s)
290
- end
291
- when Date
292
- convert_input_timestamp(v.to_s, input_timezone)
293
- else
294
- raise InvalidValue, "Invalid convert_input_timestamp type: #{v.inspect}"
295
- end
296
- end
297
-
298
- # Converts the object to the given output_timezone.
299
- def self.convert_output_timestamp(v, output_timezone) # :nodoc:
300
- if output_timezone
301
- if v.is_a?(DateTime)
302
- v.new_offset(output_timezone == :utc ? 0 : LOCAL_DATETIME_OFFSET)
303
- else
304
- v.send(output_timezone == :utc ? :getutc : :getlocal)
305
- end
306
- else
307
- v
308
- end
309
- end
310
-
311
- # Converts the given object from the given input timezone to the
312
- # application timezone using convert_input_timestamp and
313
- # convert_output_timestamp.
314
- def self.convert_timestamp(v, input_timezone) # :nodoc:
315
- begin
316
- convert_output_timestamp(convert_input_timestamp(v, input_timezone), Sequel.application_timezone)
317
- rescue InvalidValue
318
- raise
319
- rescue
320
- raise InvalidValue, "Invalid #{datetime_class} value: #{v.inspect}"
321
- end
322
- end
323
239
 
324
240
  # Method that adds a database adapter class method to Sequel that calls
325
241
  # Sequel.adapter_method.
326
242
  def self.def_adapter_method(*adapters) # :nodoc:
327
243
  adapters.each do |adapter|
328
- instance_eval("def #{adapter}(*args, &block); adapter_method('#{adapter}', *args, &block) end")
244
+ instance_eval("def #{adapter}(*args, &block); adapter_method('#{adapter}', *args, &block) end", __FILE__, __LINE__)
329
245
  end
330
246
  end
331
247
 
332
- private_class_method :adapter_method, :convert_input_timestamp, :convert_output_timestamp, :convert_timestamp, :def_adapter_method
248
+ private_class_method :adapter_method, :def_adapter_method
333
249
 
334
- require(%w"metaprogramming sql connection_pool exceptions dataset database version")
250
+ require(%w"metaprogramming sql connection_pool exceptions dataset database timezones version")
335
251
  require(%w"schema_generator schema_methods schema_sql", 'database')
336
252
  require(%w"convenience graph prepared_statements sql", 'dataset')
337
253
  require('core_sql') if !defined?(::SEQUEL_NO_CORE_EXTENSIONS) && !ENV.has_key?('SEQUEL_NO_CORE_EXTENSIONS')