sequel 5.45.0 → 5.77.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 (218) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +434 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +59 -27
  5. data/bin/sequel +11 -3
  6. data/doc/advanced_associations.rdoc +16 -14
  7. data/doc/association_basics.rdoc +119 -24
  8. data/doc/cheat_sheet.rdoc +11 -3
  9. data/doc/mass_assignment.rdoc +1 -1
  10. data/doc/migration.rdoc +27 -6
  11. data/doc/model_hooks.rdoc +1 -1
  12. data/doc/object_model.rdoc +8 -8
  13. data/doc/opening_databases.rdoc +28 -12
  14. data/doc/postgresql.rdoc +16 -8
  15. data/doc/querying.rdoc +5 -3
  16. data/doc/release_notes/5.46.0.txt +87 -0
  17. data/doc/release_notes/5.47.0.txt +59 -0
  18. data/doc/release_notes/5.48.0.txt +14 -0
  19. data/doc/release_notes/5.49.0.txt +59 -0
  20. data/doc/release_notes/5.50.0.txt +78 -0
  21. data/doc/release_notes/5.51.0.txt +47 -0
  22. data/doc/release_notes/5.52.0.txt +87 -0
  23. data/doc/release_notes/5.53.0.txt +23 -0
  24. data/doc/release_notes/5.54.0.txt +27 -0
  25. data/doc/release_notes/5.55.0.txt +21 -0
  26. data/doc/release_notes/5.56.0.txt +51 -0
  27. data/doc/release_notes/5.57.0.txt +23 -0
  28. data/doc/release_notes/5.58.0.txt +31 -0
  29. data/doc/release_notes/5.59.0.txt +73 -0
  30. data/doc/release_notes/5.60.0.txt +22 -0
  31. data/doc/release_notes/5.61.0.txt +43 -0
  32. data/doc/release_notes/5.62.0.txt +132 -0
  33. data/doc/release_notes/5.63.0.txt +33 -0
  34. data/doc/release_notes/5.64.0.txt +50 -0
  35. data/doc/release_notes/5.65.0.txt +21 -0
  36. data/doc/release_notes/5.66.0.txt +24 -0
  37. data/doc/release_notes/5.67.0.txt +32 -0
  38. data/doc/release_notes/5.68.0.txt +61 -0
  39. data/doc/release_notes/5.69.0.txt +26 -0
  40. data/doc/release_notes/5.70.0.txt +35 -0
  41. data/doc/release_notes/5.71.0.txt +21 -0
  42. data/doc/release_notes/5.72.0.txt +33 -0
  43. data/doc/release_notes/5.73.0.txt +66 -0
  44. data/doc/release_notes/5.74.0.txt +45 -0
  45. data/doc/release_notes/5.75.0.txt +35 -0
  46. data/doc/release_notes/5.76.0.txt +86 -0
  47. data/doc/release_notes/5.77.0.txt +63 -0
  48. data/doc/schema_modification.rdoc +1 -1
  49. data/doc/security.rdoc +9 -9
  50. data/doc/sharding.rdoc +3 -1
  51. data/doc/sql.rdoc +27 -15
  52. data/doc/testing.rdoc +23 -13
  53. data/doc/transactions.rdoc +6 -6
  54. data/doc/virtual_rows.rdoc +1 -1
  55. data/lib/sequel/adapters/ado/access.rb +1 -1
  56. data/lib/sequel/adapters/ado.rb +1 -1
  57. data/lib/sequel/adapters/amalgalite.rb +3 -5
  58. data/lib/sequel/adapters/ibmdb.rb +3 -3
  59. data/lib/sequel/adapters/jdbc/derby.rb +8 -0
  60. data/lib/sequel/adapters/jdbc/h2.rb +63 -10
  61. data/lib/sequel/adapters/jdbc/hsqldb.rb +8 -0
  62. data/lib/sequel/adapters/jdbc/postgresql.rb +7 -4
  63. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
  64. data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
  65. data/lib/sequel/adapters/jdbc.rb +24 -22
  66. data/lib/sequel/adapters/mysql.rb +92 -67
  67. data/lib/sequel/adapters/mysql2.rb +56 -51
  68. data/lib/sequel/adapters/odbc/mssql.rb +1 -1
  69. data/lib/sequel/adapters/odbc.rb +1 -1
  70. data/lib/sequel/adapters/oracle.rb +4 -3
  71. data/lib/sequel/adapters/postgres.rb +89 -45
  72. data/lib/sequel/adapters/shared/access.rb +11 -1
  73. data/lib/sequel/adapters/shared/db2.rb +42 -0
  74. data/lib/sequel/adapters/shared/mssql.rb +91 -10
  75. data/lib/sequel/adapters/shared/mysql.rb +78 -3
  76. data/lib/sequel/adapters/shared/oracle.rb +86 -7
  77. data/lib/sequel/adapters/shared/postgres.rb +576 -171
  78. data/lib/sequel/adapters/shared/sqlanywhere.rb +21 -5
  79. data/lib/sequel/adapters/shared/sqlite.rb +92 -8
  80. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  81. data/lib/sequel/adapters/sqlite.rb +99 -18
  82. data/lib/sequel/adapters/tinytds.rb +1 -1
  83. data/lib/sequel/adapters/trilogy.rb +117 -0
  84. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  85. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
  86. data/lib/sequel/ast_transformer.rb +6 -0
  87. data/lib/sequel/connection_pool/sharded_single.rb +5 -7
  88. data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
  89. data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
  90. data/lib/sequel/connection_pool/single.rb +6 -8
  91. data/lib/sequel/connection_pool/threaded.rb +14 -8
  92. data/lib/sequel/connection_pool/timed_queue.rb +270 -0
  93. data/lib/sequel/connection_pool.rb +57 -31
  94. data/lib/sequel/core.rb +17 -18
  95. data/lib/sequel/database/connecting.rb +27 -3
  96. data/lib/sequel/database/dataset.rb +16 -6
  97. data/lib/sequel/database/misc.rb +70 -14
  98. data/lib/sequel/database/query.rb +73 -2
  99. data/lib/sequel/database/schema_generator.rb +11 -6
  100. data/lib/sequel/database/schema_methods.rb +23 -4
  101. data/lib/sequel/database/transactions.rb +6 -0
  102. data/lib/sequel/dataset/actions.rb +111 -15
  103. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
  104. data/lib/sequel/dataset/features.rb +20 -1
  105. data/lib/sequel/dataset/misc.rb +12 -2
  106. data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
  107. data/lib/sequel/dataset/query.rb +170 -41
  108. data/lib/sequel/dataset/sql.rb +190 -71
  109. data/lib/sequel/dataset.rb +4 -0
  110. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  111. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  112. data/lib/sequel/extensions/any_not_empty.rb +2 -2
  113. data/lib/sequel/extensions/async_thread_pool.rb +14 -13
  114. data/lib/sequel/extensions/auto_cast_date_and_time.rb +94 -0
  115. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  116. data/lib/sequel/extensions/connection_expiration.rb +15 -9
  117. data/lib/sequel/extensions/connection_validator.rb +16 -11
  118. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  119. data/lib/sequel/extensions/core_refinements.rb +36 -11
  120. data/lib/sequel/extensions/date_arithmetic.rb +36 -8
  121. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  122. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  123. data/lib/sequel/extensions/duplicate_columns_handler.rb +11 -10
  124. data/lib/sequel/extensions/index_caching.rb +5 -1
  125. data/lib/sequel/extensions/inflector.rb +1 -1
  126. data/lib/sequel/extensions/is_distinct_from.rb +141 -0
  127. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  128. data/lib/sequel/extensions/migration.rb +57 -15
  129. data/lib/sequel/extensions/named_timezones.rb +22 -6
  130. data/lib/sequel/extensions/pagination.rb +1 -1
  131. data/lib/sequel/extensions/pg_array.rb +33 -4
  132. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  133. data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
  134. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
  135. data/lib/sequel/extensions/pg_enum.rb +1 -2
  136. data/lib/sequel/extensions/pg_extended_date_support.rb +39 -28
  137. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  138. data/lib/sequel/extensions/pg_hstore.rb +6 -1
  139. data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
  140. data/lib/sequel/extensions/pg_inet.rb +10 -11
  141. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  142. data/lib/sequel/extensions/pg_interval.rb +11 -11
  143. data/lib/sequel/extensions/pg_json.rb +13 -15
  144. data/lib/sequel/extensions/pg_json_ops.rb +125 -2
  145. data/lib/sequel/extensions/pg_multirange.rb +367 -0
  146. data/lib/sequel/extensions/pg_range.rb +13 -26
  147. data/lib/sequel/extensions/pg_range_ops.rb +37 -9
  148. data/lib/sequel/extensions/pg_row.rb +20 -19
  149. data/lib/sequel/extensions/pg_row_ops.rb +1 -1
  150. data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
  151. data/lib/sequel/extensions/round_timestamps.rb +1 -1
  152. data/lib/sequel/extensions/s.rb +2 -1
  153. data/lib/sequel/extensions/schema_caching.rb +1 -1
  154. data/lib/sequel/extensions/schema_dumper.rb +45 -11
  155. data/lib/sequel/extensions/server_block.rb +10 -13
  156. data/lib/sequel/extensions/set_literalizer.rb +58 -0
  157. data/lib/sequel/extensions/sql_comments.rb +110 -3
  158. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  159. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  160. data/lib/sequel/extensions/string_agg.rb +1 -1
  161. data/lib/sequel/extensions/string_date_time.rb +19 -23
  162. data/lib/sequel/extensions/symbol_aref.rb +2 -0
  163. data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
  164. data/lib/sequel/model/associations.rb +286 -92
  165. data/lib/sequel/model/base.rb +53 -33
  166. data/lib/sequel/model/dataset_module.rb +3 -0
  167. data/lib/sequel/model/errors.rb +10 -1
  168. data/lib/sequel/model/exceptions.rb +15 -3
  169. data/lib/sequel/model/inflections.rb +1 -1
  170. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  171. data/lib/sequel/plugins/auto_validations.rb +74 -16
  172. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  173. data/lib/sequel/plugins/column_encryption.rb +29 -8
  174. data/lib/sequel/plugins/composition.rb +3 -2
  175. data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
  176. data/lib/sequel/plugins/constraint_validations.rb +8 -5
  177. data/lib/sequel/plugins/defaults_setter.rb +16 -0
  178. data/lib/sequel/plugins/dirty.rb +1 -1
  179. data/lib/sequel/plugins/enum.rb +124 -0
  180. data/lib/sequel/plugins/finder.rb +4 -2
  181. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  182. data/lib/sequel/plugins/instance_specific_default.rb +1 -1
  183. data/lib/sequel/plugins/json_serializer.rb +2 -2
  184. data/lib/sequel/plugins/lazy_attributes.rb +3 -0
  185. data/lib/sequel/plugins/list.rb +8 -3
  186. data/lib/sequel/plugins/many_through_many.rb +109 -10
  187. data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
  188. data/lib/sequel/plugins/nested_attributes.rb +4 -4
  189. data/lib/sequel/plugins/optimistic_locking.rb +9 -42
  190. data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
  191. data/lib/sequel/plugins/paged_operations.rb +181 -0
  192. data/lib/sequel/plugins/pg_array_associations.rb +46 -34
  193. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +9 -3
  194. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
  195. data/lib/sequel/plugins/prepared_statements.rb +12 -2
  196. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
  197. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  198. data/lib/sequel/plugins/rcte_tree.rb +7 -4
  199. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  200. data/lib/sequel/plugins/serialization.rb +1 -0
  201. data/lib/sequel/plugins/serialization_modification_detection.rb +1 -0
  202. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  203. data/lib/sequel/plugins/sql_comments.rb +189 -0
  204. data/lib/sequel/plugins/static_cache.rb +39 -1
  205. data/lib/sequel/plugins/static_cache_cache.rb +5 -1
  206. data/lib/sequel/plugins/subclasses.rb +28 -11
  207. data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
  208. data/lib/sequel/plugins/timestamps.rb +1 -1
  209. data/lib/sequel/plugins/unused_associations.rb +521 -0
  210. data/lib/sequel/plugins/update_or_create.rb +1 -1
  211. data/lib/sequel/plugins/validate_associated.rb +22 -12
  212. data/lib/sequel/plugins/validation_helpers.rb +41 -11
  213. data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
  214. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  215. data/lib/sequel/sql.rb +1 -1
  216. data/lib/sequel/timezones.rb +12 -14
  217. data/lib/sequel/version.rb +1 -1
  218. metadata +109 -19
