sequel 5.19.0 → 5.24.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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +102 -0
  3. data/doc/dataset_filtering.rdoc +15 -0
  4. data/doc/opening_databases.rdoc +5 -1
  5. data/doc/release_notes/5.20.0.txt +89 -0
  6. data/doc/release_notes/5.21.0.txt +87 -0
  7. data/doc/release_notes/5.22.0.txt +48 -0
  8. data/doc/release_notes/5.23.0.txt +56 -0
  9. data/doc/release_notes/5.24.0.txt +56 -0
  10. data/doc/sharding.rdoc +2 -0
  11. data/doc/testing.rdoc +1 -0
  12. data/doc/transactions.rdoc +38 -0
  13. data/lib/sequel/adapters/ado.rb +27 -19
  14. data/lib/sequel/adapters/jdbc.rb +7 -1
  15. data/lib/sequel/adapters/jdbc/mysql.rb +2 -2
  16. data/lib/sequel/adapters/jdbc/postgresql.rb +1 -13
  17. data/lib/sequel/adapters/jdbc/sqlite.rb +29 -0
  18. data/lib/sequel/adapters/mysql2.rb +2 -3
  19. data/lib/sequel/adapters/shared/mssql.rb +7 -7
  20. data/lib/sequel/adapters/shared/postgres.rb +37 -19
  21. data/lib/sequel/adapters/shared/sqlite.rb +27 -3
  22. data/lib/sequel/adapters/sqlite.rb +1 -1
  23. data/lib/sequel/adapters/tinytds.rb +12 -0
  24. data/lib/sequel/adapters/utils/mysql_mysql2.rb +2 -0
  25. data/lib/sequel/database/logging.rb +7 -1
  26. data/lib/sequel/database/query.rb +1 -1
  27. data/lib/sequel/database/schema_generator.rb +12 -3
  28. data/lib/sequel/database/schema_methods.rb +2 -0
  29. data/lib/sequel/database/transactions.rb +57 -5
  30. data/lib/sequel/dataset.rb +4 -2
  31. data/lib/sequel/dataset/actions.rb +3 -2
  32. data/lib/sequel/dataset/placeholder_literalizer.rb +4 -1
  33. data/lib/sequel/dataset/query.rb +5 -1
  34. data/lib/sequel/dataset/sql.rb +11 -7
  35. data/lib/sequel/extensions/named_timezones.rb +52 -8
  36. data/lib/sequel/extensions/pg_array.rb +4 -0
  37. data/lib/sequel/extensions/pg_json.rb +387 -123
  38. data/lib/sequel/extensions/pg_range.rb +3 -2
  39. data/lib/sequel/extensions/pg_row.rb +3 -1
  40. data/lib/sequel/extensions/schema_dumper.rb +1 -1
  41. data/lib/sequel/extensions/server_block.rb +15 -4
  42. data/lib/sequel/model/associations.rb +35 -9
  43. data/lib/sequel/model/plugins.rb +104 -0
  44. data/lib/sequel/plugins/association_dependencies.rb +3 -3
  45. data/lib/sequel/plugins/association_pks.rb +14 -4
  46. data/lib/sequel/plugins/association_proxies.rb +3 -2
  47. data/lib/sequel/plugins/class_table_inheritance.rb +11 -0
  48. data/lib/sequel/plugins/composition.rb +13 -9
  49. data/lib/sequel/plugins/finder.rb +2 -2
  50. data/lib/sequel/plugins/hook_class_methods.rb +17 -5
  51. data/lib/sequel/plugins/insert_conflict.rb +72 -0
  52. data/lib/sequel/plugins/inverted_subsets.rb +2 -2
  53. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +147 -59
  54. data/lib/sequel/plugins/rcte_tree.rb +6 -0
  55. data/lib/sequel/plugins/static_cache.rb +8 -3
  56. data/lib/sequel/plugins/static_cache_cache.rb +53 -0
  57. data/lib/sequel/plugins/subset_conditions.rb +2 -2
  58. data/lib/sequel/plugins/validation_class_methods.rb +5 -3
  59. data/lib/sequel/sql.rb +15 -3
  60. data/lib/sequel/timezones.rb +50 -11
  61. data/lib/sequel/version.rb +1 -1
  62. data/spec/adapters/mssql_spec.rb +24 -0
  63. data/spec/adapters/mysql_spec.rb +0 -5
  64. data/spec/adapters/postgres_spec.rb +319 -1
  65. data/spec/bin_spec.rb +1 -1
  66. data/spec/core/database_spec.rb +123 -2
  67. data/spec/core/dataset_spec.rb +33 -1
  68. data/spec/core/expression_filters_spec.rb +25 -1
  69. data/spec/core/schema_spec.rb +24 -0
  70. data/spec/extensions/class_table_inheritance_spec.rb +30 -8
  71. data/spec/extensions/core_refinements_spec.rb +1 -1
  72. data/spec/extensions/hook_class_methods_spec.rb +22 -0
  73. data/spec/extensions/insert_conflict_spec.rb +103 -0
  74. data/spec/extensions/migration_spec.rb +13 -0
  75. data/spec/extensions/named_timezones_spec.rb +109 -2
  76. data/spec/extensions/pg_auto_constraint_validations_spec.rb +45 -0
  77. data/spec/extensions/pg_json_spec.rb +218 -29
  78. data/spec/extensions/pg_range_spec.rb +76 -9
  79. data/spec/extensions/rcte_tree_spec.rb +6 -0
  80. data/spec/extensions/s_spec.rb +1 -1
  81. data/spec/extensions/schema_dumper_spec.rb +4 -2
  82. data/spec/extensions/server_block_spec.rb +38 -0
  83. data/spec/extensions/spec_helper.rb +8 -1
  84. data/spec/extensions/static_cache_cache_spec.rb +35 -0
  85. data/spec/integration/dataset_test.rb +25 -9
  86. data/spec/integration/plugin_test.rb +42 -0
  87. data/spec/integration/schema_test.rb +7 -2
  88. data/spec/integration/transaction_test.rb +50 -0
  89. data/spec/model/associations_spec.rb +84 -4
  90. data/spec/model/plugins_spec.rb +111 -0
  91. metadata +16 -2
