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.
- data/CHANGELOG +84 -0
- data/Rakefile +1 -1
- data/doc/cheat_sheet.rdoc +5 -2
- data/doc/opening_databases.rdoc +2 -0
- data/doc/release_notes/3.5.0.txt +510 -0
- data/lib/sequel/adapters/ado.rb +3 -1
- data/lib/sequel/adapters/ado/mssql.rb +2 -2
- data/lib/sequel/adapters/do.rb +2 -11
- data/lib/sequel/adapters/do/mysql.rb +7 -0
- data/lib/sequel/adapters/do/postgres.rb +2 -2
- data/lib/sequel/adapters/firebird.rb +3 -3
- data/lib/sequel/adapters/informix.rb +3 -3
- data/lib/sequel/adapters/jdbc/h2.rb +3 -3
- data/lib/sequel/adapters/jdbc/mssql.rb +7 -0
- data/lib/sequel/adapters/mysql.rb +60 -21
- data/lib/sequel/adapters/odbc.rb +1 -1
- data/lib/sequel/adapters/openbase.rb +3 -3
- data/lib/sequel/adapters/oracle.rb +1 -5
- data/lib/sequel/adapters/postgres.rb +3 -3
- data/lib/sequel/adapters/shared/mssql.rb +142 -33
- data/lib/sequel/adapters/shared/mysql.rb +54 -31
- data/lib/sequel/adapters/shared/oracle.rb +17 -6
- data/lib/sequel/adapters/shared/postgres.rb +7 -7
- data/lib/sequel/adapters/shared/progress.rb +3 -3
- data/lib/sequel/adapters/shared/sqlite.rb +3 -17
- data/lib/sequel/connection_pool.rb +4 -6
- data/lib/sequel/core.rb +29 -113
- data/lib/sequel/database.rb +14 -12
- data/lib/sequel/dataset.rb +8 -21
- data/lib/sequel/dataset/convenience.rb +1 -1
- data/lib/sequel/dataset/graph.rb +9 -2
- data/lib/sequel/dataset/sql.rb +170 -104
- data/lib/sequel/exceptions.rb +3 -0
- data/lib/sequel/extensions/looser_typecasting.rb +21 -0
- data/lib/sequel/extensions/named_timezones.rb +61 -0
- data/lib/sequel/extensions/schema_dumper.rb +7 -1
- data/lib/sequel/extensions/sql_expr.rb +122 -0
- data/lib/sequel/extensions/string_date_time.rb +4 -4
- data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
- data/lib/sequel/model/associations.rb +105 -45
- data/lib/sequel/model/base.rb +37 -28
- data/lib/sequel/plugins/active_model.rb +35 -0
- data/lib/sequel/plugins/association_dependencies.rb +96 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
- data/lib/sequel/plugins/force_encoding.rb +61 -0
- data/lib/sequel/plugins/many_through_many.rb +32 -11
- data/lib/sequel/plugins/nested_attributes.rb +7 -2
- data/lib/sequel/plugins/subclasses.rb +45 -0
- data/lib/sequel/plugins/touch.rb +118 -0
- data/lib/sequel/plugins/typecast_on_load.rb +61 -0
- data/lib/sequel/sql.rb +31 -30
- data/lib/sequel/timezones.rb +161 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +262 -0
- data/spec/adapters/mysql_spec.rb +46 -8
- data/spec/adapters/postgres_spec.rb +6 -3
- data/spec/adapters/spec_helper.rb +21 -0
- data/spec/adapters/sqlite_spec.rb +1 -1
- data/spec/core/connection_pool_spec.rb +1 -1
- data/spec/core/database_spec.rb +27 -1
- data/spec/core/dataset_spec.rb +63 -1
- data/spec/core/object_graph_spec.rb +1 -1
- data/spec/core/schema_spec.rb +1 -0
- data/spec/extensions/active_model_spec.rb +47 -0
- data/spec/extensions/association_dependencies_spec.rb +108 -0
- data/spec/extensions/class_table_inheritance_spec.rb +252 -0
- data/spec/extensions/force_encoding_spec.rb +75 -0
- data/spec/extensions/looser_typecasting_spec.rb +39 -0
- data/spec/extensions/many_through_many_spec.rb +60 -2
- data/spec/extensions/named_timezones_spec.rb +72 -0
- data/spec/extensions/nested_attributes_spec.rb +29 -1
- data/spec/extensions/schema_dumper_spec.rb +10 -0
- data/spec/extensions/spec_helper.rb +1 -1
- data/spec/extensions/sql_expr_spec.rb +89 -0
- data/spec/extensions/subclasses_spec.rb +52 -0
- data/spec/extensions/thread_local_timezones_spec.rb +45 -0
- data/spec/extensions/touch_spec.rb +155 -0
- data/spec/extensions/typecast_on_load_spec.rb +60 -0
- data/spec/integration/database_test.rb +8 -0
- data/spec/integration/dataset_test.rb +9 -9
- data/spec/integration/plugin_test.rb +139 -0
- data/spec/integration/schema_test.rb +7 -7
- data/spec/integration/spec_helper.rb +32 -1
- data/spec/integration/timezone_test.rb +3 -3
- data/spec/integration/transaction_test.rb +1 -1
- data/spec/integration/type_test.rb +6 -6
- data/spec/model/association_reflection_spec.rb +18 -0
- data/spec/model/associations_spec.rb +169 -9
- data/spec/model/base_spec.rb +2 -0
- data/spec/model/eager_loading_spec.rb +82 -2
- data/spec/model/model_spec.rb +8 -1
- data/spec/model/record_spec.rb +52 -9
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
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
|
-
|
|
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
|
|
382
|
-
"
|
|
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
|
|
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
|
|
391
|
-
def
|
|
392
|
-
|
|
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
|
|
426
|
-
|
|
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
|
-
|
|
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,
|
|
101
|
-
|
|
102
|
-
|
|
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
|
|
148
|
-
|
|
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
|
-
|
|
591
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
741
|
-
server_version >= 80400 ?
|
|
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
|
-
|
|
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
|
|
33
|
-
|
|
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
|
-
|
|
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
|
|
320
|
-
|
|
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
|
|
120
|
-
raise
|
|
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
|
-
|
|
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
|
|
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
|
|
120
|
-
#
|
|
121
|
-
#
|
|
122
|
-
def self.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
|
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
|
|
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
|
|
206
|
+
raise convert_exception_class(e, InvalidValue)
|
|
226
207
|
end
|
|
227
208
|
end
|
|
228
|
-
|
|
229
|
-
#
|
|
230
|
-
#
|
|
231
|
-
#
|
|
232
|
-
|
|
233
|
-
|
|
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, :
|
|
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')
|