@@ -1,7 +1,7 @@
1
1
  # frozen-string-literal: true
2
2
  #
3
3
  # The pg_range_ops extension adds support to Sequel's DSL to make
4
- # it easier to call PostgreSQL range functions and operators.
4
+ # it easier to call PostgreSQL range and multirange functions and operators.
5
5
  #
6
6
  # To load the extension:
7
7
  #
@@ -11,10 +11,11 @@
11
11
  #
12
12
  # r = Sequel.pg_range_op(:range)
13
13
  #
14
- # If you have also loaded the pg_range extension, you can use
15
- # Sequel.pg_range as well:
14
+ # If you have also loaded the pg_range or pg_multirange extensions, you can use
15
+ # Sequel.pg_range or Sequel.pg_multirange as well:
16
16
  #
17
17
  # r = Sequel.pg_range(:range)
18
+ # r = Sequel.pg_multirange(:range)
18
19
  #
19
20
  # Also, on most Sequel expression objects, you can call the pg_range
20
21
  # method:
@@ -46,13 +47,25 @@
46
47
  # r.upper_inc # upper_inc(range)
47
48
  # r.lower_inf # lower_inf(range)
48
49
  # r.upper_inf # upper_inf(range)
50
+ #
51
+ # All of the above methods work for both ranges and multiranges, as long
52
+ # as PostgreSQL supports the operation. The following methods are also
53
+ # supported:
54
+ #
55
+ # r.range_merge # range_merge(range)
56
+ # r.unnest # unnest(range)
57
+ # r.multirange # multirange(range)
58
+ #
59
+ # +range_merge+ and +unnest+ expect the receiver to represent a multirange
60
+ # value, while +multi_range+ expects the receiver to represent a range value.
49
61
  #
50
- # See the PostgreSQL range function and operator documentation for more
62
+ # See the PostgreSQL range and multirange function and operator documentation for more
51
63
  # details on what these functions and operators do.
52
64
  #
53
- # If you are also using the pg_range extension, you should load it before
54
- # loading this extension. Doing so will allow you to use PGArray#op to get
55
- # an RangeOp, allowing you to perform range operations on range literals.
65
+ # If you are also using the pg_range or pg_multirange extension, you should
66
+ # load them before loading this extension. Doing so will allow you to use
67
+ # PGRange#op and PGMultiRange#op to get a RangeOp, allowing you to perform
68
+ # range operations on range literals.
56
69
  #
57
70
  # Related module: Sequel::Postgres::RangeOp
58
71
 
@@ -77,9 +90,12 @@ module Sequel
77
90
  :overlaps => ["(".freeze, " && ".freeze, ")".freeze].freeze,
78
91
  }.freeze
79
92
 
80
- %w'lower upper isempty lower_inc upper_inc lower_inf upper_inf'.each do |f|
93
+ %w'lower upper isempty lower_inc upper_inc lower_inf upper_inf unnest'.each do |f|
81
94
  class_eval("def #{f}; function(:#{f}) end", __FILE__, __LINE__)
82
95
  end
96
+ %w'range_merge multirange'.each do |f|
97
+ class_eval("def #{f}; RangeOp.new(function(:#{f})) end", __FILE__, __LINE__)
98
+ end
83
99
  OPERATORS.each_key do |f|
