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,5 +1,7 @@
1
1
  # frozen-string-literal: true
2
2
 
3
+ require_relative '../utils/columns_limit_1'
4
+
3
5
  module Sequel
4
6
  module SqlAnywhere
5
7
  Sequel::Database.set_shared_adapter_scheme(:sqlanywhere, self)
@@ -35,7 +37,7 @@ module Sequel
35
37
  row[:auto_increment] = auto_increment == 1 || auto_increment == true
36
38
  row[:primary_key] = row.delete(:pkey) == 'Y'
37
39
  row[:allow_null] = row[:nulls_allowed].is_a?(Integer) ? row.delete(:nulls_allowed) == 1 : row.delete(:nulls_allowed)
38
- row[:db_type] = row.delete(:domain_name)
40
+ row[:db_type] = row.delete(:domain_name_with_size)
39
41
  row[:type] = if row[:db_type] =~ /numeric/i and (row[:scale].is_a?(Integer) ? row[:scale] == 0 : !row[:scale])
40
42
  :integer
41
43
  else
@@ -193,6 +195,11 @@ module Sequel
193
195
  end
194
196
  end
195
197
 
198
+ # SQLAnywhere tinyint types are unsigned.
199
+ def column_schema_tinyint_type_is_unsigned?
200
+ true
201
+ end
202
+
196
203
  # SqlAnywhere doesn't support CREATE TABLE AS, it only supports SELECT INTO.
197
204
  # Emulating CREATE TABLE AS using SELECT INTO is only possible if a dataset
198
205
  # is given as the argument, it can't work with a string, so raise an
@@ -211,6 +218,8 @@ module Sequel
211
218
  def schema_column_type(db_type)
212
219
  if convert_smallint_to_bool && db_type =~ /smallint/i
213
220
  :boolean
221
+ elsif db_type =~ /unsigned (big)?int/i
222
+ :integer
214
223
  else
215
224
  super
216
225
  end
@@ -234,6 +243,7 @@ module Sequel
234
243
  module DatasetMethods
235
244
  Dataset.def_sql_method(self, :insert, %w'insert into columns values')
236
245
  Dataset.def_sql_method(self, :select, %w'with select distinct limit columns into from join where group having window compounds order lock')
246
+ include ::Sequel::Dataset::ColumnsLimit1
237
247
 
238
248
  # Whether to convert smallint to boolean arguments for this dataset.
239
249
  # Defaults to the IBMDB module setting.
@@ -271,10 +281,6 @@ module Sequel
271
281
  false
272
282
  end
273
283
 
274
- def supports_timestamp_usecs?
275
- false
276
- end
277
-
278
284
  def supports_window_clause?
279
285
  true
280
286
  end
@@ -368,6 +374,16 @@ module Sequel
368
374
 
369
375
  private
370
376
 
377
+ # SQLAnywhere only supports 3 digits after the decimal point for times.
378
+ def default_time_format
379
+ "'%H:%M:%S.%3N'"
380
+ end
381
+
382
+ # SQLAnywhere only supports 3 digits after the decimal point for timestamps.
383
+ def default_timestamp_format
384
+ "'%Y-%m-%d %H:%M:%S.%3N'"
385
+ end
386
+
371
387
  # Use 1 for true on Sybase
372
388
  def literal_true
373
389
  '1'
@@ -145,6 +145,11 @@ module Sequel
145
145
  sqlite_version >= 30608
146
146
  end
147
147
 
148
+ # SQLite 3.8.2+ supports the without rowid table constraint
149
+ def support_without_rowid?
150
+ sqlite_version >= 30802
151
+ end
152
+
148
153
  # Override the default setting for whether to use timezones in timestamps.
149
154
  # It is set to +false+ by default, as SQLite's date/time methods do not
150
155
  # support timezones in timestamps.
@@ -169,6 +174,7 @@ module Sequel
169
174
  # DB.values([[1, 2], [3, 4]])
170
175
  # # VALUES ((1, 2), (3, 4))
171
176
  def values(v)
177
+ raise Error, "Cannot provide an empty array for values" if v.empty?
172
178
  @default_dataset.clone(:values=>v)
173
179
  end
174
180
 
@@ -320,6 +326,12 @@ module Sequel
320
326
  end
321
327
  end
322
328
 
329
+ # SQLite does not restrict the integer or decimal type to a specific range.
330
+ def column_schema_integer_min_max_values(column)
331
+ nil
332
+ end
333
+ alias column_schema_decimal_min_max_values column_schema_integer_min_max_values
334
+
323
335
  # Array of PRAGMA SQL statements based on the Database options that should be applied to
324
336
  # new connections.
325
337
  def connection_pragmas
@@ -337,6 +349,19 @@ module Sequel
337
349
  ps
338
350
  end
339
351
 
352
+ # Support creating STRICT AND/OR WITHOUT ROWID tables via :strict and :without_rowid options
353
+ def create_table_sql(name, generator, options)
354
+ if options[:strict] && options[:without_rowid]
355
+ "#{super} STRICT, WITHOUT ROWID"
356
+ elsif options[:strict]
357
+ "#{super} STRICT"
358
+ elsif options[:without_rowid]
359
+ "#{super} WITHOUT ROWID"
360
+ else
361
+ super
362
+ end
363
+ end
364
+
340
365
  # SQLite support creating temporary views.
341
366
  def create_view_prefix_sql(name, options)
342
367
  create_view_sql_append_columns("CREATE #{'TEMPORARY 'if options[:temp]}VIEW #{quote_schema_table(name)}", options[:columns])
@@ -345,8 +370,10 @@ module Sequel
345
370
  DATABASE_ERROR_REGEXPS = {
346
371
  /(is|are) not unique\z|PRIMARY KEY must be unique\z|UNIQUE constraint failed: .+\z/ => UniqueConstraintViolation,
347
372
  /foreign key constraint failed\z/i => ForeignKeyConstraintViolation,
373
+ /\ASQLITE ERROR 3091/ => CheckConstraintViolation,
348
374
  /\A(SQLITE ERROR 275 \(CONSTRAINT_CHECK\) : )?CHECK constraint failed/ => CheckConstraintViolation,
349
375
  /\A(SQLITE ERROR 19 \(CONSTRAINT\) : )?constraint failed\z/ => ConstraintViolation,
376
+ /\Acannot store [A-Z]+ value in [A-Z]+ column / => ConstraintViolation,
350
377
  /may not be NULL\z|NOT NULL constraint failed: .+\z/ => NotNullConstraintViolation,
351
378
  /\ASQLITE ERROR \d+ \(\) : CHECK constraint failed: / => CheckConstraintViolation
352
379
  }.freeze
@@ -393,7 +420,7 @@ module Sequel
393
420
  old_columns = def_columns.map{|c| c[:name]}
394
421
  opts[:old_columns_proc].call(old_columns) if opts[:old_columns_proc]
395
422
 
396
- yield def_columns if block_given?
423
+ yield def_columns if defined?(yield)
397
424
 
398
425
  constraints = (opts[:constraints] || []).dup
399
426
  pks = []
@@ -490,7 +517,6 @@ module Sequel
490
517
  # table_xinfo PRAGMA used, remove hidden columns
491
518
  # that are not generated columns
492
519
  if row[:generated] = (row.delete(:hidden) != 0)
493
- next unless row[:type].end_with?(' GENERATED ALWAYS')
494
520
  row[:type] = row[:type].sub(' GENERATED ALWAYS', '')
495
521
  end
496
522
  end
@@ -562,10 +588,10 @@ module Sequel
562
588
  EXTRACT_MAP = {:year=>"'%Y'", :month=>"'%m'", :day=>"'%d'", :hour=>"'%H'", :minute=>"'%M'", :second=>"'%f'"}.freeze
563
589
  EXTRACT_MAP.each_value(&:freeze)
564
590
 