@@ -112,7 +112,7 @@ module Sequel
112
112
  sqlite3_opts = {}
113
113
  sqlite3_opts[:readonly] = typecast_value_boolean(opts[:readonly]) if opts.has_key?(:readonly)
114
114
  db = ::SQLite3::Database.new(opts[:database].to_s, sqlite3_opts)
115
- db.busy_timeout(opts.fetch(:timeout, 5000))
115
+ db.busy_timeout(typecast_value_integer(opts.fetch(:timeout, 5000)))
116
116
 
117
117
  if USE_EXTENDED_RESULT_CODES
118
118
  db.extended_result_codes = true
@@ -16,6 +16,18 @@ module Sequel
16
16
  c = TinyTds::Client.new(opts)
17
17
  c.query_options.merge!(:cache_rows=>false)
18
18
 
19
+ if opts[:ansi]
20
+ sql = %w(
21
+ ANSI_NULLS
22
+ ANSI_PADDING
23
+ ANSI_WARNINGS
24
+ ANSI_NULL_DFLT_ON
25
+ QUOTED_IDENTIFIER
26
+ CONCAT_NULL_YIELDS_NULL
27
+ ).map{|v| "SET #{v} ON"}.join(";")
28
+ log_connection_yield(sql, c){c.execute(sql)}
29
+ end
30
+
19
31
  if (ts = opts[:textsize])
20
32
  sql = "SET TEXTSIZE #{typecast_value_integer(ts)}"
21
33
  log_connection_yield(sql, c){c.execute(sql)}
@@ -59,6 +59,8 @@ module Sequel
59
59
  ForeignKeyConstraintViolation
60
60
  when 4025
61
61
  CheckConstraintViolation
62
+ when 1205
63
+ DatabaseLockTimeout
62
64
  else
63
65
  super
64
66
  end
@@ -35,7 +35,7 @@ module Sequel
35
35
  # Yield to the block, logging any errors at error level to all loggers,
36
36
  # and all other queries with the duration at warn or info level.
37
37
  def log_connection_yield(sql, conn, args=nil)
