sequel 5.30.0 → 5.35.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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +86 -0
  3. data/README.rdoc +1 -1
  4. data/doc/advanced_associations.rdoc +4 -4
  5. data/doc/association_basics.rdoc +10 -5
  6. data/doc/code_order.rdoc +12 -2
  7. data/doc/dataset_filtering.rdoc +2 -2
  8. data/doc/model_dataset_method_design.rdoc +1 -1
  9. data/doc/postgresql.rdoc +71 -0
  10. data/doc/release_notes/5.31.0.txt +148 -0
  11. data/doc/release_notes/5.32.0.txt +46 -0
  12. data/doc/release_notes/5.33.0.txt +24 -0
  13. data/doc/release_notes/5.34.0.txt +40 -0
  14. data/doc/release_notes/5.35.0.txt +56 -0
  15. data/doc/testing.rdoc +1 -1
  16. data/lib/sequel/adapters/oracle.rb +2 -1
  17. data/lib/sequel/adapters/shared/access.rb +6 -6
  18. data/lib/sequel/adapters/shared/mssql.rb +5 -5
  19. data/lib/sequel/adapters/shared/mysql.rb +9 -9
  20. data/lib/sequel/adapters/shared/oracle.rb +16 -16
  21. data/lib/sequel/adapters/shared/postgres.rb +169 -14
  22. data/lib/sequel/adapters/shared/sqlanywhere.rb +9 -9
  23. data/lib/sequel/adapters/shared/sqlite.rb +33 -6
  24. data/lib/sequel/adapters/tinytds.rb +1 -0
  25. data/lib/sequel/connection_pool/sharded_single.rb +4 -1
  26. data/lib/sequel/connection_pool/sharded_threaded.rb +12 -12
  27. data/lib/sequel/connection_pool/single.rb +1 -1
  28. data/lib/sequel/connection_pool/threaded.rb +2 -2
  29. data/lib/sequel/core.rb +318 -314
  30. data/lib/sequel/database/connecting.rb +1 -1
  31. data/lib/sequel/database/misc.rb +16 -10
  32. data/lib/sequel/database/query.rb +3 -1
  33. data/lib/sequel/database/schema_generator.rb +0 -1
  34. data/lib/sequel/database/schema_methods.rb +15 -16
  35. data/lib/sequel/database/transactions.rb +7 -4
  36. data/lib/sequel/dataset/placeholder_literalizer.rb +3 -7
  37. data/lib/sequel/dataset/query.rb +5 -4
  38. data/lib/sequel/deprecated.rb +3 -1
  39. data/lib/sequel/exceptions.rb +2 -0
  40. data/lib/sequel/extensions/_pretty_table.rb +1 -2
  41. data/lib/sequel/extensions/columns_introspection.rb +1 -2
  42. data/lib/sequel/extensions/connection_expiration.rb +2 -2
  43. data/lib/sequel/extensions/connection_validator.rb +2 -2
  44. data/lib/sequel/extensions/core_refinements.rb +2 -0
  45. data/lib/sequel/extensions/duplicate_columns_handler.rb +2 -0
  46. data/lib/sequel/extensions/fiber_concurrency.rb +24 -0
  47. data/lib/sequel/extensions/index_caching.rb +9 -7
  48. data/lib/sequel/extensions/integer64.rb +2 -0
  49. data/lib/sequel/extensions/migration.rb +1 -2
  50. data/lib/sequel/extensions/pg_array_ops.rb +4 -0
  51. data/lib/sequel/extensions/pg_enum.rb +7 -2
  52. data/lib/sequel/extensions/pg_extended_date_support.rb +1 -1
  53. data/lib/sequel/extensions/pg_hstore.rb +6 -0
  54. data/lib/sequel/extensions/pg_hstore_ops.rb +2 -0
  55. data/lib/sequel/extensions/pg_inet.rb +15 -5
  56. data/lib/sequel/extensions/pg_interval.rb +2 -0
  57. data/lib/sequel/extensions/pg_json_ops.rb +2 -0
  58. data/lib/sequel/extensions/pg_range.rb +5 -7
  59. data/lib/sequel/extensions/pg_range_ops.rb +2 -0
  60. data/lib/sequel/extensions/pg_row.rb +0 -1
  61. data/lib/sequel/extensions/pg_timestamptz.rb +2 -0
  62. data/lib/sequel/extensions/run_transaction_hooks.rb +72 -0
  63. data/lib/sequel/extensions/s.rb +2 -0
  64. data/lib/sequel/extensions/schema_dumper.rb +10 -4
  65. data/lib/sequel/extensions/server_block.rb +3 -3
  66. data/lib/sequel/extensions/symbol_aref_refinement.rb +2 -0
  67. data/lib/sequel/extensions/symbol_as_refinement.rb +2 -0
  68. data/lib/sequel/extensions/to_dot.rb +9 -3
  69. data/lib/sequel/model.rb +2 -0
  70. data/lib/sequel/model/associations.rb +54 -25
  71. data/lib/sequel/model/base.rb +70 -57
  72. data/lib/sequel/model/plugins.rb +3 -3
  73. data/lib/sequel/plugins/association_lazy_eager_option.rb +66 -0
  74. data/lib/sequel/plugins/association_multi_add_remove.rb +2 -0
  75. data/lib/sequel/plugins/association_pks.rb +60 -18
  76. data/lib/sequel/plugins/association_proxies.rb +2 -0
  77. data/lib/sequel/plugins/blacklist_security.rb +1 -2
  78. data/lib/sequel/plugins/boolean_subsets.rb +4 -1
  79. data/lib/sequel/plugins/class_table_inheritance.rb +28 -28
  80. data/lib/sequel/plugins/csv_serializer.rb +2 -0
  81. data/lib/sequel/plugins/dirty.rb +13 -13
  82. data/lib/sequel/plugins/forbid_lazy_load.rb +216 -0
  83. data/lib/sequel/plugins/instance_specific_default.rb +113 -0
  84. data/lib/sequel/plugins/json_serializer.rb +3 -7
  85. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  86. data/lib/sequel/plugins/pg_array_associations.rb +2 -3
  87. data/lib/sequel/plugins/prepared_statements.rb +5 -11
  88. data/lib/sequel/plugins/prepared_statements_safe.rb +1 -3
  89. data/lib/sequel/plugins/rcte_tree.rb +10 -16
  90. data/lib/sequel/plugins/single_table_inheritance.rb +15 -15
  91. data/lib/sequel/plugins/skip_saving_columns.rb +108 -0
  92. data/lib/sequel/plugins/string_stripper.rb +1 -1
  93. data/lib/sequel/plugins/subclasses.rb +2 -0
  94. data/lib/sequel/plugins/validation_class_methods.rb +5 -1
  95. data/lib/sequel/timezones.rb +6 -4
  96. data/lib/sequel/version.rb +1 -1
  97. metadata +18 -2