84
100
  class_eval("def #{f}(v); operator(:#{f}, v) end", __FILE__, __LINE__)
85
101
  end
@@ -127,6 +143,18 @@ module Sequel
127
143
  end
128
144
  end
129
145
  end
146
+
147
+ # :nocov:
148
+ if defined?(PGMultiRange)
149
+ # :nocov:
150
+ class PGMultiRange
151
+ # Wrap the PGRange instance in an RangeOp, allowing you to easily use
152
+ # the PostgreSQL range functions and operators with literal ranges.
153
+ def op
154
+ RangeOp.new(self)
155
+ end
156
+ end
157
+ end
130
158
  end
131
159
 
132
160
  module SQL::Builders
@@ -160,7 +188,7 @@ end
160
188
  if defined?(Sequel::CoreRefinements)
161
189
  module Sequel::CoreRefinements
162
190
  refine Symbol do
163
- include Sequel::Postgres::RangeOpMethods
191
+ send INCLUDE_METH, Sequel::Postgres::RangeOpMethods
164
192
  end
165
193
  end
166
194
  end
@@ -136,6 +136,15 @@ module Sequel
136
136
  ds.quote_schema_table_append(sql, db_type)
137
137
  end
138
138
  end
139
+
140
+ # Allow automatic parameterization if all values support it.
141
+ def sequel_auto_param_type(ds)
142
+ if db_type && all?{|v| nil == v || ds.send(:auto_param_type, v)}
143
+ s = String.new << "::"
144
+ ds.quote_schema_table_append(s, db_type)
145
+ s
146
+ end
147
+ end
139
148
  end
140
149
 
141
150
  # Class for row-valued/composite types that are treated as hashes.
@@ -208,6 +217,15 @@ module Sequel
208
217
  ds.quote_schema_table_append(sql, db_type)
209
218
  end
210
219
  end