38
- return yield if @loggers.empty?
38
+ return yield if skip_logging?
39
39
  sql = "#{connection_info(conn) if conn && log_connection_info}#{sql}#{"; #{args.inspect}" if args}"
40
40
  timer = Sequel.start_timer
41
41
 
@@ -58,6 +58,12 @@ module Sequel
58
58
 
59
59
  private
60
60
 
61
+ # Determine if logging should be skipped. Defaults to true if no loggers
62
+ # have been specified.
63
+ def skip_logging?
64
+ @loggers.empty?
65
+ end
66
+
61
67
  # String including information about the connection, for use when logging
62
68
  # connection info.
63
69
  def connection_info(conn)
@@ -331,7 +331,7 @@ module Sequel
331
331
  :time
332
332
  when /\A(bool(ean)?)\z/io
333
333
  :boolean
334
- when /\A(real|float|double( precision)?|double\(\d+,\d+\)( unsigned)?)\z/io
334
+ when /\A(real|float( unsigned)?|double( precision)?|double\(\d+,\d+\)( unsigned)?)\z/io
335
335
  :float
336
336
  when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(\d+|false|true)\))?))\z/io
337
337
  $1 && ['0', 'false'].include?($1) ? :integer : :decimal
@@ -110,6 +110,9 @@ module Sequel
110
110
  # yet exist on referenced table (but will exist before the transaction commits).
111
111
  # Basically it adds DEFERRABLE INITIALLY DEFERRED on key creation.
112
112
  # If you use :immediate as the value, uses DEFERRABLE INITIALLY IMMEDIATE.
113
+ # :generated_always_as :: Specify a GENERATED ALWAYS AS column expression,
114
+ # if generated columns are supported (PostgreSQL 12+, MariaDB 5.2.0+,
115
+ # and MySQL 5.7.6+).
113
116
  # :index :: Create an index on this column. If given a hash, use the hash as the
114
117
  # options for the index.
115
118
  # :key :: For foreign key columns, the column in the associated table
@@ -126,15 +129,21 @@ module Sequel
126
129
  # be used if you have a single, nonautoincrementing primary key column
127
130
  # (use the primary_key method in that case).
128
131
  # :primary_key_constraint_name :: The name to give the primary key constraint
132
+ # :primary_key_deferrable :: Similar to :deferrable, but for the primary key constraint
133
+ # if :primary_key is used.
129
134
  # :type :: Overrides the type given as the argument. Generally not used by column
130
135
  # itself, but can be passed as an option to other methods that call column.
131
136
  # :unique :: Mark the column as unique, generally has the same effect as
132
137
  # creating a unique index on the column.
133
138
  # :unique_constraint_name :: The name to give the unique key constraint
139
+ # :unique_deferrable :: Similar to :deferrable, but for the unique constraint if :unique
140
+ # is used.
141
+ #
142
+ # PostgreSQL specific options:
143
+ #
144
+ # :identity :: Create an identity column.
134
145
  #
135
146
  # MySQL specific options:
136
- # :generated_always_as :: Specify a GENERATED ALWAYS AS column expression,
137
- # if generated columns are supported.
138
147
  # :generated_type :: Set the type of column when using :generated_always_as,
139
148
  # should be :virtual or :stored to force a type.
140
149
  def column(name, type, opts = OPTS)
@@ -634,7 +643,7 @@ module Sequel
634
643
 
635
644
  # Drop a composite foreign key constraint
636
645
  def drop_composite_foreign_key(columns, opts)
637
- @operations << {:op => :drop_constraint, :type => :foreign_key, :columns => columns}.merge!(opts)
646
+ @operations << opts.merge(:op => :drop_constraint, :type => :foreign_key, :columns => columns)
638
647
  nil
639
648
  end
640
649
  end
@@ -586,6 +586,7 @@ module Sequel
586
586
  sql << " CONSTRAINT #{quote_identifier(name)}"
587
587
  end
588
588
  sql << ' PRIMARY KEY'
589
+ constraint_deferrable_sql_append(sql, column[:primary_key_deferrable])
589
590
  end
590
591
  end
591
592
 
@@ -606,6 +607,7 @@ module Sequel
606
607
  sql << " CONSTRAINT #{quote_identifier(name)}"