@@ -23,15 +23,6 @@ module Sequel
23
23
  to_application_timestamp(v.to_s) if v
24
24
  end
25
25
 
26
- # Convert smallint type to boolean if convert_smallint_to_bool is true
27
- def schema_column_type(db_type)
28
- if convert_smallint_to_bool && db_type =~ /smallint/i
29
- :boolean
30
- else
31
- super
32
- end
33
- end
34
-
35
26
  def schema_parse_table(table, opts)
36
27
  m = output_identifier_meth(opts[:dataset])
37
28
  im = input_identifier_meth(opts[:dataset])
@@ -216,6 +207,15 @@ module Sequel
216
207
  "ALTER TABLE #{quote_schema_table(name)} RENAME #{quote_schema_table(new_name)}"
217
208
  end
218
209
 
210
+ # Convert smallint type to boolean if convert_smallint_to_bool is true
211
+ def schema_column_type(db_type)
212
+ if convert_smallint_to_bool && db_type =~ /smallint/i
213
+ :boolean
214
+ else
215
+ super
216
+ end
217
+ end
218
+
219
219
  def tables_and_views(type, opts=OPTS)
220
220
  m = output_identifier_meth
221
221
  metadata_dataset.
@@ -14,6 +14,7 @@ module Sequel
14
14
  def schema_parse_table(*)
15
15
  []
16
16
  end
17
+ singleton_class.send(:private, :schema_parse_table)
17
18
  end
18
19
  end
19
20
 
@@ -37,6 +38,10 @@ module Sequel
37
38
  # booleans be stored as integers, but historically Sequel has used 't'/'f'.
38
39
  attr_accessor :integer_booleans
39
40
 
41
+ # Whether to keep CURRENT_TIMESTAMP and similar expressions in UTC. By
42
+ # default, the expressions are converted to localtime.
43
+ attr_accessor :current_timestamp_utc
44
+
40
45
  # A symbol signifying the value of the default transaction mode
41
46
  attr_reader :transaction_mode
42
47
 
@@ -184,7 +189,7 @@ module Sequel
184
189
 
185
190
  # Dataset used for parsing schema
186
191
  def _parse_pragma_ds(table_name, opts)
187
- metadata_dataset.with_sql("PRAGMA table_info(?)", input_identifier_meth(opts[:dataset]).call(table_name))
192
+ metadata_dataset.with_sql("PRAGMA table_#{'x' if sqlite_version > 33100}info(?)", input_identifier_meth(opts[:dataset]).call(table_name))
188
193
  end
189
194
 
190
195
  # Run all alter_table commands in a transaction. This is technically only
@@ -252,13 +257,20 @@ module Sequel
252
257
  when :drop_constraint
253
258
  case op[:type]
254
259
  when :primary_key
255
- duplicate_table(table){|columns| columns.each{|s| s[:primary_key] = s[:auto_increment] = nil}}
260
+ duplicate_table(table) do |columns|
261
+ columns.each do |s|
262
+ s[:unique] = false if s[:primary_key]
263
+ s[:primary_key] = s[:auto_increment] = nil
264
+ end
265
+ end
256
266
  when :foreign_key
257
267
  if op[:columns]
258
268
  duplicate_table(table, :skip_foreign_key_columns=>op[:columns])
259
269
  else
260
270
  duplicate_table(table, :no_foreign_keys=>true)
261
271
  end
272
+ when :unique
273
+ duplicate_table(table, :no_unique=>true)
262
274
  else
263
275
  duplicate_table(table)
264
276
  end
@@ -412,15 +424,19 @@ module Sequel
412
424
  skip_indexes = []
413
425
  indexes(table, :only_autocreated=>true).each do |name, h|
414
426
  skip_indexes << name
415
- if h[:columns].length == 1 && h[:unique]
416
- unique_columns.concat(h[:columns])
427
+ if h[:unique]
428
+ if h[:columns].length == 1
429
+ unique_columns.concat(h[:columns])
430
+ elsif h[:columns].map(&:to_s) != pks && !opts[:no_unique]
431
+ constraints << {:type=>:unique, :columns=>h[:columns]}
432
+ end
417
433
  end
418
434
  end
419
435
  unique_columns -= pks
420
436
  unless unique_columns.empty?
421
437
  unique_columns.map!{|c| quote_identifier(c)}
422
438
  def_columns.each do |c|
423
- c[:unique] = true if unique_columns.include?(quote_identifier(c[:name]))
439
+ c[:unique] = true if unique_columns.include?(quote_identifier(c[:name])) && c[:unique] != false
424
440
  end
425
441
  end
426
442
 
@@ -466,6 +482,15 @@ module Sequel
466
482
  def parse_pragma(table_name, opts)
467
483
  pks = 0
468
484
  sch = _parse_pragma_ds(table_name, opts).map do |row|
485
+ if sqlite_version > 33100
486
+ # table_xinfo PRAGMA used, remove hidden columns
487
+ # that are not generated columns
488
+ if row[:generated] = (row.delete(:hidden) != 0)
489
+ next unless row[:type].end_with?(' GENERATED ALWAYS')
490
+ row[:type] = row[:type].sub(' GENERATED ALWAYS', '')
491
+ end
492
+ end
493
+
469
494
  row.delete(:cid)
