sequel 5.30.0 → 5.35.0

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