sequel 5.39.0 → 5.65.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (190) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +326 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +57 -25
  5. data/bin/sequel +11 -3
  6. data/doc/advanced_associations.rdoc +13 -13
  7. data/doc/association_basics.rdoc +119 -24
  8. data/doc/cheat_sheet.rdoc +11 -3
  9. data/doc/migration.rdoc +12 -6
  10. data/doc/model_hooks.rdoc +1 -1
  11. data/doc/object_model.rdoc +8 -8
  12. data/doc/opening_databases.rdoc +18 -11
  13. data/doc/postgresql.rdoc +16 -8
  14. data/doc/querying.rdoc +5 -3
  15. data/doc/release_notes/5.40.0.txt +40 -0
  16. data/doc/release_notes/5.41.0.txt +25 -0
  17. data/doc/release_notes/5.42.0.txt +136 -0
  18. data/doc/release_notes/5.43.0.txt +98 -0
  19. data/doc/release_notes/5.44.0.txt +32 -0
  20. data/doc/release_notes/5.45.0.txt +34 -0
  21. data/doc/release_notes/5.46.0.txt +87 -0
  22. data/doc/release_notes/5.47.0.txt +59 -0
  23. data/doc/release_notes/5.48.0.txt +14 -0
  24. data/doc/release_notes/5.49.0.txt +59 -0
  25. data/doc/release_notes/5.50.0.txt +78 -0
  26. data/doc/release_notes/5.51.0.txt +47 -0
  27. data/doc/release_notes/5.52.0.txt +87 -0
  28. data/doc/release_notes/5.53.0.txt +23 -0
  29. data/doc/release_notes/5.54.0.txt +27 -0
  30. data/doc/release_notes/5.55.0.txt +21 -0
  31. data/doc/release_notes/5.56.0.txt +51 -0
  32. data/doc/release_notes/5.57.0.txt +23 -0
  33. data/doc/release_notes/5.58.0.txt +31 -0
  34. data/doc/release_notes/5.59.0.txt +73 -0
  35. data/doc/release_notes/5.60.0.txt +22 -0
  36. data/doc/release_notes/5.61.0.txt +43 -0
  37. data/doc/release_notes/5.62.0.txt +132 -0
  38. data/doc/release_notes/5.63.0.txt +33 -0
  39. data/doc/release_notes/5.64.0.txt +50 -0
  40. data/doc/release_notes/5.65.0.txt +21 -0
  41. data/doc/schema_modification.rdoc +1 -1
  42. data/doc/security.rdoc +9 -9
  43. data/doc/sql.rdoc +28 -16
  44. data/doc/testing.rdoc +22 -11
  45. data/doc/transactions.rdoc +6 -6
  46. data/doc/virtual_rows.rdoc +2 -2
  47. data/lib/sequel/adapters/ado/access.rb +1 -1
  48. data/lib/sequel/adapters/ado.rb +17 -17
  49. data/lib/sequel/adapters/amalgalite.rb +3 -5
  50. data/lib/sequel/adapters/ibmdb.rb +2 -2
  51. data/lib/sequel/adapters/jdbc/derby.rb +8 -0
  52. data/lib/sequel/adapters/jdbc/h2.rb +60 -10
  53. data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
  54. data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
  55. data/lib/sequel/adapters/jdbc.rb +16 -18
  56. data/lib/sequel/adapters/mysql.rb +80 -67
  57. data/lib/sequel/adapters/mysql2.rb +54 -49
  58. data/lib/sequel/adapters/odbc.rb +6 -2
  59. data/lib/sequel/adapters/oracle.rb +4 -3
  60. data/lib/sequel/adapters/postgres.rb +83 -40
  61. data/lib/sequel/adapters/shared/access.rb +11 -1
  62. data/lib/sequel/adapters/shared/db2.rb +30 -0
  63. data/lib/sequel/adapters/shared/mssql.rb +58 -7
  64. data/lib/sequel/adapters/shared/mysql.rb +47 -2
  65. data/lib/sequel/adapters/shared/oracle.rb +76 -0
  66. data/lib/sequel/adapters/shared/postgres.rb +418 -174
  67. data/lib/sequel/adapters/shared/sqlanywhere.rb +11 -1
  68. data/lib/sequel/adapters/shared/sqlite.rb +103 -11
  69. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  70. data/lib/sequel/adapters/sqlite.rb +60 -18
  71. data/lib/sequel/adapters/tinytds.rb +1 -1
  72. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  73. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
  74. data/lib/sequel/ast_transformer.rb +6 -0
  75. data/lib/sequel/connection_pool/sharded_single.rb +5 -7
  76. data/lib/sequel/connection_pool/sharded_threaded.rb +5 -1
  77. data/lib/sequel/connection_pool/single.rb +6 -8
  78. data/lib/sequel/connection_pool/threaded.rb +8 -8
  79. data/lib/sequel/connection_pool/timed_queue.rb +257 -0
  80. data/lib/sequel/connection_pool.rb +47 -30
  81. data/lib/sequel/core.rb +28 -18
  82. data/lib/sequel/database/connecting.rb +26 -2
  83. data/lib/sequel/database/misc.rb +69 -14
  84. data/lib/sequel/database/query.rb +73 -2
  85. data/lib/sequel/database/schema_generator.rb +45 -52
  86. data/lib/sequel/database/schema_methods.rb +17 -1
  87. data/lib/sequel/dataset/actions.rb +107 -13
  88. data/lib/sequel/dataset/features.rb +20 -0
  89. data/lib/sequel/dataset/misc.rb +12 -2
  90. data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
  91. data/lib/sequel/dataset/prepared_statements.rb +2 -0
  92. data/lib/sequel/dataset/query.rb +118 -16
  93. data/lib/sequel/dataset/sql.rb +177 -47
  94. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  95. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  96. data/lib/sequel/extensions/any_not_empty.rb +1 -1
  97. data/lib/sequel/extensions/async_thread_pool.rb +438 -0
  98. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  99. data/lib/sequel/extensions/blank.rb +8 -0
  100. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  101. data/lib/sequel/extensions/core_refinements.rb +36 -11
  102. data/lib/sequel/extensions/date_arithmetic.rb +71 -31
  103. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  104. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  105. data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
  106. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  107. data/lib/sequel/extensions/inflector.rb +9 -1
  108. data/lib/sequel/extensions/is_distinct_from.rb +141 -0
  109. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  110. data/lib/sequel/extensions/migration.rb +7 -2
  111. data/lib/sequel/extensions/named_timezones.rb +26 -6
  112. data/lib/sequel/extensions/pagination.rb +1 -1
  113. data/lib/sequel/extensions/pg_array.rb +23 -3
  114. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  115. data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
  116. data/lib/sequel/extensions/pg_enum.rb +1 -1
  117. data/lib/sequel/extensions/pg_extended_date_support.rb +28 -25
  118. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  119. data/lib/sequel/extensions/pg_hstore.rb +6 -1
  120. data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
  121. data/lib/sequel/extensions/pg_inet.rb +10 -11
  122. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  123. data/lib/sequel/extensions/pg_interval.rb +45 -19
  124. data/lib/sequel/extensions/pg_json.rb +13 -15
  125. data/lib/sequel/extensions/pg_json_ops.rb +73 -2
  126. data/lib/sequel/extensions/pg_loose_count.rb +3 -1
  127. data/lib/sequel/extensions/pg_multirange.rb +367 -0
  128. data/lib/sequel/extensions/pg_range.rb +10 -23
  129. data/lib/sequel/extensions/pg_range_ops.rb +37 -9
  130. data/lib/sequel/extensions/pg_row.rb +19 -13
  131. data/lib/sequel/extensions/pg_row_ops.rb +1 -1
  132. data/lib/sequel/extensions/query.rb +2 -0
  133. data/lib/sequel/extensions/s.rb +2 -1
  134. data/lib/sequel/extensions/schema_dumper.rb +13 -2
  135. data/lib/sequel/extensions/server_block.rb +8 -12
  136. data/lib/sequel/extensions/sql_comments.rb +110 -3
  137. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  138. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  139. data/lib/sequel/extensions/string_agg.rb +1 -1
  140. data/lib/sequel/extensions/string_date_time.rb +19 -23
  141. data/lib/sequel/extensions/symbol_aref.rb +2 -0
  142. data/lib/sequel/model/associations.rb +345 -101
  143. data/lib/sequel/model/base.rb +51 -27
  144. data/lib/sequel/model/errors.rb +10 -1
  145. data/lib/sequel/model/inflections.rb +1 -1
  146. data/lib/sequel/model/plugins.rb +5 -0
  147. data/lib/sequel/plugins/association_proxies.rb +2 -0
  148. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  149. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  150. data/lib/sequel/plugins/auto_validations.rb +87 -15
  151. data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
  152. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  153. data/lib/sequel/plugins/column_encryption.rb +728 -0
  154. data/lib/sequel/plugins/composition.rb +10 -4
  155. data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
  156. data/lib/sequel/plugins/constraint_validations.rb +2 -1
  157. data/lib/sequel/plugins/dataset_associations.rb +4 -1
  158. data/lib/sequel/plugins/dirty.rb +1 -1
  159. data/lib/sequel/plugins/enum.rb +124 -0
  160. data/lib/sequel/plugins/finder.rb +4 -2
  161. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  162. data/lib/sequel/plugins/instance_specific_default.rb +1 -1
  163. data/lib/sequel/plugins/json_serializer.rb +39 -24
  164. data/lib/sequel/plugins/lazy_attributes.rb +3 -0
  165. data/lib/sequel/plugins/list.rb +3 -1
  166. data/lib/sequel/plugins/many_through_many.rb +109 -10
  167. data/lib/sequel/plugins/nested_attributes.rb +12 -7
  168. data/lib/sequel/plugins/pg_array_associations.rb +56 -38
  169. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +3 -1
  170. data/lib/sequel/plugins/prepared_statements.rb +10 -1
  171. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  172. data/lib/sequel/plugins/rcte_tree.rb +27 -19
  173. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  174. data/lib/sequel/plugins/serialization.rb +9 -3
  175. data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
  176. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  177. data/lib/sequel/plugins/sql_comments.rb +189 -0
  178. data/lib/sequel/plugins/static_cache.rb +1 -1
  179. data/lib/sequel/plugins/subclasses.rb +28 -11
  180. data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
  181. data/lib/sequel/plugins/timestamps.rb +1 -1
  182. data/lib/sequel/plugins/unused_associations.rb +521 -0
  183. data/lib/sequel/plugins/update_or_create.rb +1 -1
  184. data/lib/sequel/plugins/validate_associated.rb +22 -12
  185. data/lib/sequel/plugins/validation_helpers.rb +38 -11
  186. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  187. data/lib/sequel/sql.rb +1 -1
  188. data/lib/sequel/timezones.rb +12 -14
  189. data/lib/sequel/version.rb +1 -1
  190. metadata +101 -43