470
495
  row[:allow_null] = row.delete(:notnull).to_i == 0
471
496
  row[:default] = row.delete(:dflt_value)
@@ -482,6 +507,8 @@ module Sequel
482
507
  row
483
508
  end
484
509
 
510
+ sch.compact!
511
+
485
512
  if pks > 1
486
513
  # SQLite does not allow use of auto increment for tables
487
514
  # with composite primary keys, so remove auto_increment
@@ -598,7 +625,7 @@ module Sequel
598
625
  # SQLite has CURRENT_TIMESTAMP and related constants in UTC instead
599
626
  # of in localtime, so convert those constants to local time.
600
627
  def constant_sql_append(sql, constant)
601
- if c = CONSTANT_MAP[constant]
628
+ if (c = CONSTANT_MAP[constant]) && !db.current_timestamp_utc
602
629
  sql << c
603
630
  else
604
631
  super
@@ -16,6 +16,7 @@ module Sequel
16
16
  c = TinyTds::Client.new(opts)
17
17
  c.query_options.merge!(:cache_rows=>false)
18
18
 
19
+ # SEQUEL6: Default to ansi: true
19
20
  if opts[:ansi]
20
21
  sql = %w(
21
22
  ANSI_NULLS
@@ -41,7 +41,10 @@ class Sequel::ShardedSingleConnectionPool < Sequel::ConnectionPool
41
41
  # :server :: Should be a symbol specifing the server to disconnect from,
42
42
  # or an array of symbols to specify multiple servers.
43
43
  def disconnect(opts=OPTS)
44
- (opts[:server] ? Array(opts[:server]) : servers).each{|s| disconnect_server(s)}
44
+ (opts[:server] ? Array(opts[:server]) : servers).each do |s|
45
+ raise Sequel::Error, "invalid server: #{s}" unless @servers.has_key?(s)
46
+ disconnect_server(s)
47
+ end
45
48
  end
46
49
 
47
50
  def freeze
@@ -57,7 +57,7 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
57
57
  # it is yielding all of the connections, which means that until
58
58
  # the method's block returns, the pool is locked.
59
59
  def all_connections
60
- t = Thread.current
60
+ t = Sequel.current
61
61
  sync do
62
62
  @allocated.values.each do |threads|
63
63
  threads.each do |thread, conn|
@@ -95,9 +95,7 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
95
95
  # or an array of symbols to specify multiple servers.
96
96
  def disconnect(opts=OPTS)
97
97
  (opts[:server] ? Array(opts[:server]) : sync{@servers.keys}).each do |s|
98
- if conns = sync{disconnect_server_connections(s)}
99
- disconnect_connections(conns)
100
- end
98
+ disconnect_connections(sync{disconnect_server_connections(s)})
101
99
  end
102
100
  end
103
101
 
@@ -121,7 +119,7 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
121
119
  # connection can be acquired, a Sequel::PoolTimeout is raised.
122
120
  def hold(server=:default)
123
121
  server = pick_server(server)
124
- t = Thread.current
122
+ t = Sequel.current
125
123
  if conn = owned_connection(t, server)
126
124
  return yield(conn)
127
125
  end
@@ -203,9 +201,9 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
203
201
 
204
202
  until conn = assign_connection(thread, server)
205
203
  elapsed = Sequel.elapsed_seconds_since(timer)
204
+ # :nocov:
206
205
  raise_pool_timeout(elapsed, server) if elapsed > timeout
207
206
 
208
- # :nocov:
209
207
  # It's difficult to get to this point, it can only happen if there is a race condition
210
208
  # where a connection cannot be acquired even after the thread is signalled by the condition variable
211
209
  sync do
@@ -278,13 +276,15 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
278
276
  # Mark any allocated connections to be removed when they are checked back in. The calling
279
277
  # code should already have the mutex before calling this.
280
278
  def disconnect_server_connections(server)
281
- @connections_to_remove.concat(allocated(server).values)
279
+ remove_conns = allocated(server)
280
+ dis_conns = available_connections(server)
281
+ raise Sequel::Error, "invalid server: #{server}" unless remove_conns && dis_conns
282
282
 
283
- if dis_conns = available_connections(server)
284
- conns = dis_conns.dup
285
- dis_conns.clear
286
- @waiters[server].signal
287
- end
283
+ @connections_to_remove.concat(remove_conns.values)
284
+
285
+ conns = dis_conns.dup
286
+ dis_conns.clear
287
+ @waiters[server].signal
288
288
  conns
289
289
  end
290
290
 
@@ -11,7 +11,7 @@ class Sequel::SingleConnectionPool < Sequel::ConnectionPool
11
11
 
12
12
  # Yield the connection if one has been made.
13
13
  def all_connections
14
- yield @conn.first if @conn
14
+ yield @conn.first unless @conn.empty?
15
15
  end
16
16
 
17
17
  # Disconnect the connection from the database.
@@ -83,7 +83,7 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
83
83
  # is available or the timeout expires. If the timeout expires before a
84
84
  # connection can be acquired, a Sequel::PoolTimeout is raised.
85
85
  def hold(server=nil)
86
- t = Thread.current
86
+ t = Sequel.current
87
87
  if conn = owned_connection(t)
88
88
  return yield(conn)
89
89
  end
@@ -152,9 +152,9 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
152
152
 
153
153
  until conn = assign_connection(thread)
154
154
  elapsed = Sequel.elapsed_seconds_since(timer)
155
+ # :nocov:
155
156
  raise_pool_timeout(elapsed) if elapsed > timeout
156
157
 
157
- # :nocov:
158
158
  # It's difficult to get to this point, it can only happen if there is a race condition
159
159
  # where a connection cannot be acquired even after the thread is signalled by the condition variable
160
160
  sync do
@@ -30,7 +30,15 @@ module Sequel
30
30
  @split_symbols = false
31
31
  @single_threaded = false
32
32
 
33
- class << self
33
+ # Mutex used to protect mutable data structures
34
+ @data_mutex = Mutex.new
35
+
36
+ # Frozen hash used as the default options hash for most options.
37
+ OPTS = {}.freeze
38
+
39
+ SPLIT_SYMBOL_CACHE = {}
40
+
41
+ module SequelMethods
34
42
  # Sequel converts two digit years in <tt>Date</tt>s and <tt>DateTime</tt>s by default,
35
43
  # so 01/02/03 is interpreted at January 2nd, 2003, and 12/13/99 is interpreted
36
44
  # as December 13, 1999. You can override this to treat those dates as
@@ -63,361 +71,357 @@ module Sequel
63
71
  # require for backwards compatibility.
64
72
  alias orig_require require
65
73
  private :orig_require
66
- end
67
74
 
68
- # Returns true if the passed object could be a specifier of conditions, false otherwise.
69
- # Currently, Sequel considers hashes and arrays of two element arrays as
70
- # condition specifiers.
71
- #
72
- # Sequel.condition_specifier?({}) # => true
73
- # Sequel.condition_specifier?([[1, 2]]) # => true
74
- # Sequel.condition_specifier?([]) # => false
75
- # Sequel.condition_specifier?([1]) # => false
76
- # Sequel.condition_specifier?(1) # => false
77
- def self.condition_specifier?(obj)
78
- case obj
79
- when Hash
80
- true
81
- when Array
82
- !obj.empty? && !obj.is_a?(SQL::ValueList) && obj.all?{|i| i.is_a?(Array) && (i.length == 2)}
83
- else
84
- false
75
+ # Returns true if the passed object could be a specifier of conditions, false otherwise.
76
+ # Currently, Sequel considers hashes and arrays of two element arrays as
77
+ # condition specifiers.
78
+ #
79
+ # Sequel.condition_specifier?({}) # => true
80
+ # Sequel.condition_specifier?([[1, 2]]) # => true
81
+ # Sequel.condition_specifier?([]) # => false
82
+ # Sequel.condition_specifier?([1]) # => false
83
+ # Sequel.condition_specifier?(1) # => false
84
+ def condition_specifier?(obj)
85
+ case obj
86
+ when Hash
87
+ true
88
+ when Array
89
+ !obj.empty? && !obj.is_a?(SQL::ValueList) && obj.all?{|i| i.is_a?(Array) && (i.length == 2)}
90
+ else
91
+ false
92
+ end
85
93
  end
86
- end
87
94
 
88
- # Frozen hash used as the default options hash for most options.
89
- OPTS = {}.freeze
95
+ # Creates a new database object based on the supplied connection string
96
+ # and optional arguments. The specified scheme determines the database
97
+ # class used, and the rest of the string specifies the connection options.
98
+ # For example:
99
+ #
100
+ # DB = Sequel.connect('sqlite:/') # Memory database
101
+ # DB = Sequel.connect('sqlite://blog.db') # ./blog.db
102
+ # DB = Sequel.connect('sqlite:///blog.db') # /blog.db
103
+ # DB = Sequel.connect('postgres://user:password@host:port/database_name')
104
+ # DB = Sequel.connect('sqlite:///blog.db', max_connections: 10)
105
+ #
106
+ # You can also pass a single options hash:
107
+ #
108
+ # DB = Sequel.connect(adapter: 'sqlite', database: './blog.db')
109
+ #
110
+ # If a block is given, it is passed the opened +Database+ object, which is
111
+ # closed when the block exits. For example:
112
+ #
113
+ # Sequel.connect('sqlite://blog.db'){|db| puts db[:users].count}
114
+ #
115
+ # If a block is not given, a reference to this database will be held in
116
+ # <tt>Sequel::DATABASES</tt> until it is removed manually. This is by
117
+ # design, and used by <tt>Sequel::Model</tt> to pick the default
118
+ # database. It is recommended to pass a block if you do not want the
119
+ # resulting Database object to remain in memory until the process
120
+ # terminates, or use the <tt>keep_reference: false</tt> Database option.
121
+ #
122
+ # For details, see the {"Connecting to a Database" guide}[rdoc-ref:doc/opening_databases.rdoc].
123
+ # To set up a primary/replica or sharded database connection, see the {"Primary/Replica Database Configurations and Sharding" guide}[rdoc-ref:doc/sharding.rdoc].
124
+ def connect(*args, &block)
125
+ Database.connect(*args, &block)
126
+ end
90
127
 
91
- # Creates a new database object based on the supplied connection string
92
- # and optional arguments. The specified scheme determines the database
93
- # class used, and the rest of the string specifies the connection options.
94
- # For example:
95
- #
96
- # DB = Sequel.connect('sqlite:/') # Memory database
97
- # DB = Sequel.connect('sqlite://blog.db') # ./blog.db
98
- # DB = Sequel.connect('sqlite:///blog.db') # /blog.db
99
- # DB = Sequel.connect('postgres://user:password@host:port/database_name')
100
- # DB = Sequel.connect('sqlite:///blog.db', max_connections: 10)
101
- #
102
- # You can also pass a single options hash:
103
- #
104
- # DB = Sequel.connect(adapter: 'sqlite', database: './blog.db')
105
- #
106
- # If a block is given, it is passed the opened +Database+ object, which is
107
- # closed when the block exits. For example:
108
- #
109
- # Sequel.connect('sqlite://blog.db'){|db| puts db[:users].count}
110
- #
111
- # If a block is not given, a reference to this database will be held in
112
- # <tt>Sequel::DATABASES</tt> until it is removed manually. This is by
113
- # design, and used by <tt>Sequel::Model</tt> to pick the default
114
- # database. It is recommended to pass a block if you do not want the
115
- # resulting Database object to remain in memory until the process
116
- # terminates, or use the <tt>keep_reference: false</tt> Database option.
117
- #
118
- # For details, see the {"Connecting to a Database" guide}[rdoc-ref:doc/opening_databases.rdoc].
119
- # To set up a primary/replica or sharded database connection, see the {"Primary/Replica Database Configurations and Sharding" guide}[rdoc-ref:doc/sharding.rdoc].
120
- def self.connect(*args, &block)
121
- Database.connect(*args, &block)
122
- end
128
+ # Assume the core extensions are not loaded by default, if the core_extensions
129
+ # extension is loaded, this will be overridden.
130
+ def core_extensions?
131
+ false
132
+ end
123
133
 
124
- # Assume the core extensions are not loaded by default, if the core_extensions
125
- # extension is loaded, this will be overridden.
126
- def self.core_extensions?
127
- false
128
- end
134
+ # Convert the +exception+ to the given class. The given class should be
135
+ # <tt>Sequel::Error</tt> or a subclass. Returns an instance of +klass+ with
136
+ # the message and backtrace of +exception+.
137
+ def convert_exception_class(exception, klass)
138
+ return exception if exception.is_a?(klass)
139
+ e = klass.new("#{exception.class}: #{exception.message}")
140
+ e.wrapped_exception = exception
141
+ e.set_backtrace(exception.backtrace)
142
+ e
143
+ end
129
144
 
130
- # Convert the +exception+ to the given class. The given class should be
131
- # <tt>Sequel::Error</tt> or a subclass. Returns an instance of +klass+ with
132
- # the message and backtrace of +exception+.
133
- def self.convert_exception_class(exception, klass)
134
- return exception if exception.is_a?(klass)
135
- e = klass.new("#{exception.class}: #{exception.message}")
136
- e.wrapped_exception = exception
137
- e.set_backtrace(exception.backtrace)
138
- e
139
- end
145
+ # The current concurrency primitive, Thread.current by default.
146
+ def current
147
+ Thread.current
148
+ end
140
149
 
141
- # Load all Sequel extensions given. Extensions are just files that exist under
142
- # <tt>sequel/extensions</tt> in the load path, and are just required.
143
- # In some cases, requiring an extension modifies classes directly, and in others,
144
- # it just loads a module that you can extend other classes with. Consult the documentation
145
- # for each extension you plan on using for usage.
146
- #
147
- # Sequel.extension(:blank)
148
- # Sequel.extension(:core_extensions, :named_timezones)
149
- def self.extension(*extensions)
150
- extensions.each{|e| orig_require("sequel/extensions/#{e}")}
151
- end
152
-
153
- # The exception classed raised if there is an error parsing JSON.
154
- # This can be overridden to use an alternative json implementation.
155
- def self.json_parser_error_class
156
- JSON::ParserError
157
- end
150
+ # Load all Sequel extensions given. Extensions are just files that exist under
151
+ # <tt>sequel/extensions</tt> in the load path, and are just required.
152
+ # In some cases, requiring an extension modifies classes directly, and in others,
153
+ # it just loads a module that you can extend other classes with. Consult the documentation
154
+ # for each extension you plan on using for usage.
155
+ #
156
+ # Sequel.extension(:blank)
157
+ # Sequel.extension(:core_extensions, :named_timezones)
158
+ def extension(*extensions)
159
+ extensions.each{|e| orig_require("sequel/extensions/#{e}")}
160
+ end
161
+
162
+ # The exception classed raised if there is an error parsing JSON.
163
+ # This can be overridden to use an alternative json implementation.
164
+ def json_parser_error_class
165
+ JSON::ParserError
166
+ end
158
167
 
159
- # Convert given object to json and return the result.
160
- # This can be overridden to use an alternative json implementation.
161
- def self.object_to_json(obj, *args, &block)
162
- obj.to_json(*args, &block)
163
- end
168
+ # Convert given object to json and return the result.
169
+ # This can be overridden to use an alternative json implementation.
170
+ def object_to_json(obj, *args, &block)
171
+ obj.to_json(*args, &block)
172
+ end
164
173
 
165
- # Parse the string as JSON and return the result.
166
- # This can be overridden to use an alternative json implementation.
167
- def self.parse_json(json)
168
- JSON.parse(json, :create_additions=>false)
169
- end
174
+ # Parse the string as JSON and return the result.
175
+ # This can be overridden to use an alternative json implementation.
176
+ def parse_json(json)
177
+ JSON.parse(json, :create_additions=>false)
178
+ end
170
179
 
171
- # Convert each item in the array to the correct type, handling multi-dimensional
172
- # arrays. For each element in the array or subarrays, call the converter,
173
- # unless the value is nil.
174
- def self.recursive_map(array, converter)
175
- array.map do |i|
176
- if i.is_a?(Array)
177
- recursive_map(i, converter)
178
- elsif !i.nil?
179
- converter.call(i)
180
+ # Convert each item in the array to the correct type, handling multi-dimensional
181
+ # arrays. For each element in the array or subarrays, call the converter,
182
+ # unless the value is nil.
183
+ def recursive_map(array, converter)
184
+ array.map do |i|
185
+ if i.is_a?(Array)
186
+ recursive_map(i, converter)
187
+ elsif !i.nil?
188
+ converter.call(i)
189
+ end
180
190
  end
181
191
  end
182
- end
183
-
184
- # For backwards compatibility only. require_relative should be used instead.
185
- def self.require(files, subdir=nil)
186
- # Use Kernel.require_relative to work around JRuby 9.0 bug
187
- Array(files).each{|f| Kernel.require_relative "#{"#{subdir}/" if subdir}#{f}"}
188
- end
189
192
 
190
- SPLIT_SYMBOL_CACHE = {}
193
+ # For backwards compatibility only. require_relative should be used instead.
194
+ def require(files, subdir=nil)
195
+ # Use Kernel.require_relative to work around JRuby 9.0 bug
196
+ Array(files).each{|f| Kernel.require_relative "#{"#{subdir}/" if subdir}#{f}"}
197
+ end
191
198
 
192
- # Splits the symbol into three parts, if symbol splitting is enabled (not the default).
193
- # Each part will either be a string or nil. If symbol splitting
194
- # is disabled, returns an array with the first and third parts
195
- # being nil, and the second part beind a string version of the symbol.
196
- #
197
- # For columns, these parts are the table, column, and alias.
198
- # For tables, these parts are the schema, table, and alias.
199
- def self.split_symbol(sym)
200
- unless v = Sequel.synchronize{SPLIT_SYMBOL_CACHE[sym]}
201
- if split_symbols?
202
- v = case s = sym.to_s
203
- when /\A((?:(?!__).)+)__((?:(?!___).)+)___(.+)\z/
204
- [$1.freeze, $2.freeze, $3.freeze].freeze
205
- when /\A((?:(?!___).)+)___(.+)\z/
206
- [nil, $1.freeze, $2.freeze].freeze
207
- when /\A((?:(?!__).)+)__(.+)\z/
208
- [$1.freeze, $2.freeze, nil].freeze
199
+ # Splits the symbol into three parts, if symbol splitting is enabled (not the default).
200
+ # Each part will either be a string or nil. If symbol splitting
201
+ # is disabled, returns an array with the first and third parts
202
+ # being nil, and the second part beind a string version of the symbol.
203
+ #
204
+ # For columns, these parts are the table, column, and alias.
205
+ # For tables, these parts are the schema, table, and alias.
206
+ def split_symbol(sym)
207
+ unless v = Sequel.synchronize{SPLIT_SYMBOL_CACHE[sym]}
208
+ if split_symbols?
209
+ v = case s = sym.to_s
210
+ when /\A((?:(?!__).)+)__((?:(?!___).)+)___(.+)\z/
211
+ [$1.freeze, $2.freeze, $3.freeze].freeze
212
+ when /\A((?:(?!___).)+)___(.+)\z/
213
+ [nil, $1.freeze, $2.freeze].freeze
214
+ when /\A((?:(?!__).)+)__(.+)\z/
215
+ [$1.freeze, $2.freeze, nil].freeze
216
+ else
217
+ [nil, s.freeze, nil].freeze
218
+ end
209
219
  else
210
- [nil, s.freeze, nil].freeze
220
+ v = [nil,sym.to_s.freeze,nil].freeze
211
221
  end
212
- else
213
- v = [nil,sym.to_s.freeze,nil].freeze
222
+ Sequel.synchronize{SPLIT_SYMBOL_CACHE[sym] = v}
214
223
  end
215
- Sequel.synchronize{SPLIT_SYMBOL_CACHE[sym] = v}
224
+ v
216
225
  end
217
- v
218
- end
219
-
220
- # Setting this to true enables Sequel's historical behavior of splitting
221
- # symbols on double or triple underscores:
222
- #
223
- # :table__column # table.column
224
- # :column___alias # column AS alias
225
- # :table__column___alias # table.column AS alias
226
- #
227
- # It is only recommended to turn this on for backwards compatibility until
228
- # such symbols have been converted to use newer Sequel APIs such as:
229
- #
230
- # Sequel[:table][:column] # table.column
231
- # Sequel[:column].as(:alias) # column AS alias
232
- # Sequel[:table][:column].as(:alias) # table.column AS alias
233
- #
234
- # Sequel::Database instances do their own caching of literalized
235
- # symbols, and changing this setting does not affect those caches. It is
236
- # recommended that if you want to change this setting, you do so directly
237
- # after requiring Sequel, before creating any Sequel::Database instances.
238
- #
239
- # Disabling symbol splitting will also disable the handling
240
- # of double underscores in virtual row methods, causing such methods to
241
- # yield regular identifers instead of qualified identifiers:
242
- #
243
- # # Sequel.split_symbols = true
244
- # Sequel.expr{table__column} # table.column
245
- # Sequel.expr{table[:column]} # table.column
246
- #
247
- # # Sequel.split_symbols = false
248
- # Sequel.expr{table__column} # table__column
249
- # Sequel.expr{table[:column]} # table.column
250
- def self.split_symbols=(v)
251
- Sequel.synchronize{SPLIT_SYMBOL_CACHE.clear}
252
- @split_symbols = v
253
- end
254
226
 
255
- # Whether Sequel currently splits symbols into qualified/aliased identifiers.
256
- def self.split_symbols?
257
- @split_symbols
258
- end
227
+ # Setting this to true enables Sequel's historical behavior of splitting
228
+ # symbols on double or triple underscores:
229
+ #
230
+ # :table__column # table.column
231
+ # :column___alias # column AS alias
232
+ # :table__column___alias # table.column AS alias
233
+ #
234
+ # It is only recommended to turn this on for backwards compatibility until
235
+ # such symbols have been converted to use newer Sequel APIs such as:
236
+ #
237
+ # Sequel[:table][:column] # table.column
238
+ # Sequel[:column].as(:alias) # column AS alias
239
+ # Sequel[:table][:column].as(:alias) # table.column AS alias
240
+ #
241
+ # Sequel::Database instances do their own caching of literalized
242
+ # symbols, and changing this setting does not affect those caches. It is
243
+ # recommended that if you want to change this setting, you do so directly
244
+ # after requiring Sequel, before creating any Sequel::Database instances.
245
+ #
246
+ # Disabling symbol splitting will also disable the handling
247
+ # of double underscores in virtual row methods, causing such methods to
248
+ # yield regular identifers instead of qualified identifiers:
249
+ #
250
+ # # Sequel.split_symbols = true
251
+ # Sequel.expr{table__column} # table.column
252
+ # Sequel.expr{table[:column]} # table.column
253
+ #
254
+ # # Sequel.split_symbols = false
255
+ # Sequel.expr{table__column} # table__column
256
+ # Sequel.expr{table[:column]} # table.column
257
+ def split_symbols=(v)
258
+ Sequel.synchronize{SPLIT_SYMBOL_CACHE.clear}
259
+ @split_symbols = v
260
+ end
259
261
 
260
- # Converts the given +string+ into a +Date+ object.
261
- #
262
- # Sequel.string_to_date('2010-09-10') # Date.civil(2010, 09, 10)
263
- def self.string_to_date(string)
264
- begin
265
- Date.parse(string, Sequel.convert_two_digit_years)
266
- rescue => e
267
- raise convert_exception_class(e, InvalidValue)
262
+ # Whether Sequel currently splits symbols into qualified/aliased identifiers.
263
+ def split_symbols?
264
+ @split_symbols
268
265
  end
269
- end
270
266
 
271
- # Converts the given +string+ into a +Time+ or +DateTime+ object, depending on the
272
- # value of <tt>Sequel.datetime_class</tt>.
273
- #
274
- # Sequel.string_to_datetime('2010-09-10 10:20:30') # Time.local(2010, 09, 10, 10, 20, 30)
275
- def self.string_to_datetime(string)
276
- begin
277
- if datetime_class == DateTime
278
- DateTime.parse(string, convert_two_digit_years)
279
- else
280
- datetime_class.parse(string)
267
+ # Converts the given +string+ into a +Date+ object.
268
+ #
269
+ # Sequel.string_to_date('2010-09-10') # Date.civil(2010, 09, 10)
270
+ def string_to_date(string)
271
+ begin
272
+ Date.parse(string, Sequel.convert_two_digit_years)
273
+ rescue => e
274
+ raise convert_exception_class(e, InvalidValue)
281
275
  end
282
- rescue => e
283
- raise convert_exception_class(e, InvalidValue)
284
276
  end
285
- end
286
277
 
287
- # Converts the given +string+ into a <tt>Sequel::SQLTime</tt> object.
288
- #
289
- # v = Sequel.string_to_time('10:20:30') # Sequel::SQLTime.parse('10:20:30')
290
- # DB.literal(v) # => '10:20:30'
291
- def self.string_to_time(string)
292
- begin
293
- SQLTime.parse(string)
294
- rescue => e
295
- raise convert_exception_class(e, InvalidValue)
278
+ # Converts the given +string+ into a +Time+ or +DateTime+ object, depending on the
279
+ # value of <tt>Sequel.datetime_class</tt>.
280
+ #
281
+ # Sequel.string_to_datetime('2010-09-10 10:20:30') # Time.local(2010, 09, 10, 10, 20, 30)
282
+ def string_to_datetime(string)
283
+ begin
284
+ if datetime_class == DateTime
285
+ DateTime.parse(string, convert_two_digit_years)
286
+ else
287
+ datetime_class.parse(string)
288
+ end
289
+ rescue => e
290
+ raise convert_exception_class(e, InvalidValue)
291
+ end
296
292
  end
297
- end
298
-
299
- # Mutex used to protect mutable data structures
300
- @data_mutex = Mutex.new
301
-
302
- # Unless in single threaded mode, protects access to any mutable
303
- # global data structure in Sequel.
304
- # Uses a non-reentrant mutex, so calling code should be careful.
305
- # In general, this should only be used around the minimal possible code
306
- # such as Hash#[], Hash#[]=, Hash#delete, Array#<<, and Array#delete.
307
- def self.synchronize(&block)
308
- @single_threaded ? yield : @data_mutex.synchronize(&block)
309
- end
310
293
 
311
- if RUBY_VERSION >= '2.1'
312
- # A timer object that can be passed to Sequel.elapsed_seconds_since
313
- # to return the number of seconds elapsed.
314
- def self.start_timer
315
- Process.clock_gettime(Process::CLOCK_MONOTONIC)
294
+ # Converts the given +string+ into a <tt>Sequel::SQLTime</tt> object.
295
+ #
296
+ # v = Sequel.string_to_time('10:20:30') # Sequel::SQLTime.parse('10:20:30')
297
+ # DB.literal(v) # => '10:20:30'
298
+ def string_to_time(string)
299
+ begin
300
+ SQLTime.parse(string)
301
+ rescue => e
302
+ raise convert_exception_class(e, InvalidValue)
303
+ end
316
304
  end
317
- else
318
- # :nocov:
319
- def self.start_timer # :nodoc:
320
- Time.now
305
+
306
+ # Unless in single threaded mode, protects access to any mutable
307
+ # global data structure in Sequel.
308
+ # Uses a non-reentrant mutex, so calling code should be careful.
309
+ # In general, this should only be used around the minimal possible code
310
+ # such as Hash#[], Hash#[]=, Hash#delete, Array#<<, and Array#delete.
311
+ def synchronize(&block)
312
+ @single_threaded ? yield : @data_mutex.synchronize(&block)
321
313
  end
322
- # :nocov:
323
- end
324
314
 
325
- # The elapsed seconds since the given timer object was created. The
326
- # timer object should have been created via Sequel.start_timer.
327
- def self.elapsed_seconds_since(timer)
328
- start_timer - timer
329
- end
315
+ if RUBY_VERSION >= '2.1'
316
+ # A timer object that can be passed to Sequel.elapsed_seconds_since
317
+ # to return the number of seconds elapsed.
318
+ def start_timer
319
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
320
+ end
321
+ else
322
+ # :nocov:
323
+ def start_timer # :nodoc:
324
+ Time.now
325
+ end
326
+ # :nocov:
327
+ end
330
328
 
331
- # Uses a transaction on all given databases with the given options. This:
332
- #
333
- # Sequel.transaction([DB1, DB2, DB3]){}
334
- #
335
- # is equivalent to:
336
- #
337
- # DB1.transaction do
338
- # DB2.transaction do
339
- # DB3.transaction do
340
- # end
341
- # end
342
- # end
343
- #
344
- # except that if Sequel::Rollback is raised by the block, the transaction is
345
- # rolled back on all databases instead of just the last one.
346
- #
347
- # Note that this method cannot guarantee that all databases will commit or
348
- # rollback. For example, if DB3 commits but attempting to commit on DB2
349
- # fails (maybe because foreign key checks are deferred), there is no way
350
- # to uncommit the changes on DB3. For that kind of support, you need to
351
- # have two-phase commit/prepared transactions (which Sequel supports on
352
- # some databases).
353
- def self.transaction(dbs, opts=OPTS, &block)
354
- unless opts[:rollback]
355
- rescue_rollback = true
356
- opts = Hash[opts].merge!(:rollback=>:reraise)
329
+ # The elapsed seconds since the given timer object was created. The
330
+ # timer object should have been created via Sequel.start_timer.
331
+ def elapsed_seconds_since(timer)
332
+ start_timer - timer
357
333
  end
358
- pr = dbs.reverse.inject(block){|bl, db| proc{db.transaction(opts, &bl)}}
359
- if rescue_rollback
360
- begin
334
+
335
+ # Uses a transaction on all given databases with the given options. This:
336
+ #
337
+ # Sequel.transaction([DB1, DB2, DB3]){}
338
+ #
339
+ # is equivalent to:
340
+ #
341
+ # DB1.transaction do
342
+ # DB2.transaction do
343
+ # DB3.transaction do
344
+ # end
345
+ # end
346
+ # end
347
+ #
348
+ # except that if Sequel::Rollback is raised by the block, the transaction is
349
+ # rolled back on all databases instead of just the last one.
350
+ #
351
+ # Note that this method cannot guarantee that all databases will commit or
352
+ # rollback. For example, if DB3 commits but attempting to commit on DB2
353
+ # fails (maybe because foreign key checks are deferred), there is no way
354
+ # to uncommit the changes on DB3. For that kind of support, you need to
355
+ # have two-phase commit/prepared transactions (which Sequel supports on
356
+ # some databases).
357
+ def transaction(dbs, opts=OPTS, &block)
358
+ unless opts[:rollback]
359
+ rescue_rollback = true
360
+ opts = Hash[opts].merge!(:rollback=>:reraise)
361
+ end
362
+ pr = dbs.reverse.inject(block){|bl, db| proc{db.transaction(opts, &bl)}}
363
+ if rescue_rollback
364
+ begin
365
+ pr.call
366
+ rescue Sequel::Rollback
367
+ nil
368
+ end
369
+ else
361
370
  pr.call
362
- rescue Sequel::Rollback
363
- nil
364
371
  end
365
- else
366
- pr.call
367
372
  end
368
- end
369
373
 
370
- # If the supplied block takes a single argument,
371
- # yield an <tt>SQL::VirtualRow</tt> instance to the block
372
- # argument. Otherwise, evaluate the block in the context of a
373
- # <tt>SQL::VirtualRow</tt> instance.
374
- #
375
- # Sequel.virtual_row{a} # Sequel::SQL::Identifier.new(:a)
376
- # Sequel.virtual_row{|o| o.a} # Sequel::SQL::Function.new(:a)
377
- def self.virtual_row(&block)
378
- vr = VIRTUAL_ROW
379
- case block.arity
380
- when -1, 0
381
- vr.instance_exec(&block)
382
- else
383
- block.call(vr)
384
- end
385
- end
374
+ # If the supplied block takes a single argument,
375
+ # yield an <tt>SQL::VirtualRow</tt> instance to the block
376
+ # argument. Otherwise, evaluate the block in the context of a
377
+ # <tt>SQL::VirtualRow</tt> instance.
378
+ #
379
+ # Sequel.virtual_row{a} # Sequel::SQL::Identifier.new(:a)
380
+ # Sequel.virtual_row{|o| o.a} # Sequel::SQL::Function.new(:a)
381
+ def virtual_row(&block)
382
+ vr = VIRTUAL_ROW
383
+ case block.arity
384
+ when -1, 0
385
+ vr.instance_exec(&block)
386
+ else
387
+ block.call(vr)
388
+ end
389
+ end
386
390
 
387
- ### Private Class Methods ###
391
+ private
388
392
 
389
- # Helper method that the database adapter class methods that are added to Sequel via
390
- # metaprogramming use to parse arguments.
391
- def self.adapter_method(adapter, *args, &block)
392
- options = args.last.is_a?(Hash) ? args.pop : OPTS
393
- opts = {:adapter => adapter.to_sym}
394
- opts[:database] = args.shift if args.first.is_a?(String)
395
- if args.any?
396
- raise ::Sequel::Error, "Wrong format of arguments, either use (), (String), (Hash), or (String, Hash)"
397
- end
393
+ # Helper method that the database adapter class methods that are added to Sequel via
394
+ # metaprogramming use to parse arguments.
395
+ def adapter_method(adapter, *args, &block)
396
+ options = args.last.is_a?(Hash) ? args.pop : OPTS
397
+ opts = {:adapter => adapter.to_sym}
398
+ opts[:database] = args.shift if args.first.is_a?(String)
399
+ if args.any?
400
+ raise ::Sequel::Error, "Wrong format of arguments, either use (), (String), (Hash), or (String, Hash)"
401
+ end
398
402
 
399
- connect(opts.merge(options), &block)
400
- end
403
+ connect(opts.merge(options), &block)
404
+ end
401
405
 
402
- # Method that adds a database adapter class method to Sequel that calls
403
- # Sequel.adapter_method.
404
- def self.def_adapter_method(*adapters) # :nodoc:
405
- adapters.each do |adapter|
406
- define_singleton_method(adapter){|*args, &block| adapter_method(adapter, *args, &block)}
406
+ # Method that adds a database adapter class method to Sequel that calls
407
+ # Sequel.adapter_method.
408
+ def def_adapter_method(*adapters) # :nodoc:
409
+ adapters.each do |adapter|
410
+ define_singleton_method(adapter){|*args, &block| adapter_method(adapter, *args, &block)}
411
+ end
407
412
  end
408
413
  end
409
-
410
- private_class_method :adapter_method, :def_adapter_method
411
-
412
- require_relative "deprecated"
413
- require_relative "sql"
414
- require_relative "connection_pool"
415
- require_relative "exceptions"
416
- require_relative "dataset"
417
- require_relative "database"
418
- require_relative "timezones"
419
- require_relative "ast_transformer"
420
- require_relative "version"
414
+ extend SequelMethods
415
+
416
+ require_relative "deprecated"
417
+ require_relative "sql"
418
+ require_relative "connection_pool"
419
+ require_relative "exceptions"
420
+ require_relative "dataset"
421
+ require_relative "database"
422
+ require_relative "timezones"
423
+ require_relative "ast_transformer"
424
+ require_relative "version"
421
425
 
422
426
  class << self
423
427
  # Allow nicer syntax for creating Sequel expressions: