sequel 5.58.0 → 5.78.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 (161) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +288 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +24 -23
  5. data/bin/sequel +11 -3
  6. data/doc/advanced_associations.rdoc +16 -14
  7. data/doc/association_basics.rdoc +53 -17
  8. data/doc/cheat_sheet.rdoc +3 -3
  9. data/doc/mass_assignment.rdoc +1 -1
  10. data/doc/migration.rdoc +15 -0
  11. data/doc/model_hooks.rdoc +1 -1
  12. data/doc/object_model.rdoc +8 -8
  13. data/doc/opening_databases.rdoc +20 -12
  14. data/doc/postgresql.rdoc +8 -8
  15. data/doc/querying.rdoc +1 -1
  16. data/doc/release_notes/5.59.0.txt +73 -0
  17. data/doc/release_notes/5.60.0.txt +22 -0
  18. data/doc/release_notes/5.61.0.txt +43 -0
  19. data/doc/release_notes/5.62.0.txt +132 -0
  20. data/doc/release_notes/5.63.0.txt +33 -0
  21. data/doc/release_notes/5.64.0.txt +50 -0
  22. data/doc/release_notes/5.65.0.txt +21 -0
  23. data/doc/release_notes/5.66.0.txt +24 -0
  24. data/doc/release_notes/5.67.0.txt +32 -0
  25. data/doc/release_notes/5.68.0.txt +61 -0
  26. data/doc/release_notes/5.69.0.txt +26 -0
  27. data/doc/release_notes/5.70.0.txt +35 -0
  28. data/doc/release_notes/5.71.0.txt +21 -0
  29. data/doc/release_notes/5.72.0.txt +33 -0
  30. data/doc/release_notes/5.73.0.txt +66 -0
  31. data/doc/release_notes/5.74.0.txt +45 -0
  32. data/doc/release_notes/5.75.0.txt +35 -0
  33. data/doc/release_notes/5.76.0.txt +86 -0
  34. data/doc/release_notes/5.77.0.txt +63 -0
  35. data/doc/release_notes/5.78.0.txt +67 -0
  36. data/doc/schema_modification.rdoc +3 -3
  37. data/doc/security.rdoc +9 -9
  38. data/doc/sharding.rdoc +3 -1
  39. data/doc/sql.rdoc +14 -14
  40. data/doc/testing.rdoc +16 -12
  41. data/doc/transactions.rdoc +6 -6
  42. data/doc/virtual_rows.rdoc +1 -1
  43. data/lib/sequel/adapters/ibmdb.rb +1 -1
  44. data/lib/sequel/adapters/jdbc/h2.rb +3 -0
  45. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -0
  46. data/lib/sequel/adapters/jdbc/postgresql.rb +3 -0
  47. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
  48. data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
  49. data/lib/sequel/adapters/jdbc.rb +10 -6
  50. data/lib/sequel/adapters/mysql.rb +19 -7
  51. data/lib/sequel/adapters/mysql2.rb +2 -2
  52. data/lib/sequel/adapters/odbc/mssql.rb +1 -1
  53. data/lib/sequel/adapters/oracle.rb +1 -0
  54. data/lib/sequel/adapters/postgres.rb +62 -16
  55. data/lib/sequel/adapters/shared/access.rb +9 -1
  56. data/lib/sequel/adapters/shared/db2.rb +12 -0
  57. data/lib/sequel/adapters/shared/mssql.rb +71 -9
  58. data/lib/sequel/adapters/shared/mysql.rb +80 -1
  59. data/lib/sequel/adapters/shared/oracle.rb +17 -7
  60. data/lib/sequel/adapters/shared/postgres.rb +494 -164
  61. data/lib/sequel/adapters/shared/sqlanywhere.rb +18 -5
  62. data/lib/sequel/adapters/shared/sqlite.rb +40 -4
  63. data/lib/sequel/adapters/sqlite.rb +42 -3
  64. data/lib/sequel/adapters/trilogy.rb +117 -0
  65. data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
  66. data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
  67. data/lib/sequel/connection_pool/threaded.rb +14 -8
  68. data/lib/sequel/connection_pool/timed_queue.rb +270 -0
  69. data/lib/sequel/connection_pool.rb +57 -31
  70. data/lib/sequel/database/connecting.rb +25 -1
  71. data/lib/sequel/database/dataset.rb +16 -6
  72. data/lib/sequel/database/misc.rb +65 -14
  73. data/lib/sequel/database/query.rb +72 -1
  74. data/lib/sequel/database/schema_generator.rb +2 -1
  75. data/lib/sequel/database/schema_methods.rb +13 -3
  76. data/lib/sequel/database/transactions.rb +6 -0
  77. data/lib/sequel/dataset/actions.rb +60 -13
  78. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
  79. data/lib/sequel/dataset/features.rb +15 -1
  80. data/lib/sequel/dataset/misc.rb +12 -2
  81. data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
  82. data/lib/sequel/dataset/query.rb +62 -37
  83. data/lib/sequel/dataset/sql.rb +58 -36
  84. data/lib/sequel/dataset.rb +4 -0
  85. data/lib/sequel/exceptions.rb +5 -0
  86. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  87. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  88. data/lib/sequel/extensions/any_not_empty.rb +2 -2
  89. data/lib/sequel/extensions/async_thread_pool.rb +21 -13
  90. data/lib/sequel/extensions/auto_cast_date_and_time.rb +94 -0
  91. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  92. data/lib/sequel/extensions/connection_expiration.rb +15 -9
  93. data/lib/sequel/extensions/connection_validator.rb +16 -11
  94. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  95. data/lib/sequel/extensions/date_arithmetic.rb +36 -8
  96. data/lib/sequel/extensions/duplicate_columns_handler.rb +10 -9
  97. data/lib/sequel/extensions/index_caching.rb +5 -1
  98. data/lib/sequel/extensions/is_distinct_from.rb +3 -1
  99. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  100. data/lib/sequel/extensions/migration.rb +65 -15
  101. data/lib/sequel/extensions/named_timezones.rb +22 -6
  102. data/lib/sequel/extensions/pg_array.rb +33 -4
  103. data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
  104. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
  105. data/lib/sequel/extensions/pg_enum.rb +1 -2
  106. data/lib/sequel/extensions/pg_extended_date_support.rb +38 -27
  107. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  108. data/lib/sequel/extensions/pg_hstore.rb +5 -0
  109. data/lib/sequel/extensions/pg_inet.rb +10 -11
  110. data/lib/sequel/extensions/pg_interval.rb +10 -11
  111. data/lib/sequel/extensions/pg_json.rb +10 -10
  112. data/lib/sequel/extensions/pg_json_ops.rb +52 -0
  113. data/lib/sequel/extensions/pg_multirange.rb +6 -11
  114. data/lib/sequel/extensions/pg_range.rb +9 -14
  115. data/lib/sequel/extensions/pg_row.rb +20 -19
  116. data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
  117. data/lib/sequel/extensions/round_timestamps.rb +1 -1
  118. data/lib/sequel/extensions/schema_caching.rb +1 -1
  119. data/lib/sequel/extensions/schema_dumper.rb +32 -9
  120. data/lib/sequel/extensions/server_block.rb +2 -1
  121. data/lib/sequel/extensions/set_literalizer.rb +58 -0
  122. data/lib/sequel/extensions/sqlite_json_ops.rb +76 -18
  123. data/lib/sequel/extensions/symbol_aref.rb +2 -0
  124. data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
  125. data/lib/sequel/model/associations.rb +50 -11
  126. data/lib/sequel/model/base.rb +45 -21
  127. data/lib/sequel/model/dataset_module.rb +3 -0
  128. data/lib/sequel/model/exceptions.rb +15 -3
  129. data/lib/sequel/plugins/auto_validations.rb +53 -15
  130. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  131. data/lib/sequel/plugins/column_encryption.rb +27 -6
  132. data/lib/sequel/plugins/composition.rb +2 -2
  133. data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
  134. data/lib/sequel/plugins/constraint_validations.rb +8 -5
  135. data/lib/sequel/plugins/defaults_setter.rb +16 -0
  136. data/lib/sequel/plugins/dirty.rb +1 -1
  137. data/lib/sequel/plugins/finder.rb +4 -2
  138. data/lib/sequel/plugins/list.rb +8 -3
  139. data/lib/sequel/plugins/many_through_many.rb +1 -1
  140. data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
  141. data/lib/sequel/plugins/nested_attributes.rb +4 -4
  142. data/lib/sequel/plugins/optimistic_locking.rb +9 -42
  143. data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
  144. data/lib/sequel/plugins/paged_operations.rb +181 -0
  145. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +9 -3
  146. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
  147. data/lib/sequel/plugins/prepared_statements.rb +2 -1
  148. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
  149. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  150. data/lib/sequel/plugins/rcte_tree.rb +7 -4
  151. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  152. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  153. data/lib/sequel/plugins/sql_comments.rb +5 -5
  154. data/lib/sequel/plugins/static_cache.rb +38 -0
  155. data/lib/sequel/plugins/static_cache_cache.rb +5 -1
  156. data/lib/sequel/plugins/tactical_eager_loading.rb +21 -14
  157. data/lib/sequel/plugins/validate_associated.rb +22 -12
  158. data/lib/sequel/plugins/validation_helpers.rb +29 -2
  159. data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
  160. data/lib/sequel/version.rb +1 -1
  161. metadata +76 -6
@@ -88,11 +88,11 @@ module Sequel
88
88
  # Note that the migration this produces does not have a down
89
89
  # block, so you cannot reverse it.
90
90
  def dump_foreign_key_migration(options=OPTS)
91
- ts = tables(options)
91
+ ts = _dump_tables(options)
92
92
  <<END_MIG
93
93
  Sequel.migration do
94
94
  change do
95
- #{ts.sort.map{|t| dump_table_foreign_keys(t)}.reject{|x| x == ''}.join("\n\n").gsub(/^/, ' ')}
95
+ #{ts.map{|t| dump_table_foreign_keys(t)}.reject{|x| x == ''}.join("\n\n").gsub(/^/, ' ')}
96
96
  end
97
97
  end
98
98
  END_MIG
@@ -106,11 +106,11 @@ END_MIG
106
106
  # set to :namespace, prepend the table name to the index name if the
107
107
  # database does not use a global index namespace.
108
108
  def dump_indexes_migration(options=OPTS)
109
- ts = tables(options)
109
+ ts = _dump_tables(options)
110
110
  <<END_MIG
111
111
  Sequel.migration do
112
112
  change do
113
- #{ts.sort.map{|t| dump_table_indexes(t, :add_index, options)}.reject{|x| x == ''}.join("\n\n").gsub(/^/, ' ')}
113
+ #{ts.map{|t| dump_table_indexes(t, :add_index, options)}.reject{|x| x == ''}.join("\n\n").gsub(/^/, ' ')}
114
114
  end