607
608
  end
608
609
  sql << ' UNIQUE'
610
+ constraint_deferrable_sql_append(sql, column[:unique_deferrable])
609
611
  end
610
612
  end
611
613
 
@@ -25,13 +25,19 @@ module Sequel
25
25
  # Otherwise, add the block to the list of blocks to call after the currently
26
26
  # in progress transaction commits (and only if it commits).
27
27
  # Options:
28
+ # :savepoint :: If currently inside a savepoint, only run this hook on transaction
29
+ # commit if all enclosing savepoints have been released.
28
30
  # :server :: The server/shard to use.
29
31
  def after_commit(opts=OPTS, &block)
30
32
  raise Error, "must provide block to after_commit" unless block
31
33
  synchronize(opts[:server]) do |conn|
32
34
  if h = _trans(conn)
33
35
  raise Error, "cannot call after_commit in a prepared transaction" if h[:prepare]
34
- add_transaction_hook(conn, :after_commit, block)
36
+ if opts[:savepoint] && in_savepoint?(conn)
37
+ add_savepoint_hook(conn, :after_commit, block)
38
+ else
39
+ add_transaction_hook(conn, :after_commit, block)
40
+ end
35
41
  else
36
42
  yield
37
43
  end
@@ -42,13 +48,20 @@ module Sequel
42
48
  # Otherwise, add the block to the list of the blocks to call after the currently
43
49
  # in progress transaction rolls back (and only if it rolls back).
44
50
  # Options:
51
+ # :savepoint :: If currently inside a savepoint, run this hook immediately when
52
+ # any enclosing savepoint is rolled back, which may be before the transaction
53
+ # commits or rollsback.
45
54
  # :server :: The server/shard to use.
46
55
  def after_rollback(opts=OPTS, &block)
47
56
  raise Error, "must provide block to after_rollback" unless block
48
57
  synchronize(opts[:server]) do |conn|
49
58
  if h = _trans(conn)
50
59
  raise Error, "cannot call after_rollback in a prepared transaction" if h[:prepare]
51
- add_transaction_hook(conn, :after_rollback, block)
60
+ if opts[:savepoint] && in_savepoint?(conn)
61
+ add_savepoint_hook(conn, :after_rollback, block)
62
+ else
63
+ add_transaction_hook(conn, :after_rollback, block)
64
+ end
52
65
  end
53
66
  end
54
67
  end
@@ -298,6 +311,13 @@ module Sequel
298
311
  Sequel.synchronize{@transactions[conn] = hash}
299
312
  end
300
313
 
314
+ # Set the given callable as a hook to be called. Type should be either
315
+ # :after_commit or :after_rollback.
316
+ def add_savepoint_hook(conn, type, block)
317
+ savepoint = _trans(conn)[:savepoints].last
318
+ (savepoint[type] ||= []) << block
319
+ end
320
+
301
321
  # Set the given callable as a hook to be called. Type should be either
302
322
  # :after_commit or :after_rollback.
303
323
  def add_transaction_hook(conn, type, block)
@@ -401,6 +421,14 @@ module Sequel
401
421
  supports_savepoints? && savepoint_level(conn) > 1
402
422
  end
403
423
 
424
+ # Retrieve the savepoint hooks that should be run for the given
425
+ # connection and commit status.
426
+ def savepoint_hooks(conn, committed)
427
+ if in_savepoint?(conn)
428
+ _trans(conn)[:savepoints].last[committed ? :after_commit : :after_rollback]
429
+ end
430
+ end
431
+
404
432
  # Retrieve the transaction hooks that should be run for the given
405
433
  # connection and commit status.
406
434
  def transaction_hooks(conn, committed)
@@ -411,16 +439,40 @@ module Sequel
411
439
 
412
440
  # Remove the current thread from the list of active transactions
413
441
  def remove_transaction(conn, committed)
414
- callbacks = transaction_hooks(conn, committed)
442
+ if in_savepoint?(conn)
443
+ savepoint_callbacks = savepoint_hooks(conn, committed)
444
+ if committed
445
+ savepoint_rollback_callbacks = savepoint_hooks(conn, false)
446
+ end
447
+ else
448
+ callbacks = transaction_hooks(conn, committed)
449
+ end
415
450
 
