sequel 3.4.0 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
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')