565
- Dataset.def_sql_method(self, :delete, [['if db.sqlite_version >= 30803', %w'with delete from where'], ["else", %w'delete from where']])
566
- Dataset.def_sql_method(self, :insert, [['if db.sqlite_version >= 30803', %w'with insert conflict into columns values on_conflict'], ["else", %w'insert conflict into columns values']])
591
+ Dataset.def_sql_method(self, :delete, [['if db.sqlite_version >= 33500', %w'with delete from where returning'], ['elsif db.sqlite_version >= 30803', %w'with delete from where'], ["else", %w'delete from where']])
592
+ Dataset.def_sql_method(self, :insert, [['if db.sqlite_version >= 33500', %w'with insert conflict into columns values on_conflict returning'], ['elsif db.sqlite_version >= 30803', %w'with insert conflict into columns values on_conflict'], ["else", %w'insert conflict into columns values']])
567
593
  Dataset.def_sql_method(self, :select, [['if opts[:values]', %w'with values compounds'], ['else', %w'with select distinct columns from join where group having window compounds order limit lock']])
568
- Dataset.def_sql_method(self, :update, [['if db.sqlite_version >= 33300', %w'with update table set from where'], ['elsif db.sqlite_version >= 30803', %w'with update table set where'], ["else", %w'update table set where']])
594
+ Dataset.def_sql_method(self, :update, [['if db.sqlite_version >= 33500', %w'with update table set from where returning'], ['elsif db.sqlite_version >= 33300', %w'with update table set from where'], ['elsif db.sqlite_version >= 30803', %w'with update table set where'], ["else", %w'update table set where']])
569
595
 
570
596
  def cast_sql_append(sql, expr, type)
571
597
  if type == Time or type == DateTime
@@ -639,10 +665,16 @@ module Sequel
639
665
  # SQLite performs a TRUNCATE style DELETE if no filter is specified.
640
666
  # Since we want to always return the count of records, add a condition
641
667
  # that is always true and then delete.
642
- def delete
643
- @opts[:where] ? super : where(1=>1).delete
668
+ def delete(&block)
669
+ @opts[:where] ? super : where(1=>1).delete(&block)
644
670
  end
645
671
 
672
+ # Always return false when using VALUES
673
+ def empty?
674
+ return false if @opts[:values]
675
+ super
676
+ end
677
+
646
678
  # Return an array of strings specifying a query explanation for a SELECT of the
647
679
  # current dataset. Currently, the options are ignored, but it accepts options
648
680
  # to be compatible with other adapters.
@@ -657,10 +689,25 @@ module Sequel
657
689
 
658
690
  # HAVING requires GROUP BY on SQLite
659
691
  def having(*cond)
660
- raise(InvalidOperation, "Can only specify a HAVING clause on a grouped dataset") unless @opts[:group]
692
+ raise(InvalidOperation, "Can only specify a HAVING clause on a grouped dataset") if !@opts[:group] && db.sqlite_version < 33900
661
693
  super
662
694
  end
663
695
 
696
+ # Support insert select for associations, so that the model code can use
697
+ # returning instead of a separate query.
698
+ def insert_select(*values)
699
+ return unless supports_insert_select?
700
+ # Handle case where query does not return a row
701
+ server?(:default).with_sql_first(insert_select_sql(*values)) || false
702
+ end
703
+
704
+ # The SQL to use for an insert_select, adds a RETURNING clause to the insert
705
+ # unless the RETURNING clause is already present.
706
+ def insert_select_sql(*values)
707
+ ds = opts[:returning] ? self : returning
708
+ ds.insert_sql(*values)
709
+ end
710
+
664
711
  # SQLite uses the nonstandard ` (backtick) for quoting identifiers.
665
712
  def quoted_identifier_append(sql, c)
666
713
  sql << '`' << c.to_s.gsub('`', '``') << '`'
@@ -742,6 +789,13 @@ module Sequel
742
789
  insert_conflict(:ignore)
743
790
  end
744
791
 
792
+ # Automatically add aliases to RETURNING values to work around SQLite bug.
793
+ def returning(*values)
794
+ return super if values.empty?
795
+ raise Error, "RETURNING is not supported on #{db.database_type}" unless supports_returning?(:insert)
796
+ clone(:returning=>_returning_values(values).freeze)
797
+ end
798
+
745
799
  # SQLite 3.8.3+ supports common table expressions.
746
800
  def supports_cte?(type=:select)
747
801
  db.sqlite_version >= 30803
@@ -782,6 +836,11 @@ module Sequel
782
836
  false
783
837
  end
784
838
 
839
+ # SQLite 3.35.0 supports RETURNING on INSERT/UPDATE/DELETE.
840
+ def supports_returning?(_)
841
+ db.sqlite_version >= 33500
842
+ end
843
+
785
844
  # SQLite supports timezones in literal timestamps, since it stores them