416
451
  if transaction_finished?(conn)
417
452
  h = _trans(conn)
418
453
  rolled_back = !committed
419
454
  Sequel.synchronize{h[:rolled_back] = rolled_back}
420
455
  Sequel.synchronize{@transactions.delete(conn)}
456
+ callbacks.each(&:call) if callbacks
457
+ elsif savepoint_callbacks || savepoint_rollback_callbacks
458
+ if committed
459
+ meth = in_savepoint?(conn) ? :add_savepoint_hook : :add_transaction_hook
460
+
461
+ if savepoint_callbacks
462
+ savepoint_callbacks.each do |block|
463
+ send(meth, conn, :after_commit, block)
464
+ end
465
+ end
466
+
467
+ if savepoint_rollback_callbacks
468
+ savepoint_rollback_callbacks.each do |block|
469
+ send(meth, conn, :after_rollback, block)
470
+ end
471
+ end
472
+ else
473
+ savepoint_callbacks.each(&:call)
474
+ end
421
475
  end
422
-
423
- callbacks.each(&:call) if callbacks
424
476
  end
425
477
 
426
478
  # SQL to rollback to a savepoint
@@ -20,8 +20,10 @@ module Sequel
20
20
  # old_posts = posts.where{stamp < Date.today - 7}
21
21
  # davids_old_posts = davids_posts.where{stamp < Date.today - 7}
22
22
  #
23
- # Datasets are Enumerable objects, so they can be manipulated using any
24
- # of the Enumerable methods, such as map, inject, etc.
23
+ # Datasets are Enumerable objects, so they can be manipulated using many
24
+ # of the Enumerable methods, such as +map+ and +inject+. Note that there are some methods
25
+ # that Dataset defines that override methods defined in Enumerable and result in different
26
+ # behavior, such as +select+ and +group_by+.
25
27
  #
26
28
  # For more information, see the {"Dataset Basics" guide}[rdoc-ref:doc/dataset_basics.rdoc].
27
29
  class Dataset
@@ -333,6 +333,7 @@ module Sequel
333
333
  # after every 50 records.
334
334
  # :return :: When this is set to :primary_key, returns an array of
335
335
  # autoincremented primary key values for the rows inserted.
336
+ # This does not have an effect if +values+ is a Dataset.
336
337
  # :server :: Set the server/shard to use for the transaction and insert
337
338
  # queries.
338
339
  # :slice :: Same as :commit_every, :commit_every takes precedence.
@@ -1069,7 +1070,7 @@ module Sequel
1069
1070
 
1070
1071
  # Set the server to use to :default unless it is already set in the passed opts
1071
1072
  def default_server_opts(opts)
1072
- if @db.sharded?
1073
+ if @db.sharded? && !opts.has_key?(:server)
1073
1074
  opts = Hash[opts]
1074
1075
  opts[:server] = @opts[:server] || :default
1075
1076
  end
@@ -1080,7 +1081,7 @@ module Sequel
1080
1081
  # :read_only server unless a specific server is set.
1081
1082
  def execute(sql, opts=OPTS, &block)
1082
1083
  db = @db
1083
- if db.sharded?
1084
+ if db.sharded? && !opts.has_key?(:server)
1084
1085
  opts = Hash[opts]
1085
1086
  opts[:server] = @opts[:server] || (@opts[:lock] ? :default : :read_only)
1086
1087
  opts
@@ -170,7 +170,10 @@ module Sequel
170
170
  # receiver's dataset to the block, and the block should return the new dataset
171
171
  # to use.
172
172
  def with_dataset
173
- dup.instance_exec{@dataset = yield @dataset; self}.freeze
173
+ dataset = yield @dataset
174
+ other = dup
175
+ other.instance_variable_set(:@dataset, dataset)
176
+ other.freeze
174
177
  end
175
178
 
176
179
  # Return an array of all objects by running the SQL query for the given arguments.
@@ -1062,6 +1062,10 @@ module Sequel
1062
1062
  # :args :: Specify the arguments/columns for the CTE, should be an array of symbols.