220
+
221
+ # Allow automatic parameterization if all values support it.
222
+ def sequel_auto_param_type(ds)
223
+ if db_type && all?{|_,v| nil == v || ds.send(:auto_param_type, v)}
224
+ s = String.new << "::"
225
+ ds.quote_schema_table_append(s, db_type)
226
+ s
227
+ end
228
+ end
211
229
  end
212
230
 
213
231
  ROW_TYPE_CLASSES = [HashRow, ArrayRow].freeze
@@ -519,26 +537,9 @@ module Sequel
519
537
 
520
538
  private
521
539
 
522
- # Format composite types used in bound variable arrays.
523
- def bound_variable_array(arg)
524
- case arg
525
- when ArrayRow
526
- "\"(#{arg.map{|v| bound_variable_array(v) if v}.join(',').gsub(/("|\\)/, '\\\\\1')})\""
527
- when HashRow
528
- arg.check_columns!
529
- "\"(#{arg.values_at(*arg.columns).map{|v| bound_variable_array(v) if v}.join(',').gsub(/("|\\)/, '\\\\\1')})\""
530
- else
531
- super
532
- end
533
- end
534
-
535
540
  # Make the column type detection handle registered row types.
536
- def schema_column_type(db_type)
537
- if type = @row_schema_types[db_type]
538
- type
539
- else
540
- super
541
- end
541
+ def schema_composite_type(db_type)
542
+ @row_schema_types[db_type] || super
542
543
  end
543
544
  end
544
545
  end
@@ -210,7 +210,7 @@ end
210
210
  if defined?(Sequel::CoreRefinements)
211
211
  module Sequel::CoreRefinements
212
212
  refine Symbol do
213
- include Sequel::Postgres::PGRowOp::ExpressionMethods
213
+ send INCLUDE_METH, Sequel::Postgres::PGRowOp::ExpressionMethods
214
214
  end
215
215
  end
216
216
  end
@@ -1,20 +1,35 @@
1
1
  # frozen-string-literal: true
2
2
  #
3
3
  # The pg_timestamptz extension changes the default timestamp
4
- # type for the database to be +timestamptz+ (+timestamp with time zone+)
5
- # instead of +timestamp+ (+timestamp without time zone+). This is
4
+ # type for the database to be +timestamptz+ (<tt>timestamp with time zone</tt>)
5
+ # instead of +timestamp+ (<tt>timestamp without time zone</tt>). This is
6
6
  # recommended if you are dealing with multiple timezones in your application.
7
+ #
8
+ # If you are using the auto_cast_date_and_time extension, the pg_timestamptz
9
+ # extension will automatically cast Time and DateTime values to
10
+ # <tt>TIMESTAMP WITH TIME ZONE</tt> instead of +TIMESTAMP+.
7
11
  #
8
12
  # To load the extension into the database:
9
13
  #
10
14
  # DB.extension :pg_timestamptz
11
15
  #
12
- # Related module: Sequel::Postgres::Timestamptz
16
+ # To load the extension into individual datasets:
17
+ #
18
+ # ds = ds.extension(:pg_timestamptz)
19
+ #
20
+ # Note that the loading into individual datasets only affects the integration
21
+ # with the auto_cast_date_and_time extension.
22
+ #
23
+ # Related modules: Sequel::Postgres::Timestamptz, Sequel::Postgres::TimestamptzDatasetMethods
13
24
 
14
25
  #
15
26
  module Sequel
16
27
  module Postgres
17
28
  module Timestamptz
29
+ def self.extended(db)
30
+ db.extend_datasets(TimestamptzDatasetMethods)
31
+ end
32
+
18
33
  private
19
34
 
20
35
  # Use timestamptz by default for generic timestamp value.
@@ -22,7 +37,16 @@ module Sequel
22
37
  :timestamptz
23
38
  end
24
39
  end
40
+
41
+ module TimestamptzDatasetMethods
42
+ private
43
+
44
+ def literal_datetime_timestamp_cast
45
+ 'TIMESTAMP WITH TIME ZONE '
46
+ end
47
+ end
25
48
  end
26
49
 
50
+ Dataset.register_extension(:pg_timestamptz, Postgres::TimestamptzDatasetMethods)
27
51
  Database.register_extension(:pg_timestamptz, Postgres::Timestamptz)
28
52
  end
@@ -35,7 +35,7 @@ module Sequel
35
35
 
36
36
  # Round Sequel::SQLTime values before literalizing
37
37
  def literal_sqltime(v)
38
- super(v.round(timestamp_precision))
38
+ super(v.round(sqltime_precision))
39
39
  end