786
845
  # as text. But using timezones in timestamps breaks SQLite datetime
787
846
  # functions, so we allow the user to override the default per database.
@@ -814,6 +873,26 @@ module Sequel
814
873
 
815
874
  private
816
875
 
876
+ # Add aliases to symbols and identifiers to work around SQLite bug.
877
+ def _returning_values(values)
878
+ values.map do |v|
879
+ case v
880
+ when Symbol
881
+ _, c, a = split_symbol(v)
882
+ a ? v : Sequel.as(v, c)
883
+ when SQL::Identifier, SQL::QualifiedIdentifier
884
+ Sequel.as(v, unqualified_column_for(v))
885
+ else
886
+ v
887
+ end
888
+ end
889
+ end
890
+
891
+ # Use from_self for aggregate dataset using VALUES.
892
+ def aggreate_dataset_use_from_self?
893
+ super || @opts[:values]
894
+ end
895
+
817
896
  # SQLite uses string literals instead of identifiers in AS clauses.
818
897
  def as_sql_append(sql, aliaz, column_aliases=nil)
819
898
  raise Error, "sqlite does not support derived column lists" if column_aliases
@@ -851,6 +930,11 @@ module Sequel
851
930
  500
852
931
  end
853
932
 
933
+ # The strftime format to use when literalizing the time.
934
+ def default_timestamp_format
935
+ db.use_timestamp_timezones? ? "'%Y-%m-%d %H:%M:%S.%6N%z'" : super
936
+ end
937
+
854
938
  # SQL fragment specifying a list of identifiers
855
939
  def identifier_list(columns)
856
940
  columns.map{|i| quote_identifier(i)}.join(', ')
@@ -120,7 +120,7 @@ module Sequel
120
120
 
121
121
  case type
122
122
  when :select
123
- yield rs if block_given?
123
+ yield rs if defined?(yield)
124
124
  when :rows
125
125
  return @api.sqlany_affected_rows(rs)
126
126
  when :insert
@@ -98,6 +98,11 @@ module Sequel
98
98
  # The conversion procs to use for this database
99
99
  attr_reader :conversion_procs
100
100
 
101
+ def initialize(opts = OPTS)
102
+ super
103
+ @allow_regexp = typecast_value_boolean(opts[:setup_regexp_function])
104
+ end
105
+
101
106
  # Connect to the database. Since SQLite is a file based database,
102
107
  # available options are limited:
103
108
  #
@@ -106,6 +111,18 @@ module Sequel
106
111
  # static data that you do not want to modify
107
112
  # :timeout :: how long to wait for the database to be available if it
108
113
  # is locked, given in milliseconds (default is 5000)
114
+ # :setup_regexp_function :: enable use of Regexp objects with SQL
115
+ # 'REGEXP' operator. If the value is :cached or "cached",
116
+ # caches the generated regexps, which can result in a memory
117
+ # leak if dynamic regexps are used. If the value is a Proc,
118
+ # it will be called with a string for the regexp and a string
119
+ # for the value to compare, and should return whether the regexp
120
+ # matches.
121
+ # :regexp_function_cache :: If setting +setup_regexp_function+ to +cached+, this
122
+ # determines the cache to use. It should either be a proc or a class, and it
123
+ # defaults to +Hash+. You can use +ObjectSpace::WeakKeyMap+ on Ruby 3.3+ to
124
+ # have the VM automatically remove regexps from the cache after they
125
+ # are no longer used.
109
126
  def connect(server)
110
127
  opts = server_opts(server)
111
128
  opts[:database] = ':memory:' if blank_object?(opts[:database])
@@ -119,6 +136,10 @@ module Sequel
119
136
  end
120
137
 
121
138
  connection_pragmas.each{|s| log_connection_yield(s, db){db.execute_batch(s)}}
139
+
140
+ if typecast_value_boolean(opts[:setup_regexp_function])
141
+ setup_regexp_function(db, opts[:setup_regexp_function])
142
+ end
122
143
 
123
144
  class << db
124
145
  attr_reader :prepared_statements
@@ -128,6 +149,12 @@ module Sequel
128
149
  db