1063
1063
  # :recursive :: Specify that this is a recursive CTE
1064
1064
  #
1065
+ # PostgreSQL Specific Options:
1066
+ # :materialized :: Set to false to force inlining of the CTE, or true to force not inlining
1067
+ # the CTE (PostgreSQL 12+).
1068
+ #
1065
1069
  # DB[:items].with(:items, DB[:syx].where(Sequel[:name].like('A%')))
1066
1070
  # # WITH items AS (SELECT * FROM syx WHERE (name LIKE 'A%' ESCAPE '\')) SELECT * FROM items
1067
1071
  def with(name, dataset, opts=OPTS)
@@ -1091,7 +1095,7 @@ module Sequel
1091
1095
  # # SELECT i1.id, i1.parent_id FROM i1 INNER JOIN t ON (t.id = i1.parent_id)
1092
1096
  # # ) SELECT * FROM t
1093
1097
  def with_recursive(name, nonrecursive, recursive, opts=OPTS)
1094
- raise(Error, 'This datatset does not support common table expressions') unless supports_cte?
1098
+ raise(Error, 'This dataset does not support common table expressions') unless supports_cte?
1095
1099
  if hoist_cte?(nonrecursive)
1096
1100
  s, ds = hoist_cte(nonrecursive)
1097
1101
  s.with_recursive(name, ds, recursive, opts)
@@ -1510,13 +1510,7 @@ module Sequel
1510
1510
  comma = ', '
1511
1511
  ws.each do |w|
1512
1512
  sql << comma if c
1513
- quote_identifier_append(sql, w[:name])
1514
- if args = w[:args]
1515
- sql << '('
1516
- identifier_list_append(sql, args)
1517
- sql << ')'
1518
- end
1519
- sql << ' AS '
1513
+ select_with_sql_prefix(sql, w)
1520
1514
  literal_dataset_append(sql, w[:dataset])
1521
1515
  c ||= true
1522
1516
  end
@@ -1530,6 +1524,16 @@ module Sequel
1530
1524
  "WITH "
1531
1525
  end
1532
1526
 
1527
+ def select_with_sql_prefix(sql, w)
1528
+ quote_identifier_append(sql, w[:name])
1529
+ if args = w[:args]
1530
+ sql << '('
1531
+ identifier_list_append(sql, args)
1532
+ sql << ')'
1533
+ end
1534
+ sql << ' AS '
1535
+ end
1536
+
1533
1537
  # Whether the symbol cache should be skipped when literalizing the dataset
1534
1538
  def skip_symbol_cache?
1535
1539
  @opts[:skip_symbol_cache]
@@ -2,18 +2,21 @@
2
2
  #
3
3
  # Allows the use of named timezones via TZInfo (requires tzinfo).
4
4
  # Forces the use of DateTime as Sequel's datetime_class, since
5
- # ruby's Time class doesn't support timezones other than local
6
- # and UTC.
5
+ # historically, Ruby's Time class doesn't support timezones other
6
+ # than local and UTC. To continue using Ruby's Time class when using
7
+ # the named_timezones extension:
8
+ #
9
+ # # Load the extension
10
+ # Sequel.extension :named_timezones
11
+ #
12
+ # # Set Sequel.datetime_class back to Time
13
+ # Sequel.datetime_class = Time
7
14
  #
8
15
  # This allows you to either pass strings or TZInfo::Timezone
9
16
  # instance to Sequel.database_timezone=, application_timezone=, and
10
17
  # typecast_timezone=. If a string is passed, it is converted to a
11
18
  # TZInfo::Timezone using TZInfo::Timezone.get.
12
19
  #
13
- # To load the extension:
14
- #
15
- # Sequel.extension :named_timezones
16
- #
17
20
  # Let's say you have the database server in New York and the
18
21
  # application server in Los Angeles. For historical reasons, data
19
22
  # is stored in local New York time, but the application server only
@@ -37,7 +40,8 @@
37
40
  # Note that typecasting from the database timezone to the application
38
41
  # timezone when fetching rows is dependent on the database adapter,
39
42
  # and only works on adapters where Sequel itself does the conversion.