115
115
  end
116
116
  END_MIG
@@ -138,7 +138,7 @@ END_MIG
138
138
  options[:foreign_keys] = false
139
139
  end
140
140
 
141
- ts = sort_dumped_tables(tables(options), options)
141
+ ts = sort_dumped_tables(_dump_tables(options), options)
142
142
  skipped_fks = if sfk = options[:skipped_foreign_keys]
143
143
  # Handle skipped foreign keys by adding them at the end via
144
144
  # alter_table/add_foreign_key. Note that skipped foreign keys
@@ -166,6 +166,21 @@ END_MIG
166
166
 
167
167
  private
168
168
 
169
+ # Handle schema option to dump tables in a different schema. Such
170
+ # tables must be schema qualified for this to work correctly.
171
+ def _dump_tables(opts)
172
+ if opts[:schema]
173
+ _literal_table_sort(tables(opts.merge(:qualify=>true)))
174
+ else
175
+ tables(opts).sort
176
+ end
177
+ end
178
+
179
+ # Sort the given table by the literalized value.
180
+ def _literal_table_sort(tables)
181
+ tables.sort_by{|s| literal(s)}
182
+ end
183
+
169
184
  # If a database default exists and can't be converted, and we are dumping with :same_db,
170
185
  # return a string with the inspect method modified a literal string is created if the code is evaled.
171
186
  def column_schema_to_ruby_default_fallback(default, options)
@@ -204,12 +219,20 @@ END_MIG
204
219
  if database_type == :mysql && h[:type] =~ /\Atimestamp/
205
220
  h[:null] = true
206
221
  end
222
+ if database_type == :mssql && schema[:max_length]
223
+ h[:size] = schema[:max_length]
224
+ end
207
225
  h
208
226
  else
209
227
  column_schema_to_ruby_type(schema)
210
228
  end
211
229
  type = col_opts.delete(:type)
212
- col_opts.delete(:size) if col_opts[:size].nil?
230
+ if col_opts.key?(:size) && col_opts[:size].nil?
231
+ col_opts.delete(:size)
232
+ if max_length = schema[:max_length]
233
+ col_opts[:size] = max_length
234
+ end
235
+ end
213
236
  if schema[:generated]
214
237
  if options[:same_db] && database_type == :postgres
215
238
  col_opts[:generated_always_as] = column_schema_to_ruby_default_fallback(schema[:default], options)
@@ -352,7 +375,7 @@ END_MIG
352
375
  options[:skipped_foreign_keys] = skipped_foreign_keys
353
376
  tables
354
377
  else
355
- tables.sort
378
+ tables
356
379
  end
357
380
  end
358
381
 
@@ -377,14 +400,14 @@ END_MIG
377
400
  # outstanding foreign keys and skipping those foreign keys.
378
401
  # The skipped foreign keys will be added at the end of the
379
402
  # migration.
380
- skip_table, skip_fks = table_fks.sort_by{|table, fks| [fks.length, table]}.first
403
+ skip_table, skip_fks = table_fks.sort_by{|table, fks| [fks.length, literal(table)]}.first
381
404
  skip_fks_hash = skipped_foreign_keys[skip_table] = {}
382
405
  skip_fks.each{|fk| skip_fks_hash[fk[:columns]] = fk}
383
406
  this_loop << skip_table
384
407
  end
385
408
 
386
409
  # Add sorted tables from this loop to the final list
387
- sorted_tables.concat(this_loop.sort)
410
+ sorted_tables.concat(_literal_table_sort(this_loop))
388
411
 
389
412
  # Remove tables that were handled this loop
390
413
  this_loop.each{|t| table_fks.delete(t)}
@@ -69,7 +69,8 @@ module Sequel
69
69
  # Also defines the with_server method on the receiver for easy use.
70
70
  def self.extended(db)
71
71
  pool = db.pool
72
- if defined?(ShardedThreadedConnectionPool) && pool.is_a?(ShardedThreadedConnectionPool)
72
+ case pool.pool_type
73
+ when :sharded_threaded, :sharded_timed_queue
73
74
  pool.extend(ThreadedServerBlock)
74
75
  pool.instance_variable_set(:@default_servers, {})