129
150
  end
130
151
 
152
+ # Whether this Database instance is setup to allow regexp matching.
153
+ # True if the :setup_regexp_function option was passed when creating the Database.
154
+ def allow_regexp?
155
+ @allow_regexp
156
+ end
157
+
131
158
  # Disconnect given connections from the database.
132
159
  def disconnect_connection(c)
133
160
  c.prepared_statements.each_value{|v| v.first.close}
@@ -185,30 +212,57 @@ module Sequel
185
212
  @conversion_procs['datetime'] = @conversion_procs['timestamp'] = method(:to_application_timestamp)
186
213
  set_integer_booleans
187
214
  end
215
+
216
+ def setup_regexp_function(db, how)
217
+ case how
218
+ when Proc
219
+ # nothing
220
+ when :cached, "cached"
221
+ cache = @opts[:regexp_function_cache] || Hash
222
+ cache = cache.is_a?(Proc) ? cache.call : cache.new
223
+ how = if RUBY_VERSION >= '2.4'
224
+ lambda do |regexp_str, str|
225
+ (cache[regexp_str] ||= Regexp.new(regexp_str)).match?(str)
226
+ end
227
+ else
228
+ lambda do |regexp_str, str|
229
+ (cache[regexp_str] ||= Regexp.new(regexp_str)).match(str)
230
+ end
231
+ end
232
+ else
233
+ how = if RUBY_VERSION >= '2.4'
234
+ lambda{|regexp_str, str| Regexp.new(regexp_str).match?(str)}
235
+ else
236
+ lambda{|regexp_str, str| Regexp.new(regexp_str).match(str)}
237
+ end
238
+ end
239
+
240
+ db.create_function("regexp", 2) do |func, regexp_str, str|
241
+ func.result = how.call(regexp_str, str) ? 1 : 0
242
+ end
243
+ end
188
244
 
189
245
  # Yield an available connection. Rescue
190
246
  # any SQLite3::Exceptions and turn them into DatabaseErrors.
191
247
  def _execute(type, sql, opts, &block)
192
- begin
193
- synchronize(opts[:server]) do |conn|
194
- return execute_prepared_statement(conn, type, sql, opts, &block) if sql.is_a?(Symbol)
195
- log_args = opts[:arguments]
196
- args = {}
197
- opts.fetch(:arguments, OPTS).each{|k, v| args[k] = prepared_statement_argument(v)}
198
- case type
199
- when :select
200
- log_connection_yield(sql, conn, log_args){conn.query(sql, args, &block)}
201
- when :insert
202
- log_connection_yield(sql, conn, log_args){conn.execute(sql, args)}
203
- conn.last_insert_row_id
204
- when :update
205
- log_connection_yield(sql, conn, log_args){conn.execute_batch(sql, args)}
206
- conn.changes
207
- end
248
+ synchronize(opts[:server]) do |conn|
249
+ return execute_prepared_statement(conn, type, sql, opts, &block) if sql.is_a?(Symbol)
250
+ log_args = opts[:arguments]
251
+ args = {}
252
+ opts.fetch(:arguments, OPTS).each{|k, v| args[k] = prepared_statement_argument(v)}
253
+ case type
254
+ when :select
255
+ log_connection_yield(sql, conn, log_args){conn.query(sql, args, &block)}
256
+ when :insert
257
+ log_connection_yield(sql, conn, log_args){conn.execute(sql, args)}
258
+ conn.last_insert_row_id
259
+ when :update
260
+ log_connection_yield(sql, conn, log_args){conn.execute_batch(sql, args)}
261
+ conn.changes
208
262
  end
209
- rescue SQLite3::Exception => e
210
- raise_error(e)
211
263
  end
264
+ rescue SQLite3::Exception => e
265
+ raise_error(e)
212
266
  end
213
267
 
214
268
  # The SQLite adapter does not need the pool to convert exceptions.
@@ -323,6 +377,28 @@ module Sequel
323
377
  BindArgumentMethods = prepared_statements_module(:bind, ArgumentMapper)
324
378
  PreparedStatementMethods = prepared_statements_module(:prepare, BindArgumentMethods)
325
379
 