40
40
 
41
41
  # Round Time values before literalizing
@@ -51,9 +51,10 @@ module Sequel::S
51
51
 
52
52
  # :nocov:
53
53
  if RUBY_VERSION >= '2.0.0'
54
+ include_meth = RUBY_VERSION >= '3.1' ? :import_methods : :include
54
55
  # :nocov:
55
56
  refine Object do
56
- include Sequel::S
57
+ send include_meth, Sequel::S
57
58
  end
58
59
  end
59
60
  end
@@ -52,7 +52,7 @@ module Sequel
52
52
  # Dump the cached schema to the filename given in Marshal format.
53
53
  def dump_schema_cache(file)
54
54
  sch = {}
55
- @schemas.each do |k,v|
55
+ @schemas.sort.each do |k,v|
56
56
  sch[k] = v.map do |c, h|
57
57
  h = Hash[h]
58
58
  h.delete(:callable_default)
@@ -6,6 +6,17 @@
6
6
  # the current database). The main interface is through
7
7
  # Sequel::Database#dump_schema_migration.
8
8
  #
9
+ # The schema_dumper extension is quite limited in what types of
10
+ # database objects it supports. In general, it only supports
11
+ # dumping tables, columns, primary key and foreign key constraints,
12
+ # and some indexes. It does not support most table options, CHECK
13
+ # constraints, partial indexes, database functions, triggers,
14
+ # security grants/revokes, and a wide variety of other useful
15
+ # database properties. Be aware of the limitations when using the
16
+ # schema_dumper extension. If you are dumping the schema to restore
17
+ # to the same database type, it is recommended to use your database's
18
+ # dump and restore programs instead of the schema_dumper extension.
19
+ #
9
20
  # To load the extension:
10
21
  #
11
22
  # DB.extension :schema_dumper
@@ -77,11 +88,11 @@ module Sequel
77
88
  # Note that the migration this produces does not have a down
78
89
  # block, so you cannot reverse it.
79
90
  def dump_foreign_key_migration(options=OPTS)
80
- ts = tables(options)
91
+ ts = _dump_tables(options)
81
92
  <<END_MIG
82
93
  Sequel.migration do
83
94
  change do
84
- #{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(/^/, ' ')}
85
96
  end
86
97
  end
87
98
  END_MIG
@@ -95,11 +106,11 @@ END_MIG
95
106
  # set to :namespace, prepend the table name to the index name if the
96
107
  # database does not use a global index namespace.
97
108
  def dump_indexes_migration(options=OPTS)
98
- ts = tables(options)
109
+ ts = _dump_tables(options)
99
110
  <<END_MIG
100
111
  Sequel.migration do
101
112
  change do
102
- #{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(/^/, ' ')}
103
114
  end
104
115
  end
105
116
  END_MIG
@@ -127,7 +138,7 @@ END_MIG
127
138
  options[:foreign_keys] = false
128
139
  end
129
140
 
130
- ts = sort_dumped_tables(tables(options), options)
141
+ ts = sort_dumped_tables(_dump_tables(options), options)
131
142
  skipped_fks = if sfk = options[:skipped_foreign_keys]
132
143
  # Handle skipped foreign keys by adding them at the end via
133
144
  # alter_table/add_foreign_key. Note that skipped foreign keys
@@ -155,6 +166,21 @@ END_MIG
155
166
 
156
167
  private
157
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
+
158
184
  # If a database default exists and can't be converted, and we are dumping with :same_db,
159
185
  # return a string with the inspect method modified a literal string is created if the code is evaled.
160
186
  def column_schema_to_ruby_default_fallback(default, options)
@@ -172,7 +198,7 @@ END_MIG
172
198
  if options[:single_pk] && schema_autoincrementing_primary_key?(schema)
173
199
  type_hash = options[:same_db] ? {:type=>schema[:db_type]} : column_schema_to_ruby_type(schema)
174
200
  [:table, :key, :on_delete, :on_update, :deferrable].each{|f| type_hash[f] = schema[f] if schema[f]}
175
- if type_hash == {:type=>Integer} || type_hash == {:type=>"integer"}
201
+ if type_hash == {:type=>Integer} || type_hash == {:type=>"integer"} || type_hash == {:type=>"INTEGER"}
176
202
  type_hash.delete(:type)
177
203
  elsif options[:same_db] && type_hash == {:type=>type_literal_generic_bignum_symbol(type_hash).to_s}
178
204
  type_hash[:type] = :Bignum