40
- # It should work on mysql, postgres, sqlite, ibmdb, and jdbc.
43
+ # It should work with the mysql, postgres, sqlite, ibmdb, and jdbc
44
+ # adapters.
41
45
  #
42
46
  # Related module: Sequel::NamedTimezones
43
47
 
@@ -63,9 +67,48 @@ module Sequel
63
67
 
64
68
  private
65
69
 
70
+ if RUBY_VERSION >= '2.6'
71
+ # Convert the given input Time (which must be in UTC) to the given input timezone,
72
+ # which should be a TZInfo::Timezone instance.
73
+ def convert_input_time_other(v, input_timezone)
74
+ Time.new(v.year, v.mon, v.day, v.hour, v.min, (v.sec + Rational(v.nsec, 1000000000)), input_timezone)
75
+ rescue TZInfo::AmbiguousTime
76
+ raise unless disamb = tzinfo_disambiguator_for(v)
77
+ period = input_timezone.period_for_local(v, &disamb)
78
+ offset = period.utc_total_offset
79
+ Time.at(v.to_i - offset, :in => input_timezone)
80
+ end
81
+
82
+ # Convert the given input Time to the given output timezone,
83
+ # which should be a TZInfo::Timezone instance.
84
+ def convert_output_time_other(v, output_timezone)
85
+ Time.at(v.to_i, :in => output_timezone)
86
+ end
87
+ else
88
+ # :nodoc:
89
+ # :nocov:
90
+ def convert_input_time_other(v, input_timezone)
91
+ local_offset = input_timezone.period_for_local(v, &tzinfo_disambiguator_for(v)).utc_total_offset
92
+ Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i
93
+ end
94
+
95
+ if defined?(TZInfo::VERSION) && TZInfo::VERSION > '2'
96
+ def convert_output_time_other(v, output_timezone)
97
+ v = output_timezone.utc_to_local(v.getutc)
98
+ local_offset = output_timezone.period_for_local(v, &tzinfo_disambiguator_for(v)).utc_total_offset
99
+ Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i + local_offset
100
+ end
101
+ else
102
+ def convert_output_time_other(v, output_timezone)
103
+ v = output_timezone.utc_to_local(v.getutc)
104
+ local_offset = output_timezone.period_for_local(v, &tzinfo_disambiguator_for(v)).utc_total_offset
105
+ Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i
106
+ end
107
+ end
108
+ end
109
+
66
110
  # Handle both TZInfo 1 and TZInfo 2
67
111
  if defined?(TZInfo::VERSION) && TZInfo::VERSION > '2'
68
- # :nodoc:
69
112
  def convert_input_datetime_other(v, input_timezone)
70
113
  local_offset = Rational(input_timezone.period_for_local(v, &tzinfo_disambiguator_for(v)).utc_total_offset, 86400)
71
114
  (v - local_offset).new_offset(local_offset)
@@ -78,6 +121,7 @@ module Sequel
78
121
  DateTime.jd(v.jd, v.hour, v.minute, v.second + v.sec_fraction, v.offset, v.start)
79
122
  end
80
123
  # :nodoc:
124
+ # :nocov:
81
125
  else
82
126
  # Assume the given DateTime has a correct time but a wrong timezone. It is
83
127
  # currently in UTC timezone, but it should be converted to the input_timezone.
@@ -340,14 +340,18 @@ module Sequel
340
340
  raise Sequel::Error, "invalid array, empty string" if eos?
341
341
  raise Sequel::Error, "invalid array, doesn't start with {" unless scan(/((\[\d+:\d+\])+=)?\{/)
342
342
 
343
+ # :nocov:
343
344
  while !eos?
345
+ # :nocov:
344
346
  char = scan(/[{}",]|[^{}",]+/)
345
347
  if char == ','
346
348
  # Comma outside quoted string indicates end of current entry
347
349
  new_entry
348
350
  elsif char == '"'
349
351
  raise Sequel::Error, "invalid array, opening quote with existing recorded data" unless @recorded.empty?
352
+ # :nocov:
350
353
  while true
354
+ # :nocov:
351
355
  char = scan(/["\\]|[^"\\]+/)
352
356
  if char == '\\'
353
357
  @recorded << getch