380
+ # Support regexp functions if using :setup_regexp_function Database option.
381
+ def complex_expression_sql_append(sql, op, args)
382
+ case op
383
+ when :~, :'!~', :'~*', :'!~*'
384
+ return super unless supports_regexp?
385
+
386
+ case_insensitive = [:'~*', :'!~*'].include?(op)
387
+ sql << 'NOT ' if [:'!~', :'!~*'].include?(op)
388
+ sql << '('
389
+ sql << 'LOWER(' if case_insensitive
390
+ literal_append(sql, args[0])
391
+ sql << ')' if case_insensitive
392
+ sql << ' REGEXP '
393
+ sql << 'LOWER(' if case_insensitive
394
+ literal_append(sql, args[1])
395
+ sql << ')' if case_insensitive
396
+ sql << ')'
397
+ else
398
+ super
399
+ end
400
+ end
401
+
326
402
  def fetch_rows(sql)
327
403
  execute(sql) do |result|
328
404
  cps = db.conversion_procs
@@ -346,6 +422,11 @@ module Sequel
346
422
  end
347
423
  end
348
424
  end
425
+
426
+ # Support regexp if using :setup_regexp_function Database option.
427
+ def supports_regexp?
428
+ db.allow_regexp?
429
+ end
349
430
 
350
431
  private
351
432
 
@@ -75,7 +75,7 @@ module Sequel
75
75
  return r.public_send(m) if m
76
76
  end
77
77
  end
78
- yield(r) if block_given?
78
+ yield(r) if defined?(yield)
79
79
  rescue TinyTds::Error => e
80
80
  raise_error(e, :disconnect=>!c.active?)
81
81
  ensure
@@ -0,0 +1,117 @@
1
+ # frozen-string-literal: true
2
+
3
+ require 'trilogy'
4
+ require_relative 'shared/mysql'
5
+
6
+ module Sequel
7
+ module Trilogy
8
+ class Database < Sequel::Database
9
+ include Sequel::MySQL::DatabaseMethods
10
+
11
+ QUERY_FLAGS = ::Trilogy::QUERY_FLAGS_CAST | ::Trilogy::QUERY_FLAGS_CAST_BOOLEANS
12
+ LOCAL_TIME_QUERY_FLAGS = QUERY_FLAGS | ::Trilogy::QUERY_FLAGS_LOCAL_TIMEZONE
13
+
14
+ set_adapter_scheme :trilogy
15
+
16
+ # Connect to the database. See Trilogy documentation for options.
17
+ def connect(server)
18
+ opts = server_opts(server)
19
+ opts[:username] ||= opts.delete(:user)
20
+ opts[:found_rows] = true
21
+ conn = ::Trilogy.new(opts)
22
+ mysql_connection_setting_sqls.each{|sql| log_connection_yield(sql, conn){conn.query(sql)}}
23
+ conn
24
+ end
25
+
26
+ def disconnect_connection(c)
27
+ c.discard!
28
+ rescue ::Trilogy::Error
29
+ nil
30
+ end
31
+
32
+ # Execute the given SQL on the given connection and yield the result.
33
+ def execute(sql, opts)
34
+ r = synchronize(opts[:server]) do |conn|
35
+ log_connection_yield((log_sql = opts[:log_sql]) ? sql + log_sql : sql, conn) do
36
+ conn.query_with_flags(sql, timezone.nil? || timezone == :local ? LOCAL_TIME_QUERY_FLAGS : QUERY_FLAGS)
37
+ end
38
+ end
39
+ yield r
40
+ rescue ::Trilogy::Error => e
41
+ raise_error(e)
42
+ end
43
+
44
+ def execute_dui(sql, opts=OPTS)
45
+ execute(sql, opts, &:affected_rows)
46
+ end
47
+
48
+ def execute_insert(sql, opts=OPTS)
49
+ execute(sql, opts, &:last_insert_id)
50
+ end
51
+
52
+ def freeze
53
+ server_version
54
+ super
55
+ end
56
+
57
+ # Return the version of the MySQL server to which we are connecting.
58
+ def server_version(_server=nil)
59
+ @server_version ||= super()
60
+ end
61
+
62
+ private
63
+
64
+ def database_specific_error_class(exception, opts)
65
+ case exception.message
66
+ when /1205 - Lock wait timeout exceeded; try restarting transaction\z/
67
+ DatabaseLockTimeout
68
+ else
69
+ super
70
+ end
71
+ end
72
+
73
+ def connection_execute_method
74
+ :query
75
+ end
76
+
77
+ def database_error_classes
78
+ [::Trilogy::Error]
79
+ end
80
+
81
+ def dataset_class_default
82
+ Dataset
83
+ end
84
+
85
+ # Convert tinyint(1) type to boolean if convert_tinyint_to_bool is true
86
+ def schema_column_type(db_type)
87
+ db_type =~ /\Atinyint\(1\)/ ? :boolean : super
88
+ end
89
+ end
90
+
91
+ class Dataset < Sequel::Dataset
92
+ include Sequel::MySQL::DatasetMethods
93
+
94
+ def fetch_rows(sql)
95
+ execute(sql) do |r|
96
+ self.columns = r.fields.map!{|c| output_identifier(c.to_s)}
97
+ r.each_hash{|h| yield h}
98
+ end
99
+ self
100
+ end
101
+
102
+ private
103
+
104
+ def execute(sql, opts=OPTS)
105
+ opts = Hash[opts]
106
+ opts[:type] = :select
107
+ super
108
+ end
109
+
110
+ # Handle correct quoting of strings using ::Trilogy#escape.
111
+ def literal_string_append(sql, v)
112
+ sql << "'" << db.synchronize(@opts[:server]){|c| c.escape(v)} << "'"
113
+ end
114
+ end
115
+ end
116
+ end
117
+
@@ -0,0 +1,22 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ class Dataset
5
+ module ColumnsLimit1
6
+ COLUMNS_CLONE_OPTIONS = {:distinct => nil, :limit => 1, :offset=>nil, :where=>nil, :having=>nil, :order=>nil, :row_proc=>nil, :graph=>nil, :eager_graph=>nil}.freeze
7
+
8
+ # Use a limit of 1 instead of a limit of 0 when
9
+ # getting the columns.
10
+ def columns!
11
+ ds = clone(COLUMNS_CLONE_OPTIONS)
12
+ ds.each{break}
13
+
14
+ if cols = ds.cache[:_columns]
15
+ self.columns = cols
16
+ else
17
+ []
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -34,7 +34,7 @@ module Sequel
34
34
  def execute(sql, opts=OPTS, &block)