@@ -193,12 +219,20 @@ END_MIG
193
219
  if database_type == :mysql && h[:type] =~ /\Atimestamp/
194
220
  h[:null] = true
195
221
  end
222
+ if database_type == :mssql && schema[:max_length]
223
+ h[:size] = schema[:max_length]
224
+ end
196
225
  h
197
226
  else
198
227
  column_schema_to_ruby_type(schema)
199
228
  end
200
229
  type = col_opts.delete(:type)
201
- 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
202
236
  if schema[:generated]
203
237
  if options[:same_db] && database_type == :postgres
204
238
  col_opts[:generated_always_as] = column_schema_to_ruby_default_fallback(schema[:default], options)
@@ -214,7 +248,7 @@ END_MIG
214
248
  col_opts[:null] = false if schema[:allow_null] == false
215
249
  if table = schema[:table]
216
250
  [:key, :on_delete, :on_update, :deferrable].each{|f| col_opts[f] = schema[f] if schema[f]}
217
- col_opts[:type] = type unless type == Integer || type == 'integer'
251
+ col_opts[:type] = type unless type == Integer || type == 'integer' || type == 'INTEGER'
218
252
  gen.foreign_key(name, table, col_opts)
219
253
  else
220
254
  gen.column(name, type, col_opts)
@@ -341,7 +375,7 @@ END_MIG
341
375
  options[:skipped_foreign_keys] = skipped_foreign_keys
342
376
  tables
343
377
  else
344
- tables.sort
378
+ tables
345
379
  end
346
380
  end
347
381
 
@@ -366,14 +400,14 @@ END_MIG
366
400
  # outstanding foreign keys and skipping those foreign keys.
367
401
  # The skipped foreign keys will be added at the end of the
368
402
  # migration.
369
- 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
370
404
  skip_fks_hash = skipped_foreign_keys[skip_table] = {}
371
405
  skip_fks.each{|fk| skip_fks_hash[fk[:columns]] = fk}
372
406
  this_loop << skip_table
373
407
  end
374
408
 
375
409
  # Add sorted tables from this loop to the final list
376
- sorted_tables.concat(this_loop.sort)
410
+ sorted_tables.concat(_literal_table_sort(this_loop))
377
411
 
378
412
  # Remove tables that were handled this loop
379
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
@@ -88,12 +89,10 @@ module Sequel
88
89
  module UnthreadedServerBlock
89
90
  # Set a default server/shard to use inside the block.
90
91
  def with_server(default_server, read_only_server=default_server)
91
- begin
92
- set_default_server(default_server, read_only_server)
93
- yield
94
- ensure
95
- clear_default_server
96
- end
92
+ set_default_server(default_server, read_only_server)
93
+ yield
94
+ ensure
95
+ clear_default_server
97
96
  end
98
97
 
99
98
  private
@@ -131,12 +130,10 @@ module Sequel
131
130
  # Set a default server/shard to use inside the block for the current
132
131
  # thread.
133
132
  def with_server(default_server, read_only_server=default_server)
134
- begin
135
- set_default_server(default_server, read_only_server)
136
- yield
137
- ensure
138
- clear_default_server
139
- end
133
+ set_default_server(default_server, read_only_server)
134
+ yield
135
+ ensure
136
+ clear_default_server
140
137
  end
141
138
 
142
139
  private
@@ -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
@@ -44,11 +44,51 @@
44
44
  #
45
45
  # DB.extension(:sql_comments)
46
46
  #
47
+ # Loading the sql_comments extension into the database also adds
48
+ # support for block-level comment support via Database#with_comments.
49
+ # You call #with_comments with a hash. Queries inside the hash will
50
+ # include a comment based on the hash (assuming they are inside the
51
+ # same thread):
52
+ #
53
+ # DB.with_comments(model: Album, action: :all) do
54
+ # DB[:albums].all
55
+ # # SELECT * FROM albums -- model:Album,action:all
56
+ # end
57
+ #
58
+ # You can nest calls to #with_comments, which will combine the
59
+ # entries from both calls:
60
+ #
61
+ # DB.with_comments(application: App, path: :scrubbed_path) do
62
+ # DB.with_comments(model: Album, action: :all) do
63
+ # ds = DB[:albums].all
64
+ # # SELECT * FROM albums
65
+ # # -- application:App,path:scrubbed_path,model:Album,action:all
66
+ # end
67
+ # end
68
+ #
69
+ # You can override comment entries specified in earlier blocks, or
70
+ # remove entries specified earlier using a nil value:
71
+ #
72
+ # DB.with_comments(application: App, path: :scrubbed_path) do
73
+ # DB.with_comments(application: Foo, path: nil) do
74
+ # ds = DB[:albums].all
75
+ # # SELECT * FROM albums # -- application:Foo
76
+ # end
77
+ # end
78
+ #
79
+ # You can combine block-level comments with dataset-specific
80
+ # comments:
81
+ #
82
+ # DB.with_comments(model: Album, action: :all) do
83
+ # DB[:table].comment("Some Comment").all
84
+ # # SELECT * FROM albums -- model:Album,action:all -- Some Comment
85
+ # end
86
+ #
47
87
  # Note that Microsoft Access does not support inline comments,