75
76
  else
@@ -0,0 +1,58 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # The set_literalizer extension allows for using Set instances in many of the
4
+ # same places that you would use Array instances:
5
+ #
6
+ # DB[:table].where(column: Set.new([1, 2, 3]))
7
+ # # SELECT FROM table WHERE (column IN (1, 2, 3))
8
+ #
9
+ # To load the extension into all datasets created from a given Database:
10
+ #
11
+ # DB.extension :set_literalizer
12
+ #
13
+ # Related module: Sequel::Dataset::SetLiteralizer
14
+
15
+ require 'set'
16
+
17
+ module Sequel
18
+ class Dataset
19
+ module SetLiteralizer
20
+ # Try to generate the same SQL for Set instances used in datasets
21
+ # that would be used for equivalent Array instances.
22
+ def complex_expression_sql_append(sql, op, args)
23
+ # Array instances are treated specially by
24
+ # Sequel::SQL::BooleanExpression.from_value_pairs. That cannot
25
+ # be modified by a dataset extension, so this tries to convert
26
+ # the complex expression values generated by default to what would
27
+ # be the complex expression values used for the equivalent array.
28
+ case op
29
+ when :'=', :'!='
30
+ if (set = args[1]).is_a?(Set)
31
+ op = op == :'=' ? :IN : :'NOT IN'
32
+ col = args[0]
33
+ array = set.to_a
34
+ if Sequel.condition_specifier?(array) && col.is_a?(Array)
35
+ array = Sequel.value_list(array)
36
+ end
37
+ args = [col, array]
38
+ end
39
+ end
40
+
41
+ super
42
+ end
43
+
44
+ private
45
+
46
+ # Literalize Set instances by converting the set to array.
47
+ def literal_other_append(sql, v)
48
+ if Set === v
49
+ literal_append(sql, v.to_a)
50
+ else
51
+ super
52
+ end
53
+ end
54
+ end
55
+
56
+ register_extension(:set_literalizer, SetLiteralizer)
57
+ end
58
+ end
@@ -2,27 +2,34 @@
2
2
  #
3
3
  # The sqlite_json_ops extension adds support to Sequel's DSL to make
4
4
  # it easier to call SQLite JSON functions and operators (added
5
- # first in SQLite 3.38.0).
5
+ # first in SQLite 3.38.0). It also supports the SQLite JSONB functions
6
+ # added in SQLite 3.45.0.
6
7
  #
7
8
  # To load the extension:
8
9
  #
9
10
  # Sequel.extension :sqlite_json_ops
10
11
  #
11
- # This extension works by calling methods on Sequel::SQLite::JSONOp objects,
12
- # which you can create via Sequel.sqlite_json_op:
12
+ # This extension works by calling methods on Sequel::SQLite::JSONOp and
13
+ # Sequel::SQLite::JSONBOp objects, which you can create using
14
+ # Sequel.sqlite_json_op and Sequel.sqlite_jsonb_op:
13
15
  #
14
16
  # j = Sequel.sqlite_json_op(:json_column)
17
+ # jb = Sequel.sqlite_jsonb_op(:jsonb_column)
15
18
  #
16
- # Also, on most Sequel expression objects, you can call the sqlite_json_op method
17
- # to create a Sequel::SQLite::JSONOp object:
19
+ # Also, on most Sequel expression objects, you can call the sqlite_json_op or
20
+ # sqlite_jsonb_op method to create a Sequel::SQLite::JSONOp or
21
+ # Sequel::SQLite::JSONBOp object:
18
22
  #
19
23
  # j = Sequel[:json_column].sqlite_json_op
24
+ # jb = Sequel[:jsonb_column].sqlite_jsonb_op
20
25
  #
21
26
  # If you have loaded the {core_extensions extension}[rdoc-ref:doc/core_extensions.rdoc],
22
27
  # or you have loaded the core_refinements extension
23
28
  # and have activated refinements for the file, you can also use Symbol#sqlite_json_op:
29
+ # or Symbol#sqlite_jsonb_op:
24
30
  #
25
31
  # j = :json_column.sqlite_json_op
32
+ # jb = :json_column.sqlite_jsonb_op
26
33
  #
27
34
  # The following methods are available for Sequel::SQLite::JSONOp instances:
28
35
  #
@@ -30,11 +37,13 @@
30
37
  # j.get(1) # (json_column ->> 1)
31
38
  # j.get_text(1) # (json_column -> 1)
32
39
  # j.extract('$.a') # json_extract(json_column, '$.a')
40
+ # jb.extract('$.a') # jsonb_extract(jsonb_column, '$.a')
33
41
  #
34
42
  # j.array_length # json_array_length(json_column)
35
43
  # j.type # json_type(json_column)
36
44
  # j.valid # json_valid(json_column)
37
- # j.json # json(json_column)
45
+ # jb.json # json(jsonb_column)
46
+ # j.jsonb # jsonb(json_column)
38
47
  #
39
48
  # j.insert('$.a', 1) # json_insert(json_column, '$.a', 1)
40
49
  # j.set('$.a', 1) # json_set(json_column, '$.a', 1)