@@ -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.
@@ -239,8 +239,12 @@ module Sequel
239
239
  super
240
240
  end
241
241
  when :drop_column
242
- ocp = lambda{|oc| oc.delete_if{|c| c.to_s == op[:name].to_s}}
243
- duplicate_table(table, :old_columns_proc=>ocp){|columns| columns.delete_if{|s| s[:name].to_s == op[:name].to_s}}
242
+ if sqlite_version >= 33500
243
+ super
244
+ else
245
+ ocp = lambda{|oc| oc.delete_if{|c| c.to_s == op[:name].to_s}}
246
+ duplicate_table(table, :old_columns_proc=>ocp){|columns| columns.delete_if{|s| s[:name].to_s == op[:name].to_s}}
247
+ end
244
248
  when :rename_column
245
249
  if sqlite_version >= 32500
246
250
  super
@@ -316,6 +320,12 @@ module Sequel
316
320
  end
317
321
  end
318
322
 
323
+ # SQLite does not restrict the integer or decimal type to a specific range.
324
+ def column_schema_integer_min_max_values(column)
325
+ nil
326
+ end
327
+ alias column_schema_decimal_min_max_values column_schema_integer_min_max_values
328
+
319
329
  # Array of PRAGMA SQL statements based on the Database options that should be applied to
320
330
  # new connections.
321
331
  def connection_pragmas
@@ -333,6 +343,11 @@ module Sequel
333
343
  ps
334
344
  end
335
345
 
346
+ # Support creating STRICT tables via :strict option
347
+ def create_table_sql(name, generator, options)
348
+ "#{super}#{' STRICT' if options[:strict]}"
349
+ end
350
+
336
351
  # SQLite support creating temporary views.
337
352
  def create_view_prefix_sql(name, options)
338
353
  create_view_sql_append_columns("CREATE #{'TEMPORARY 'if options[:temp]}VIEW #{quote_schema_table(name)}", options[:columns])
@@ -343,6 +358,7 @@ module Sequel
343
358
  /foreign key constraint failed\z/i => ForeignKeyConstraintViolation,
344
359
  /\A(SQLITE ERROR 275 \(CONSTRAINT_CHECK\) : )?CHECK constraint failed/ => CheckConstraintViolation,
345
360
  /\A(SQLITE ERROR 19 \(CONSTRAINT\) : )?constraint failed\z/ => ConstraintViolation,
361
+ /\Acannot store [A-Z]+ value in [A-Z]+ column / => ConstraintViolation,
346
362
  /may not be NULL\z|NOT NULL constraint failed: .+\z/ => NotNullConstraintViolation,