48
88
  # and attempting to use comments on it will result in SQL syntax
49
89
  # errors.
50
90
  #
51
- # Related module: Sequel::SQLComments
91
+ # Related modules: Sequel::SQLComments, Sequel::Database::SQLComments
52
92
 
53
93
  #
54
94
  module Sequel
@@ -62,7 +102,7 @@ module Sequel
62
102
  %w'select insert update delete'.each do |type|
63
103
  define_method(:"#{type}_sql") do |*a|
64
104
  sql = super(*a)
65
- if comment = @opts[:comment]
105
+ if comment = _sql_comment
66
106
  # This assumes that the comment stored in the dataset has
67
107
  # already been formatted. If not, this could result in SQL
68
108
  # injection.
@@ -74,8 +114,10 @@ module Sequel
74
114
  if sql.frozen?
75
115
  sql += comment
76
116
  sql.freeze
77
- else
117
+ elsif @opts[:append_sql] || @opts[:placeholder_literalizer]
78
118
  sql << comment
119
+ else
120
+ sql += comment
79
121
  end
80
122
  end
81
123
  sql
@@ -84,6 +126,11 @@ module Sequel
84
126
 
85
127
  private
86
128
 
129
+ # The comment to include in the SQL query, if any.
130
+ def _sql_comment
131
+ @opts[:comment]
132
+ end
133
+
87
134
  # Format the comment. For maximum compatibility, this uses a
88
135
  # single line SQL comment, and converts all consecutive whitespace
89
136
  # in the comment to a single space.
@@ -92,5 +139,65 @@ module Sequel
92
139
  end
93
140
  end
94
141
 
142
+ module Database::SQLComments
143
+ def self.extended(db)
144
+ db.instance_variable_set(:@comment_hashes, {})
145
+ db.extend_datasets DatasetSQLComments
146
+ end
147
+
148
+ # A map of threads to comment hashes, used for correctly setting
149
+ # comments for all queries inside #with_comments blocks.
150
+ attr_reader :comment_hashes
151
+
152
+ # Store the comment hash and use it to create comments inside the block
153
+ def with_comments(comment_hash)
154
+ hashes = @comment_hashes
155
+ t = Sequel.current
156
+ new_hash = if hash = Sequel.synchronize{hashes[t]}
157
+ hash.merge(comment_hash)
158
+ else
159
+ comment_hash.dup
160
+ end
161
+ yield Sequel.synchronize{hashes[t] = new_hash}
162
+ ensure
163
+ if hash
164
+ Sequel.synchronize{hashes[t] = hash}
165
+ else
166
+ t && Sequel.synchronize{hashes.delete(t)}
167
+ end
168
+ end
169
+
170
+ module DatasetSQLComments
171
+ include Sequel::SQLComments
172
+
173
+ private
174
+
175
+ # Include comments added via Database#with_comments in the output SQL.
176
+ def _sql_comment
177
+ specific_comment = super
178
+ return specific_comment if @opts[:append_sql]
179
+
180
+ t = Sequel.current
181
+ hashes = db.comment_hashes
182
+ block_comment = if comment_hash = Sequel.synchronize{hashes[t]}
183
+ comment_array = comment_hash.map{|k,v| "#{k}:#{v}" unless v.nil?}
184
+ comment_array.compact!
185
+ comment_array.join(",")
186
+ end
187
+
188
+ if block_comment
189
+ if specific_comment
190
+ format_sql_comment(block_comment + specific_comment)
191
+ else
192
+ format_sql_comment(block_comment)
193
+ end
194
+ else
195
+ specific_comment
196
+ end
197
+ end
198
+ end
199
+ end
200
+
95
201
  Dataset.register_extension(:sql_comments, SQLComments)
202
+ Database.register_extension(:sql_comments, Database::SQLComments)
96
203
  end