@@ -42,22 +51,30 @@
42
51
  # j.remove('$.a') # json_remove(json_column, '$.a')
43
52
  # j.patch('{"a":2}') # json_patch(json_column, '{"a":2}')
44
53
  #
54
+ # jb.insert('$.a', 1) # jsonb_insert(jsonb_column, '$.a', 1)
55
+ # jb.set('$.a', 1) # jsonb_set(jsonb_column, '$.a', 1)
56
+ # jb.replace('$.a', 1) # jsonb_replace(jsonb_column, '$.a', 1)
57
+ # jb.remove('$.a') # jsonb_remove(jsonb_column, '$.a')
58
+ # jb.patch('{"a":2}') # jsonb_patch(jsonb_column, '{"a":2}')
59
+ #
45
60
  # j.each # json_each(json_column)
46
61
  # j.tree # json_tree(json_column)
47
62
  #
48
- # Related modules: Sequel::SQLite::JSONOp
63
+ # Related modules: Sequel::SQLite::JSONBaseOp, Sequel::SQLite::JSONOp,
64
+ # Sequel::SQLite::JSONBOp
49
65
 
50
66
  #
51
67
  module Sequel
52
68
  module SQLite
53
- # The JSONOp class is a simple container for a single object that
54
- # defines methods that yield Sequel expression objects representing
55
- # SQLite json operators and functions.
69
+ # JSONBaseOp is an abstract base wrapper class for a object that
70
+ # defines methods that return Sequel expression objects representing
71
+ # SQLite json operators and functions. It is subclassed by both
72
+ # JSONOp and JSONBOp for json and jsonb specific behavior.
56
73
  #
57
74
  # In the method documentation examples, assume that:
58
75
  #
59
76
  # json_op = Sequel.sqlite_json_op(:json)
60
- class JSONOp < Sequel::SQL::Wrapper
77
+ class JSONBaseOp < Sequel::SQL::Wrapper
61
78
  GET = ["(".freeze, " ->> ".freeze, ")".freeze].freeze
62
79
  private_constant :GET
63
80
 
@@ -82,7 +99,7 @@ module Sequel
82
99
  # json_op.array_length # json_array_length(json)
83
100
  # json_op.array_length('$[1]') # json_array_length(json, '$[1]')
84
101
  def array_length(*args)
85
- Sequel::SQL::NumericExpression.new(:NOOP, function(:array_length, *args))
102
+ Sequel::SQL::NumericExpression.new(:NOOP, SQL::Function.new(:json_array_length, self, *args))
86
103
  end
87
104
 
88
105
  # Returns an expression for a set of information extracted from the top-level
@@ -92,7 +109,7 @@ module Sequel
92
109
  # json_op.each # json_each(json)
93
110
  # json_op.each('$.a') # json_each(json, '$.a')
94
111
  def each(*args)
95
- function(:each, *args)
112
+ SQL::Function.new(:json_each, self, *args)
96
113
  end
97
114
 
98
115
  # Returns an expression for the JSON array element or object field at the specified
@@ -129,10 +146,17 @@ module Sequel
129
146
  #
130
147
  # json_op.json # json(json)
131
148
  def json
132
- self.class.new(SQL::Function.new(:json, self))
149
+ JSONOp.new(SQL::Function.new(:json, self))
133
150
  end
134
151
  alias minify json
135
152
 
153
+ # Returns the JSONB format of the JSON.
154
+ #
155
+ # json_op.jsonb # jsonb(json)
156
+ def jsonb
157
+ JSONBOp.new(SQL::Function.new(:jsonb, self))
158
+ end
159
+
136
160
  # Returns an expression for updating the JSON object using the RFC 7396 MergePatch algorithm
137
161
  #
138
162
  # json_op.patch('{"a": 1, "b": null}') # json_patch(json, '{"a": 1, "b": null}')
@@ -172,7 +196,7 @@ module Sequel
172
196
  # json_op.tree # json_tree(json)
173
197
  # json_op.tree('$.a') # json_tree(json, '$.a')
174
198
  def tree(*args)
175
- function(:tree, *args)
199
+ SQL::Function.new(:json_tree, self, *args)
176
200
  end
177
201
 
178
202
  # Returns an expression for the type of the JSON value or the JSON value at the given path.
@@ -180,13 +204,13 @@ module Sequel
180
204
  # json_op.type # json_type(json)
181
205
  # json_op.type('$[1]') # json_type(json, '$[1]')
182
206
  def type(*args)
183
- Sequel::SQL::StringExpression.new(:NOOP, function(:type, *args))
207
+ Sequel::SQL::StringExpression.new(:NOOP, SQL::Function.new(:json_type, self, *args))
184
208
  end
185
209
  alias typeof type
186
210
 
187
211
  # Returns a boolean expression for whether the JSON is valid or not.
188
212
  def valid
189
- Sequel::SQL::BooleanExpression.new(:NOOP, function(:valid))
213
+ Sequel::SQL::BooleanExpression.new(:NOOP, SQL::Function.new(:json_valid, self))
190
214
  end