347
363
  /\ASQLITE ERROR \d+ \(\) : CHECK constraint failed: / => CheckConstraintViolation
348
364
  }.freeze
@@ -389,7 +405,7 @@ module Sequel
389
405
  old_columns = def_columns.map{|c| c[:name]}
390
406
  opts[:old_columns_proc].call(old_columns) if opts[:old_columns_proc]
391
407
 
392
- yield def_columns if block_given?
408
+ yield def_columns if defined?(yield)
393
409
 
394
410
  constraints = (opts[:constraints] || []).dup
395
411
  pks = []
@@ -424,10 +440,10 @@ module Sequel
424
440
  skip_indexes = []
425
441
  indexes(table, :only_autocreated=>true).each do |name, h|
426
442
  skip_indexes << name
427
- if h[:unique]
443
+ if h[:unique] && !opts[:no_unique]
428
444
  if h[:columns].length == 1
429
445
  unique_columns.concat(h[:columns])
430
- elsif h[:columns].map(&:to_s) != pks && !opts[:no_unique]
446
+ elsif h[:columns].map(&:to_s) != pks
431
447
  constraints << {:type=>:unique, :columns=>h[:columns]}
432
448
  end
433
449
  end
@@ -558,10 +574,10 @@ module Sequel
558
574
  EXTRACT_MAP = {:year=>"'%Y'", :month=>"'%m'", :day=>"'%d'", :hour=>"'%H'", :minute=>"'%M'", :second=>"'%f'"}.freeze
559
575
  EXTRACT_MAP.each_value(&:freeze)
560
576
 
561
- Dataset.def_sql_method(self, :delete, [['if db.sqlite_version >= 30803', %w'with delete from where'], ["else", %w'delete from where']])
562
- 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']])
577
+ 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']])
578
+ 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']])
563
579
  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']])
564
- Dataset.def_sql_method(self, :update, [['if db.sqlite_version >= 30803', %w'with update table set where'], ["else", %w'update table set where']])
580
+ 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']])
565
581
 
566
582
  def cast_sql_append(sql, expr, type)
567
583
  if type == Time or type == DateTime
@@ -635,8 +651,8 @@ module Sequel
635
651
  # SQLite performs a TRUNCATE style DELETE if no filter is specified.
636
652
  # Since we want to always return the count of records, add a condition
637
653
  # that is always true and then delete.
638
- def delete
639
- @opts[:where] ? super : where(1=>1).delete
654
+ def delete(&block)
655
+ @opts[:where] ? super : where(1=>1).delete(&block)
640
656
  end
641
657
 
642
658
  # Return an array of strings specifying a query explanation for a SELECT of the
@@ -653,10 +669,25 @@ module Sequel
653
669
 
654
670
  # HAVING requires GROUP BY on SQLite
655
671
  def having(*cond)
656
- raise(InvalidOperation, "Can only specify a HAVING clause on a grouped dataset") unless @opts[:group]
672
+ raise(InvalidOperation, "Can only specify a HAVING clause on a grouped dataset") if !@opts[:group] && db.sqlite_version < 33900
657
673
  super
658
674
  end
659
675
 
676
+ # Support insert select for associations, so that the model code can use
677
+ # returning instead of a separate query.
678
+ def insert_select(*values)
679
+ return unless supports_insert_select?
680
+ # Handle case where query does not return a row
681
+ server?(:default).with_sql_first(insert_select_sql(*values)) || false
682
+ end
683
+
684
+ # The SQL to use for an insert_select, adds a RETURNING clause to the insert
685
+ # unless the RETURNING clause is already present.
686
+ def insert_select_sql(*values)
687
+ ds = opts[:returning] ? self : returning
688
+ ds.insert_sql(*values)
689
+ end
690
+
660
691
  # SQLite uses the nonstandard ` (backtick) for quoting identifiers.