35
35
  if opts[:sproc]
36
36
  call_sproc(sql, opts, &block)
37
- elsif sql.is_a?(Symbol)
37
+ elsif sql.is_a?(Symbol) || sql.is_a?(Sequel::Dataset::ArgumentMapper)
38
38
  execute_prepared_statement(sql, opts, &block)
39
39
  else
40
40
  synchronize(opts[:server]){|conn| _execute(conn, sql, opts, &block)}
@@ -80,6 +80,12 @@ module Sequel
80
80
  SQL::DelayedEvaluation.new(lambda{|ds| v(o.call(ds))})
81
81
  when SQL::Wrapper
82
82
  SQL::Wrapper.new(v(o.value))
83
+ when SQL::Expression
84
+ if o.respond_to?(:sequel_ast_transform)
85
+ o.sequel_ast_transform(method(:v))
86
+ else
87
+ o
88
+ end
83
89
  else
84
90
  o
85
91
  end
@@ -55,13 +55,11 @@ class Sequel::ShardedSingleConnectionPool < Sequel::ConnectionPool
55
55
  # Yields the connection to the supplied block for the given server.
56
56
  # This method simulates the ConnectionPool#hold API.
57
57
  def hold(server=:default)
58
- begin
59
- server = pick_server(server)
60
- yield(@conns[server] ||= make_new(server))
61
- rescue Sequel::DatabaseDisconnectError, *@error_classes => e
62
- disconnect_server(server) if disconnect_error?(e)
63
- raise
64
- end
58
+ server = pick_server(server)
59
+ yield(@conns[server] ||= make_new(server))
60
+ rescue Sequel::DatabaseDisconnectError, *@error_classes => e
61
+ disconnect_server(server) if disconnect_error?(e)
62
+ raise
65
63
  end
66
64
 
67
65
  # The ShardedSingleConnectionPool always has a maximum size of 1.