191
215
 
192
216
  private
@@ -198,7 +222,7 @@ module Sequel
198
222
 
199
223
  # Internals of the methods that return functions prefixed with +json_+.
200
224
  def function(name, *args)
201
- SQL::Function.new("json_#{name}", self, *args)
225
+ SQL::Function.new("#{function_prefix}_#{name}", self, *args)
202
226
  end
203
227
 
204
228
  # Internals of the methods that return functions prefixed with +json_+, that
@@ -208,12 +232,36 @@ module Sequel
208
232
  end
209
233
  end
210
234
 
235
+ # JSONOp is used for SQLite json-specific functions and operators.
236
+ class JSONOp < JSONBaseOp
237
+ private
238
+
239
+ def function_prefix
240
+ "json"
241
+ end
242
+ end
243
+
244
+ # JSONOp is used for SQLite jsonb-specific functions and operators.
245
+ class JSONBOp < JSONBaseOp
246
+ private
247
+
248
+ def function_prefix
249
+ "jsonb"
250
+ end
251
+ end
252
+
211
253
  module JSONOpMethods
212
254
  # Wrap the receiver in an JSONOp so you can easily use the SQLite
213
255
  # json functions and operators with it.
214
256
  def sqlite_json_op
215
257
  JSONOp.new(self)
216
258
  end
259
+
260
+ # Wrap the receiver in an JSONBOp so you can easily use the SQLite
261
+ # jsonb functions and operators with it.
262
+ def sqlite_jsonb_op
263
+ JSONBOp.new(self)
264
+ end
217
265
  end
218
266
  end
219
267
 
@@ -227,6 +275,16 @@ module Sequel
227
275
  SQLite::JSONOp.new(v)
228
276
  end
229
277
  end
278
+
279
+ # Return the object wrapped in an SQLite::JSONBOp.
280
+ def sqlite_jsonb_op(v)
281
+ case v
282
+ when SQLite::JSONBOp
283
+ v
284
+ else
285
+ SQLite::JSONBOp.new(v)
286
+ end
287
+ end
230
288
  end
231
289
 
232
290
  class SQL::GenericExpression
@@ -35,6 +35,7 @@ if RUBY_VERSION >= '2.0'
35
35
  class Symbol
36
36
  prepend Sequel::SymbolAref
37
37
  end
38
+ # :nocov:
38
39
  else
39
40
  class Symbol
40
41
  if method_defined?(:[])
@@ -51,3 +52,4 @@ else
51
52
  end
52
53
  end
53
54
  end
55
+ # :nocov:
@@ -0,0 +1,78 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # The transaction_connection_validator extension automatically
4
+ # retries a transaction on a connection if an disconnect error
5
+ # is raised when sending the statement to begin a new
6
+ # transaction, as long as the user has not already checked out
7
+ # a connection. This is safe to do because no other queries
8
+ # have been issued on the connection, and no user-level code
9
+ # is run before retrying.
10
+ #
11
+ # This approach to connection validation can be significantly
12
+ # lower overhead than the connection_validator extension,
13
+ # though it does not handle all cases handled by the
14
+ # connection_validator extension. However, it performs the
15
+ # validation checks on every new transaction, so it will
16
+ # automatically handle disconnected connections in some cases
17
+ # where the connection_validator extension will not by default
18
+ # (as the connection_validator extension only checks
19
+ # connections if they have not been used in the last hour by
20
+ # default).
21
+ #
22
+ # Related module: Sequel::TransactionConnectionValidator
23
+
24
+ #
25
+ module Sequel
26
+ module TransactionConnectionValidator
27
+ class DisconnectRetry < DatabaseDisconnectError
28
+ # The connection that raised the disconnect error
29
+ attr_accessor :connection
30
+
31
+ # The underlying disconnect error, in case it needs to be reraised.
32
+ attr_accessor :database_error
33
+ end
34
+
35
+ # Rescue disconnect errors raised when beginning a new transaction. If there
36
+ # is a disconnnect error, it should be safe to retry the transaction using a
37
+ # new connection, as we haven't yielded control to the user yet.
38
+ def transaction(opts=OPTS)
39
+ super
40
+ rescue DisconnectRetry => e
41
+ if synchronize(opts[:server]){|conn| conn.equal?(e.connection)}
42
+ # If retrying would use the same connection, that means the
43
+ # connection was not removed from the pool, which means the caller has
44
+ # already checked out the connection, and retrying will not be successful.
45
+ # In this case, we can only reraise the exception.
46
+ raise e.database_error
47
+ end
48
+
49
+ num_retries ||= 0
50
+ num_retries += 1
51
+ retry if num_retries < 5
52
+
53
+ raise e.database_error
54
+ end
55
+
56
+ private
57
+
58
+ # Reraise disconnect errors as DisconnectRetry so they can be retried.
59
+ def begin_new_transaction(conn, opts)
60
+ super
61
+ rescue Sequel::DatabaseDisconnectError, *database_error_classes => e
62
+ if e.is_a?(Sequel::DatabaseDisconnectError) || disconnect_error?(e, OPTS)
63
+ exception = DisconnectRetry.new(e.message)
64
+ exception.set_backtrace([])
65
+ exception.connection = conn
66
+ unless e.is_a?(Sequel::DatabaseError)
67
+ e = Sequel.convert_exception_class(e, database_error_class(e, OPTS))
68
+ end
69
+ exception.database_error = e
70
+ raise exception
71
+ end
72
+
73
+ raise
74
+ end
75
+ end
76
+
77
+ Database.register_extension(:transaction_connection_validator, TransactionConnectionValidator)
78
+ end
@@ -276,7 +276,7 @@ module Sequel
276
276
 