661
692
  def quoted_identifier_append(sql, c)
662
693
  sql << '`' << c.to_s.gsub('`', '``') << '`'
@@ -738,6 +769,13 @@ module Sequel
738
769
  insert_conflict(:ignore)
739
770
  end
740
771
 
772
+ # Automatically add aliases to RETURNING values to work around SQLite bug.
773
+ def returning(*values)
774
+ return super if values.empty?
775
+ raise Error, "RETURNING is not supported on #{db.database_type}" unless supports_returning?(:insert)
776
+ clone(:returning=>_returning_values(values).freeze)
777
+ end
778
+
741
779
  # SQLite 3.8.3+ supports common table expressions.
742
780
  def supports_cte?(type=:select)
743
781
  db.sqlite_version >= 30803
@@ -753,6 +791,11 @@ module Sequel
753
791
  false
754
792
  end
755
793
 
794
+ # SQLite does not support deleting from a joined dataset
795
+ def supports_deleting_joins?
796
+ false
797
+ end
798
+
756
799
  # SQLite does not support INTERSECT ALL or EXCEPT ALL
757
800
  def supports_intersect_except_all?
758
801
  false
@@ -763,11 +806,21 @@ module Sequel
763
806
  false
764
807
  end
765
808
 
809
+ # SQLite 3.33.0 supports modifying joined datasets
810
+ def supports_modifying_joins?
811
+ db.sqlite_version >= 33300
812
+ end
813
+
766
814
  # SQLite does not support multiple columns for the IN/NOT IN operators
767
815
  def supports_multiple_column_in?
768
816
  false
769
817
  end
770
818
 
819
+ # SQLite 3.35.0 supports RETURNING on INSERT/UPDATE/DELETE.
820
+ def supports_returning?(_)
821
+ db.sqlite_version >= 33500
822
+ end
823
+
771
824
  # SQLite supports timezones in literal timestamps, since it stores them
772
825
  # as text. But using timezones in timestamps breaks SQLite datetime
773
826
  # functions, so we allow the user to override the default per database.
@@ -800,6 +853,21 @@ module Sequel
800
853
 
801
854
  private
802
855
 
856
+ # Add aliases to symbols and identifiers to work around SQLite bug.
857
+ def _returning_values(values)
858
+ values.map do |v|
859
+ case v
860
+ when Symbol
861
+ _, c, a = split_symbol(v)
862
+ a ? v : Sequel.as(v, c)
863
+ when SQL::Identifier, SQL::QualifiedIdentifier
864
+ Sequel.as(v, unqualified_column_for(v))
865
+ else
866
+ v
867
+ end
868
+ end
869
+ end
870
+
803
871
  # SQLite uses string literals instead of identifiers in AS clauses.
804
872
  def as_sql_append(sql, aliaz, column_aliases=nil)
805
873
  raise Error, "sqlite does not support derived column lists" if column_aliases
@@ -825,6 +893,13 @@ module Sequel
825
893
  end
826
894
  end
827
895
 
896
+ # Raise an InvalidOperation exception if insert is not allowed for this dataset.
897
+ def check_insert_allowed!
898
+ raise(InvalidOperation, "Grouped datasets cannot be modified") if opts[:group]
899
+ raise(InvalidOperation, "Joined datasets cannot be modified") if joined_dataset?
900
+ end
901
+ alias check_delete_allowed! check_insert_allowed!
902
+
828
903
  # SQLite supports a maximum of 500 rows in a VALUES clause.
829
904
  def default_import_slice
830
905
  500
@@ -944,6 +1019,23 @@ module Sequel
944
1019
  def _truncate_sql(table)
945
1020
  "DELETE FROM #{table}"
946
1021
  end
1022
+
1023
+ # Use FROM to specify additional tables in an update query
1024
+ def update_from_sql(sql)
1025
+ if(from = @opts[:from][1..-1]).empty?
1026
+ raise(Error, 'Need multiple FROM tables if updating/deleting a dataset with JOINs') if @opts[:join]
1027
+ else
1028
+ sql << ' FROM '
1029
+ source_list_append(sql, from)
1030
+ select_join_sql(sql)
1031
+ end
1032
+ end
1033
+
1034
+ # Only include the primary table in the main update clause
1035
+ def update_table_sql(sql)
1036
+ sql << ' '
1037
+ source_list_append(sql, @opts[:from][0..0])
1038
+ end
947
1039
  end
948
1040
  end
949
1041
  end
@@ -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
  #
@@ -119,6 +124,12 @@ module Sequel
119
124
  end
120
125
 
121
126
  connection_pragmas.each{|s| log_connection_yield(s, db){db.execute_batch(s)}}
127
+
128
+ if typecast_value_boolean(opts[:setup_regexp_function])
129
+ db.create_function("regexp", 2) do |func, regexp_str, string|
130
+ func.result = Regexp.new(regexp_str).match(string) ? 1 : 0
131
+ end
132
+ end
122
133
 
123
134
  class << db
124
135
  attr_reader :prepared_statements
@@ -128,6 +139,12 @@ module Sequel
128
139
  db
129
140
  end
130
141
 
142
+ # Whether this Database instance is setup to allow regexp matching.
143
+ # True if the :setup_regexp_function option was passed when creating the Database.
144
+ def allow_regexp?
145
+ @allow_regexp
146
+ end
147
+
131
148
  # Disconnect given connections from the database.
132
149
  def disconnect_connection(c)
133
150
  c.prepared_statements.each_value{|v| v.first.close}
@@ -189,26 +206,24 @@ module Sequel
189
206
  # Yield an available connection. Rescue
190
207
  # any SQLite3::Exceptions and turn them into DatabaseErrors.
191
208
  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
209
+ synchronize(opts[:server]) do |conn|
210
+ return execute_prepared_statement(conn, type, sql, opts, &block) if sql.is_a?(Symbol)
211
+ log_args = opts[:arguments]
212
+ args = {}
213
+ opts.fetch(:arguments, OPTS).each{|k, v| args[k] = prepared_statement_argument(v)}
214
+ case type
215
+ when :select
216
+ log_connection_yield(sql, conn, log_args){conn.query(sql, args, &block)}
217
+ when :insert
218
+ log_connection_yield(sql, conn, log_args){conn.execute(sql, args)}
219
+ conn.last_insert_row_id
220
+ when :update
221
+ log_connection_yield(sql, conn, log_args){conn.execute_batch(sql, args)}
222
+ conn.changes
208
223
  end
209
- rescue SQLite3::Exception => e
210
- raise_error(e)
211
224
  end
225
+ rescue SQLite3::Exception => e
226
+ raise_error(e)
212
227
  end
213
228
 
214
229
  # The SQLite adapter does not need the pool to convert exceptions.
@@ -323,6 +338,28 @@ module Sequel
323
338
  BindArgumentMethods = prepared_statements_module(:bind, ArgumentMapper)
324
339
  PreparedStatementMethods = prepared_statements_module(:prepare, BindArgumentMethods)
325
340
 
341
+ # Support regexp functions if using :setup_regexp_function Database option.
342
+ def complex_expression_sql_append(sql, op, args)
343
+ case op
344
+ when :~, :'!~', :'~*', :'!~*'
345
+ return super unless supports_regexp?
346
+
347
+ case_insensitive = [:'~*', :'!~*'].include?(op)
348
+ sql << 'NOT ' if [:'!~', :'!~*'].include?(op)
349
+ sql << '('
350
+ sql << 'LOWER(' if case_insensitive
351
+ literal_append(sql, args[0])
352
+ sql << ')' if case_insensitive
353
+ sql << ' REGEXP '
354
+ sql << 'LOWER(' if case_insensitive
355
+ literal_append(sql, args[1])
356
+ sql << ')' if case_insensitive
357
+ sql << ')'
358
+ else
359
+ super
360
+ end
361
+ end
362
+
326
363
  def fetch_rows(sql)
327
364
  execute(sql) do |result|
328
365
  cps = db.conversion_procs
@@ -346,6 +383,11 @@ module Sequel
346
383
  end
347
384
  end
348
385
  end
386
+
387
+ # Support regexp if using :setup_regexp_function Database option.
388
+ def supports_regexp?
389
+ db.allow_regexp?
390
+ end
349
391
 
350
392
  private
351
393
 
@@ -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,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.
@@ -22,6 +22,8 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
22
22
  @connections_to_disconnect = []