277
277
  if eo[:no_results]
278
278
  no_results = true
279
- elsif eo[:eager_block] || eo[:loader] == false
279
+ elsif eo[:eager_block] || eo[:loader] == false || !use_placeholder_loader?
280
280
  ds = eager_loading_dataset(eo)
281
281
 
282
282
  strategy = ds.opts[:eager_limit_strategy] || strategy
@@ -299,13 +299,28 @@ module Sequel
299
299
  strategy = :ruby if strategy == :correlated_subquery
300
300
  strategy = nil if strategy == :ruby && assign_singular?
301
301
  objects = apply_eager_limit_strategy(ds, strategy, eager_limit).all
302
+
303
+ if strategy == :window_function
304
+ delete_rn = ds.row_number_column
305
+ objects.each{|obj| obj.values.delete(delete_rn)}
306
+ end
302
307
  elsif strategy == :union
303
308
  objects = []
304
309
  ds = associated_dataset
305
310
  loader = union_eager_loader
306
311
  joiner = " UNION ALL "
307
312
  ids.each_slice(subqueries_per_union).each do |slice|
308
- objects.concat(ds.with_sql(slice.map{|k| loader.sql(*k)}.join(joiner)).to_a)
313
+ sql = loader.send(:sql_origin)
314
+ join = false
315
+ slice.each do |k|
316
+ if join
317
+ sql << joiner
318
+ else
319
+ join = true
320
+ end
321
+ loader.append_sql(sql, *k)
322
+ end
323
+ objects.concat(ds.with_sql(sql).to_a)
309
324
  end
310
325
  ds = ds.eager(cascade) if cascade
311
326
  ds.send(:post_load, objects)
@@ -441,7 +456,7 @@ module Sequel
441
456
  def placeholder_loader
442
457
  if use_placeholder_loader?
443
458
  cached_fetch(:placeholder_loader) do
444
- Sequel::Dataset::PlaceholderLiteralizer.loader(associated_dataset) do |pl, ds|
459
+ associated_dataset.placeholder_literalizer_loader do |pl, ds|
445
460
  ds = ds.where(Sequel.&(*predicate_keys.map{|k| SQL::BooleanExpression.new(:'=', k, pl.arg)}))
446
461
  if self[:block]
447
462
  ds = self[:block].call(ds)
@@ -743,8 +758,8 @@ module Sequel
743
758
  # A placeholder literalizer used to speed up eager loading.
744
759
  def placeholder_eager_loader
745
760
  cached_fetch(:placeholder_eager_loader) do
746
- Sequel::Dataset::PlaceholderLiteralizer.loader(associated_dataset) do |pl, ds|
747
- apply_eager_limit_strategy(eager_loading_dataset.where(predicate_key=>pl.arg), eager_limit_strategy)
761
+ eager_loading_dataset.placeholder_literalizer_loader do |pl, ds|
762
+ apply_eager_limit_strategy(ds.where(predicate_key=>pl.arg), eager_limit_strategy)
748
763
  end
749
764
  end
750
765
  end
@@ -803,7 +818,7 @@ module Sequel
803
818
  # loading a limited association.
804
819
  def union_eager_loader
805
820
  cached_fetch(:union_eager_loader) do
806
- Sequel::Dataset::PlaceholderLiteralizer.loader(associated_dataset) do |pl, ds|
821
+ associated_dataset.placeholder_literalizer_loader do |pl, ds|
807
822
  ds = self[:eager_block].call(ds) if self[:eager_block]
808
823
  keys = predicate_keys
809
824
  ds = ds.where(keys.map{pl.arg}.zip(keys))
@@ -817,7 +832,7 @@ module Sequel
817
832
 
818
833
  # Whether the placeholder loader can be used to load the association.
819
834
  def use_placeholder_loader?
820
- self[:use_placeholder_loader]
835
+ self[:use_placeholder_loader] && _associated_dataset.supports_placeholder_literalizer?
821
836
  end
822
837
  end
823
838
 
@@ -1717,6 +1732,10 @@ module Sequel
1717
1732
  # :graph_select :: A column or array of columns to select from the associated table
1718
1733
  # when eagerly loading the association via +eager_graph+. Defaults to all
1719
1734
  # columns in the associated table.
1735
+ # :graph_use_association_block :: Makes eager_graph consider the association block. Without this, eager_graph
1736
+ # ignores the bock and only use the :graph_* options.
1737
+ # :instance_specific :: Marks the association as instance specific. Should be used if the association block
1738
+ # uses instance specific state, or transient state (accessing current date/time, etc.).
1720
1739
  # :limit :: Limit the number of records to the provided value. Use