23
23
  @servers = opts.fetch(:servers_hash, Hash.new(:default))
24
24
  remove_instance_variable(:@waiter)
25
+ remove_instance_variable(:@allocated)
26
+ @allocated = {}
25
27
  @waiters = {}
26
28
 
27
29
  add_servers([:default])
@@ -36,7 +38,9 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
36
38
  unless @servers.has_key?(server)
37
39
  @servers[server] = server
38
40
  @available_connections[server] = []
39
- @allocated[server] = {}
41
+ allocated = {}
42
+ allocated.compare_by_identity
43
+ @allocated[server] = allocated
40
44
  @waiters[server] = ConditionVariable.new
41
45
  end
42
46
  end
@@ -24,15 +24,13 @@ class Sequel::SingleConnectionPool < Sequel::ConnectionPool
24
24
 
25
25
  # Yield the connection to the block.
26
26
  def hold(server=nil)
27
- begin
28
- unless c = @conn.first
29
- @conn.replace([c = make_new(:default)])
30
- end
31
- yield c
32
- rescue Sequel::DatabaseDisconnectError, *@error_classes => e
33
- disconnect if disconnect_error?(e)
34
- raise
27
+ unless c = @conn.first
28
+ @conn.replace([c = make_new(:default)])
35
29
  end
30
+ yield c
31
+ rescue Sequel::DatabaseDisconnectError, *@error_classes => e
32
+ disconnect if disconnect_error?(e)
33
+ raise
36
34
  end
37
35
 
38
36
  # The SingleConnectionPool always has a maximum size of 1.
@@ -3,7 +3,7 @@
3
3
  # A connection pool allowing multi-threaded access to a pool of connections.
4
4
  # This is the default connection pool used by Sequel.
5
5
  class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
6
- USE_WAITER = true
6
+ USE_WAITER = true # SEQUEL6: Remove
7
7
  Sequel::Deprecation.deprecate_constant(self, :USE_WAITER)
8
8
 
9
9
  # The maximum number of connections this pool will create (per shard/server
@@ -12,17 +12,17 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
12
12
 
13
13
  # An array of connections that are available for use by the pool.
14
14
  # The calling code should already have the mutex before calling this.
15
- attr_reader :available_connections
15
+ attr_reader :available_connections # SEQUEL6: Remove
16
16
 
17
- # A hash with thread keys and connection values for currently allocated connections.
17
+ # A hash with thread/fiber keys and connection values for currently allocated connections.
18
18
  # The calling code should already have the mutex before calling this.
19
- attr_reader :allocated
19
+ attr_reader :allocated # SEQUEL6: Remove
20
20
 
21
21
  # The following additional options are respected:
22
22
  # :max_connections :: The maximum number of connections the connection pool
23
23
  # will open (default 4)
24
24
  # :pool_timeout :: The amount of seconds to wait to acquire a connection
25
- # before raising a PoolTimeoutError (default 5)
25
+ # before raising a PoolTimeout error (default 5)
26
26
  def initialize(db, opts = OPTS)
27
27
  super
28
28
  @max_size = Integer(opts[:max_connections] || 4)
@@ -31,6 +31,7 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
31
31
  @connection_handling = opts[:connection_handling]
32
32
  @available_connections = []
33
33
  @allocated = {}
34
+ @allocated.compare_by_identity
34
35
  @timeout = Float(opts[:pool_timeout] || 5)
35
36
  @waiter = ConditionVariable.new
36
37
  end
@@ -49,8 +50,7 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
49
50
  end
50
51
  end
51
52
 
52
- # Removes all connections currently available, optionally
53
- # yielding each connection to the given block. This method has the effect of
53
+ # Removes all connections currently available. This method has the effect of
54
54
  # disconnecting from the database, assuming that no connections are currently
55
55
  # being used. If you want to be able to disconnect connections that are
56
56
  # currently in use, use the ShardedThreadedConnectionPool, which can do that.
@@ -134,7 +134,7 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
134
134
  # calling this.
135
135
  #
136
136
  # This should return a connection is one is available within the timeout,
137
- # or nil if a connection could not be acquired within the timeout.
137
+ # or raise PoolTimeout if a connection could not be acquired within the timeout.
138
138
  def acquire(thread)
139
139
  if conn = assign_connection(thread)
140
140
  return conn