1721
1740
  # an array with two elements for the value to specify a
1722
1741
  # limit (first element) and an offset (second element).
@@ -1856,6 +1875,16 @@ module Sequel
1856
1875
  # in certain places to disable optimizations.
1857
1876
  opts[:instance_specific] = _association_instance_specific_default(name)
1858
1877
  end
1878
+ if (orig_opts[:instance_specific] || orig_opts[:dataset]) && !opts.has_key?(:allow_eager) && !opts[:eager_loader]
1879
+ # For associations explicitly marked as instance specific, or that use the
1880
+ # :dataset option, where :allow_eager is not set, and no :eager_loader is
1881
+ # provided, disallow eager loading. In these cases, eager loading is
1882
+ # unlikely to work. This is not done for implicit setting of :instance_specific,
1883
+ # because implicit use is done by default for all associations with blocks,
1884
+ # and the vast majority of associations with blocks use the block for filtering
1885
+ # in a manner compatible with eager loading.
1886
+ opts[:allow_eager] = false
1887
+ end
1859
1888
  opts = assoc_class.new.merge!(opts)
1860
1889
 
1861
1890
  if opts[:clone] && !opts.cloneable?(cloned_assoc)
@@ -2444,6 +2473,9 @@ module Sequel
2444
2473
  # Return dataset to graph into given the association reflection, applying the :callback option if set.
2445
2474
  def eager_graph_dataset(opts, eager_options)
2446
2475
  ds = opts.associated_class.dataset
2476
+ if opts[:graph_use_association_block] && (b = opts[:block])
2477
+ ds = b.call(ds)
2478
+ end
2447
2479
  if cb = eager_options[:callback]
2448
2480
  ds = cb.call(ds)
2449
2481
  end
@@ -3355,8 +3387,15 @@ module Sequel
3355
3387
  local_opts = ds.opts[:eager_graph][:local]
3356
3388
  limit_strategy = r.eager_graph_limit_strategy(local_opts[:limit_strategy])
3357
3389
 
3358
- if r[:conditions] && !Sequel.condition_specifier?(r[:conditions]) && !r[:orig_opts].has_key?(:graph_conditions) && !r[:orig_opts].has_key?(:graph_only_conditions) && !r.has_key?(:graph_block)
3359
- raise Error, "Cannot eager_graph association when :conditions specified and not a hash or an array of pairs. Specify :graph_conditions, :graph_only_conditions, or :graph_block for the association. Model: #{r[:model]}, association: #{r[:name]}"
3390
+ # SEQUEL6: remove and integrate the auto_restrict_eager_graph plugin
3391
+ if !r[:orig_opts].has_key?(:graph_conditions) && !r[:orig_opts].has_key?(:graph_only_conditions) && !r.has_key?(:graph_block) && !r[:allow_eager_graph]
3392
+ if r[:conditions] && !Sequel.condition_specifier?(r[:conditions])
3393
+ raise Error, "Cannot eager_graph association when :conditions specified and not a hash or an array of pairs. Specify :graph_conditions, :graph_only_conditions, or :graph_block for the association. Model: #{r[:model]}, association: #{r[:name]}"
3394
+ end
3395
+
3396
+ if r[:block] && !r[:graph_use_association_block]
3397
+ warn "eager_graph used for association when association given a block without graph options. The block is ignored in this case. This will result in an exception starting in Sequel 6. Model: #{r[:model]}, association: #{r[:name]}"
3398
+ end
3360
3399
  end
3361
3400
 
3362
3401
  ds = loader.call(:self=>ds, :table_alias=>assoc_table_alias, :implicit_qualifier=>(ta == ds.opts[:eager_graph][:master]) ? first_source : qualifier_from_alias_symbol(ta, first_source), :callback=>callback, :join_type=>join_type || local_opts[:join_type], :join_only=>local_opts[:join_only], :limit_strategy=>limit_strategy, :from_self_alias=>ds.opts[:eager_graph][:master])
@@ -3535,11 +3574,11 @@ module Sequel
3535
3574
  end
3536
3575
 
3537
3576
  # Eagerly load all specified associations.
3538
- def eager_load(a, eager_assoc=@opts[:eager])
3577
+ def eager_load(a, eager_assoc=@opts[:eager], m=model)
3539
3578
  return if a.empty?
3540
3579
 
3541
3580
  # Reflections for all associations to eager load
3542
- reflections = eager_assoc.keys.map{|assoc| model.association_reflection(assoc) || (raise Sequel::UndefinedAssociation, "Model: #{self}, Association: #{assoc}")}
3581
+ reflections = eager_assoc.keys.map{|assoc| m.association_reflection(assoc) || (raise Sequel::UndefinedAssociation, "Model: #{self}, Association: #{assoc}")}
3543
3582
 
3544
3583
  perform_eager_loads(prepare_eager_load(a, reflections, eager_assoc))
3545
3584