sequel 5.39.0 → 5.62.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (181) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +294 -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 +89 -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/schema_modification.rdoc +1 -1
  39. data/doc/security.rdoc +9 -9
  40. data/doc/sql.rdoc +27 -15
  41. data/doc/testing.rdoc +22 -11
  42. data/doc/transactions.rdoc +6 -6
  43. data/doc/virtual_rows.rdoc +2 -2
  44. data/lib/sequel/adapters/ado/access.rb +1 -1
  45. data/lib/sequel/adapters/ado.rb +17 -17
  46. data/lib/sequel/adapters/amalgalite.rb +3 -5
  47. data/lib/sequel/adapters/ibmdb.rb +2 -2
  48. data/lib/sequel/adapters/jdbc/derby.rb +8 -0
  49. data/lib/sequel/adapters/jdbc/h2.rb +60 -10
  50. data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
  51. data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
  52. data/lib/sequel/adapters/jdbc.rb +16 -18
  53. data/lib/sequel/adapters/mysql.rb +80 -67
  54. data/lib/sequel/adapters/mysql2.rb +54 -49
  55. data/lib/sequel/adapters/odbc.rb +6 -2
  56. data/lib/sequel/adapters/oracle.rb +3 -3
  57. data/lib/sequel/adapters/postgres.rb +83 -40
  58. data/lib/sequel/adapters/shared/access.rb +11 -1
  59. data/lib/sequel/adapters/shared/db2.rb +30 -0
  60. data/lib/sequel/adapters/shared/mssql.rb +58 -7
  61. data/lib/sequel/adapters/shared/mysql.rb +40 -2
  62. data/lib/sequel/adapters/shared/oracle.rb +76 -0
  63. data/lib/sequel/adapters/shared/postgres.rb +418 -174
  64. data/lib/sequel/adapters/shared/sqlanywhere.rb +10 -0
  65. data/lib/sequel/adapters/shared/sqlite.rb +102 -11
  66. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  67. data/lib/sequel/adapters/sqlite.rb +60 -18
  68. data/lib/sequel/adapters/tinytds.rb +1 -1
  69. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  70. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
  71. data/lib/sequel/ast_transformer.rb +6 -0
  72. data/lib/sequel/connection_pool/sharded_single.rb +5 -7
  73. data/lib/sequel/connection_pool/single.rb +6 -8
  74. data/lib/sequel/connection_pool.rb +42 -28
  75. data/lib/sequel/core.rb +28 -18
  76. data/lib/sequel/database/connecting.rb +26 -2
  77. data/lib/sequel/database/misc.rb +69 -14
  78. data/lib/sequel/database/query.rb +38 -1
  79. data/lib/sequel/database/schema_generator.rb +45 -52
  80. data/lib/sequel/database/schema_methods.rb +17 -1
  81. data/lib/sequel/dataset/actions.rb +82 -13
  82. data/lib/sequel/dataset/features.rb +20 -0
  83. data/lib/sequel/dataset/misc.rb +1 -1
  84. data/lib/sequel/dataset/prepared_statements.rb +2 -0
  85. data/lib/sequel/dataset/query.rb +118 -16
  86. data/lib/sequel/dataset/sql.rb +177 -47
  87. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  88. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  89. data/lib/sequel/extensions/any_not_empty.rb +1 -1
  90. data/lib/sequel/extensions/async_thread_pool.rb +438 -0
  91. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  92. data/lib/sequel/extensions/blank.rb +8 -0
  93. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  94. data/lib/sequel/extensions/core_refinements.rb +36 -11
  95. data/lib/sequel/extensions/date_arithmetic.rb +71 -31
  96. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  97. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  98. data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
  99. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  100. data/lib/sequel/extensions/inflector.rb +9 -1
  101. data/lib/sequel/extensions/is_distinct_from.rb +141 -0
  102. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  103. data/lib/sequel/extensions/migration.rb +7 -2
  104. data/lib/sequel/extensions/named_timezones.rb +22 -6
  105. data/lib/sequel/extensions/pagination.rb +1 -1
  106. data/lib/sequel/extensions/pg_array.rb +23 -3
  107. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  108. data/lib/sequel/extensions/pg_auto_parameterize.rb +478 -0
  109. data/lib/sequel/extensions/pg_enum.rb +1 -1
  110. data/lib/sequel/extensions/pg_extended_date_support.rb +28 -25
  111. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  112. data/lib/sequel/extensions/pg_hstore.rb +6 -1
  113. data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
  114. data/lib/sequel/extensions/pg_inet.rb +10 -11
  115. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  116. data/lib/sequel/extensions/pg_interval.rb +45 -19
  117. data/lib/sequel/extensions/pg_json.rb +13 -15
  118. data/lib/sequel/extensions/pg_json_ops.rb +73 -2
  119. data/lib/sequel/extensions/pg_loose_count.rb +3 -1
  120. data/lib/sequel/extensions/pg_multirange.rb +367 -0
  121. data/lib/sequel/extensions/pg_range.rb +10 -23
  122. data/lib/sequel/extensions/pg_range_ops.rb +37 -9
  123. data/lib/sequel/extensions/pg_row.rb +19 -13
  124. data/lib/sequel/extensions/pg_row_ops.rb +1 -1
  125. data/lib/sequel/extensions/query.rb +2 -0
  126. data/lib/sequel/extensions/s.rb +2 -1
  127. data/lib/sequel/extensions/schema_dumper.rb +13 -2
  128. data/lib/sequel/extensions/server_block.rb +8 -12
  129. data/lib/sequel/extensions/sql_comments.rb +110 -3
  130. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  131. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  132. data/lib/sequel/extensions/string_agg.rb +1 -1
  133. data/lib/sequel/extensions/string_date_time.rb +19 -23
  134. data/lib/sequel/extensions/symbol_aref.rb +2 -0
  135. data/lib/sequel/model/associations.rb +324 -95
  136. data/lib/sequel/model/base.rb +51 -27
  137. data/lib/sequel/model/errors.rb +10 -1
  138. data/lib/sequel/model/inflections.rb +1 -1
  139. data/lib/sequel/model/plugins.rb +5 -0
  140. data/lib/sequel/plugins/association_proxies.rb +2 -0
  141. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  142. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  143. data/lib/sequel/plugins/auto_validations.rb +87 -15
  144. data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
  145. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  146. data/lib/sequel/plugins/column_encryption.rb +728 -0
  147. data/lib/sequel/plugins/composition.rb +10 -4
  148. data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
  149. data/lib/sequel/plugins/constraint_validations.rb +2 -1
  150. data/lib/sequel/plugins/dataset_associations.rb +4 -1
  151. data/lib/sequel/plugins/dirty.rb +1 -1
  152. data/lib/sequel/plugins/enum.rb +124 -0
  153. data/lib/sequel/plugins/finder.rb +3 -1
  154. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  155. data/lib/sequel/plugins/instance_specific_default.rb +1 -1
  156. data/lib/sequel/plugins/json_serializer.rb +39 -24
  157. data/lib/sequel/plugins/lazy_attributes.rb +3 -0
  158. data/lib/sequel/plugins/list.rb +3 -1
  159. data/lib/sequel/plugins/many_through_many.rb +108 -9
  160. data/lib/sequel/plugins/nested_attributes.rb +12 -7
  161. data/lib/sequel/plugins/pg_array_associations.rb +56 -38
  162. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +3 -1
  163. data/lib/sequel/plugins/prepared_statements.rb +10 -1
  164. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  165. data/lib/sequel/plugins/rcte_tree.rb +27 -19
  166. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  167. data/lib/sequel/plugins/serialization.rb +9 -3
  168. data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
  169. data/lib/sequel/plugins/sql_comments.rb +189 -0
  170. data/lib/sequel/plugins/static_cache.rb +1 -1
  171. data/lib/sequel/plugins/subclasses.rb +28 -11
  172. data/lib/sequel/plugins/tactical_eager_loading.rb +15 -2
  173. data/lib/sequel/plugins/timestamps.rb +1 -1
  174. data/lib/sequel/plugins/unused_associations.rb +521 -0
  175. data/lib/sequel/plugins/update_or_create.rb +1 -1
  176. data/lib/sequel/plugins/validation_helpers.rb +38 -11
  177. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  178. data/lib/sequel/sql.rb +1 -1
  179. data/lib/sequel/timezones.rb +12 -14
  180. data/lib/sequel/version.rb +1 -1
  181. metadata +94 -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)
@@ -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,11 @@ module Sequel
316
320
  end
317
321
  end
318
322
 
323
+ # SQLite does not restrict the integer type to a specific range.
324
+ def column_schema_integer_min_max_values(db_type)
325
+ nil
326
+ end
327
+
319
328
  # Array of PRAGMA SQL statements based on the Database options that should be applied to
320
329
  # new connections.
321
330
  def connection_pragmas
@@ -333,6 +342,11 @@ module Sequel
333
342
  ps
334
343
  end
335
344
 
345
+ # Support creating STRICT tables via :strict option
346
+ def create_table_sql(name, generator, options)
347
+ "#{super}#{' STRICT' if options[:strict]}"
348
+ end
349
+
336
350
  # SQLite support creating temporary views.
337
351
  def create_view_prefix_sql(name, options)
338
352
  create_view_sql_append_columns("CREATE #{'TEMPORARY 'if options[:temp]}VIEW #{quote_schema_table(name)}", options[:columns])
@@ -343,6 +357,7 @@ module Sequel
343
357
  /foreign key constraint failed\z/i => ForeignKeyConstraintViolation,
344
358
  /\A(SQLITE ERROR 275 \(CONSTRAINT_CHECK\) : )?CHECK constraint failed/ => CheckConstraintViolation,
345
359
  /\A(SQLITE ERROR 19 \(CONSTRAINT\) : )?constraint failed\z/ => ConstraintViolation,
360
+ /\Acannot store [A-Z]+ value in [A-Z]+ column / => ConstraintViolation,
346
361
  /may not be NULL\z|NOT NULL constraint failed: .+\z/ => NotNullConstraintViolation,
347
362
  /\ASQLITE ERROR \d+ \(\) : CHECK constraint failed: / => CheckConstraintViolation
348
363
  }.freeze
@@ -389,7 +404,7 @@ module Sequel
389
404
  old_columns = def_columns.map{|c| c[:name]}
390
405
  opts[:old_columns_proc].call(old_columns) if opts[:old_columns_proc]
391
406
 
392
- yield def_columns if block_given?
407
+ yield def_columns if defined?(yield)
393
408
 
394
409
  constraints = (opts[:constraints] || []).dup
395
410
  pks = []
@@ -424,10 +439,10 @@ module Sequel
424
439
  skip_indexes = []
425
440
  indexes(table, :only_autocreated=>true).each do |name, h|
426
441
  skip_indexes << name
427
- if h[:unique]
442
+ if h[:unique] && !opts[:no_unique]
428
443
  if h[:columns].length == 1
429
444
  unique_columns.concat(h[:columns])
430
- elsif h[:columns].map(&:to_s) != pks && !opts[:no_unique]
445
+ elsif h[:columns].map(&:to_s) != pks
431
446
  constraints << {:type=>:unique, :columns=>h[:columns]}
432
447
  end
433
448
  end
@@ -558,10 +573,10 @@ module Sequel
558
573
  EXTRACT_MAP = {:year=>"'%Y'", :month=>"'%m'", :day=>"'%d'", :hour=>"'%H'", :minute=>"'%M'", :second=>"'%f'"}.freeze
559
574
  EXTRACT_MAP.each_value(&:freeze)
560
575
 
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']])
576
+ 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']])
577
+ 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
578
  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']])
579
+ 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
580
 
566
581
  def cast_sql_append(sql, expr, type)
567
582
  if type == Time or type == DateTime
@@ -635,8 +650,8 @@ module Sequel
635
650
  # SQLite performs a TRUNCATE style DELETE if no filter is specified.
636
651
  # Since we want to always return the count of records, add a condition
637
652
  # that is always true and then delete.
638
- def delete
639
- @opts[:where] ? super : where(1=>1).delete
653
+ def delete(&block)
654
+ @opts[:where] ? super : where(1=>1).delete(&block)
640
655
  end
641
656
 
642
657
  # Return an array of strings specifying a query explanation for a SELECT of the
@@ -653,10 +668,25 @@ module Sequel
653
668
 
654
669
  # HAVING requires GROUP BY on SQLite
655
670
  def having(*cond)
656
- raise(InvalidOperation, "Can only specify a HAVING clause on a grouped dataset") unless @opts[:group]
671
+ raise(InvalidOperation, "Can only specify a HAVING clause on a grouped dataset") if !@opts[:group] && db.sqlite_version < 33900
657
672
  super
658
673
  end
659
674
 
675
+ # Support insert select for associations, so that the model code can use
676
+ # returning instead of a separate query.
677
+ def insert_select(*values)
678
+ return unless supports_insert_select?
679
+ # Handle case where query does not return a row
680
+ server?(:default).with_sql_first(insert_select_sql(*values)) || false
681
+ end
682
+
683
+ # The SQL to use for an insert_select, adds a RETURNING clause to the insert
684
+ # unless the RETURNING clause is already present.
685
+ def insert_select_sql(*values)
686
+ ds = opts[:returning] ? self : returning
687
+ ds.insert_sql(*values)
688
+ end
689
+
660
690
  # SQLite uses the nonstandard ` (backtick) for quoting identifiers.
661
691
  def quoted_identifier_append(sql, c)
662
692
  sql << '`' << c.to_s.gsub('`', '``') << '`'
@@ -738,6 +768,13 @@ module Sequel
738
768
  insert_conflict(:ignore)
739
769
  end
740
770
 
771
+ # Automatically add aliases to RETURNING values to work around SQLite bug.
772
+ def returning(*values)
773
+ return super if values.empty?
774
+ raise Error, "RETURNING is not supported on #{db.database_type}" unless supports_returning?(:insert)
775
+ clone(:returning=>_returning_values(values).freeze)
776
+ end
777
+
741
778
  # SQLite 3.8.3+ supports common table expressions.
742
779
  def supports_cte?(type=:select)
743
780
  db.sqlite_version >= 30803
@@ -753,6 +790,11 @@ module Sequel
753
790
  false
754
791
  end
755
792
 
793
+ # SQLite does not support deleting from a joined dataset
794
+ def supports_deleting_joins?
795
+ false
796
+ end
797
+
756
798
  # SQLite does not support INTERSECT ALL or EXCEPT ALL
757
799
  def supports_intersect_except_all?
758
800
  false
@@ -763,11 +805,21 @@ module Sequel
763
805
  false
764
806
  end
765
807
 
808
+ # SQLite 3.33.0 supports modifying joined datasets
809
+ def supports_modifying_joins?
810
+ db.sqlite_version >= 33300
811
+ end
812
+
766
813
  # SQLite does not support multiple columns for the IN/NOT IN operators
767
814
  def supports_multiple_column_in?
768
815
  false
769
816
  end
770
817
 
818
+ # SQLite 3.35.0 supports RETURNING on INSERT/UPDATE/DELETE.
819
+ def supports_returning?(_)
820
+ db.sqlite_version >= 33500
821
+ end
822
+
771
823
  # SQLite supports timezones in literal timestamps, since it stores them
772
824
  # as text. But using timezones in timestamps breaks SQLite datetime
773
825
  # functions, so we allow the user to override the default per database.
@@ -800,6 +852,21 @@ module Sequel
800
852
 
801
853
  private
802
854
 
855
+ # Add aliases to symbols and identifiers to work around SQLite bug.
856
+ def _returning_values(values)
857
+ values.map do |v|
858
+ case v
859
+ when Symbol
860
+ _, c, a = split_symbol(v)
861
+ a ? v : Sequel.as(v, c)
862
+ when SQL::Identifier, SQL::QualifiedIdentifier
863
+ Sequel.as(v, unqualified_column_for(v))
864
+ else
865
+ v
866
+ end
867
+ end
868
+ end
869
+
803
870
  # SQLite uses string literals instead of identifiers in AS clauses.
804
871
  def as_sql_append(sql, aliaz, column_aliases=nil)
805
872
  raise Error, "sqlite does not support derived column lists" if column_aliases
@@ -825,6 +892,13 @@ module Sequel
825
892
  end
826
893
  end
827
894
 
895
+ # Raise an InvalidOperation exception if insert is not allowed for this dataset.
896
+ def check_insert_allowed!
897
+ raise(InvalidOperation, "Grouped datasets cannot be modified") if opts[:group]
898
+ raise(InvalidOperation, "Joined datasets cannot be modified") if joined_dataset?
899
+ end
900
+ alias check_delete_allowed! check_insert_allowed!
901
+
828
902
  # SQLite supports a maximum of 500 rows in a VALUES clause.
829
903
  def default_import_slice
830
904
  500
@@ -944,6 +1018,23 @@ module Sequel
944
1018
  def _truncate_sql(table)
945
1019
  "DELETE FROM #{table}"
946
1020
  end
1021
+
1022
+ # Use FROM to specify additional tables in an update query
1023
+ def update_from_sql(sql)
1024
+ if(from = @opts[:from][1..-1]).empty?
1025
+ raise(Error, 'Need multiple FROM tables if updating/deleting a dataset with JOINs') if @opts[:join]
1026
+ else
1027
+ sql << ' FROM '
1028
+ source_list_append(sql, from)
1029
+ select_join_sql(sql)
1030
+ end
1031
+ end
1032
+
1033
+ # Only include the primary table in the main update clause
1034
+ def update_table_sql(sql)
1035
+ sql << ' '
1036
+ source_list_append(sql, @opts[:from][0..0])
1037
+ end
947
1038
  end
948
1039
  end
949
1040
  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.
@@ -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.
@@ -74,26 +74,35 @@ class Sequel::ConnectionPool
74
74
 
75
75
  # The after_connect proc used for this pool. This is called with each new
76
76
  # connection made, and is usually used to set custom per-connection settings.
77
- attr_accessor :after_connect
77
+ # Deprecated.
78
+ attr_reader :after_connect # SEQUEL6: Remove
78
79
 
79
- # An array of sql strings to execute on each new connection.
80
- attr_accessor :connect_sqls
80
+ # Override the after_connect proc for the connection pool. Deprecated.
81
+ # Disables support for shard-specific :after_connect and :connect_sqls if used.
82
+ def after_connect=(v) # SEQUEL6: Remove
83
+ @use_old_connect_api = true
84
+ @after_connect = v
85
+ end
86
+
87
+ # An array of sql strings to execute on each new connection. Deprecated.
88
+ attr_reader :connect_sqls # SEQUEL6: Remove
89
+
90
+ # Override the connect_sqls for the connection pool. Deprecated.
91
+ # Disables support for shard-specific :after_connect and :connect_sqls if used.
92
+ def connect_sqls=(v) # SEQUEL6: Remove
93
+ @use_old_connect_api = true
94
+ @connect_sqls = v
95
+ end
81
96
 
82
97
  # The Sequel::Database object tied to this connection pool.
83
98
  attr_accessor :db
84
99
 
85
- # Instantiates a connection pool with the given options. The block is called
86
- # with a single symbol (specifying the server/shard to use) every time a new
87
- # connection is needed. The following options are respected for all connection
88
- # pools:
89
- # :after_connect :: A callable object called after each new connection is made, with the
90
- # connection object (and server argument if the callable accepts 2 arguments),
91
- # useful for customizations that you want to apply to all connections.
92
- # :connect_sqls :: An array of sql strings to execute on each new connection, after :after_connect runs.
93
- def initialize(db, opts=OPTS)
100
+ # Instantiates a connection pool with the given Database and options.
101
+ def initialize(db, opts=OPTS) # SEQUEL6: Remove second argument, always use db.opts
94
102
  @db = db
95
- @after_connect = opts[:after_connect]
96
- @connect_sqls = opts[:connect_sqls]
103
+ @use_old_connect_api = false # SEQUEL6: Remove
104
+ @after_connect = opts[:after_connect] # SEQUEL6: Remove
105
+ @connect_sqls = opts[:connect_sqls] # SEQUEL6: Remove
97
106
  @error_classes = db.send(:database_error_classes).dup.freeze
98
107
  end
99
108
 
@@ -119,25 +128,30 @@ class Sequel::ConnectionPool
119
128
  # and checking for connection errors.
120
129
  def make_new(server)
121
130
  begin
122
- conn = @db.connect(server)
131
+ if @use_old_connect_api
132
+ # SEQUEL6: Remove block
133
+ conn = @db.connect(server)
123
134
 
124
- if ac = @after_connect
125
- if ac.arity == 2
126
- ac.call(conn, server)
127
- else
128
- ac.call(conn)
135
+ if ac = @after_connect
136
+ if ac.arity == 2
137
+ ac.call(conn, server)
138
+ else
139
+ ac.call(conn)
140
+ end
129
141
  end
130
- end
131
-
132
- if cs = @connect_sqls
133
- cs.each do |sql|
134
- db.send(:log_connection_execute, conn, sql)
142
+
143
+ if cs = @connect_sqls
144
+ cs.each do |sql|
145
+ db.send(:log_connection_execute, conn, sql)
146
+ end
135
147
  end
148
+
149
+ conn
150
+ else
151
+ @db.new_connection(server)
136
152
  end
137
153
  rescue Exception=>exception
138
154
  raise Sequel.convert_exception_class(exception, Sequel::DatabaseConnectionError)
139
- end
140
- raise(Sequel::DatabaseConnectionError, "Connection parameters not valid") unless conn
141
- conn
155
+ end || raise(Sequel::DatabaseConnectionError, "Connection parameters not valid")
142
156
  end
143
157
  end
data/lib/sequel/core.rb CHANGED
@@ -176,6 +176,17 @@ module Sequel
176
176
  JSON.parse(json, :create_additions=>false)
177
177
  end
178
178
 
179
+ # If a mutex is given, synchronize access using it. If nil is given, just
180
+ # yield to the block. This is designed for cases where a mutex may or may
181
+ # not be provided.
182
+ def synchronize_with(mutex)
183
+ if mutex
184
+ mutex.synchronize{yield}
185
+ else
186
+ yield
187
+ end
188
+ end
189
+
179
190
  # Convert each item in the array to the correct type, handling multi-dimensional
180
191
  # arrays. For each element in the array or subarrays, call the converter,
181
192
  # unless the value is nil.
@@ -267,11 +278,9 @@ module Sequel
267
278
  #
268
279
  # Sequel.string_to_date('2010-09-10') # Date.civil(2010, 09, 10)
269
280
  def string_to_date(string)
270
- begin
271
- Date.parse(string, Sequel.convert_two_digit_years)
272
- rescue => e
273
- raise convert_exception_class(e, InvalidValue)
274
- end
281
+ Date.parse(string, Sequel.convert_two_digit_years)
282
+ rescue => e
283
+ raise convert_exception_class(e, InvalidValue)
275
284
  end
276
285
 
277
286
  # Converts the given +string+ into a +Time+ or +DateTime+ object, depending on the
@@ -279,15 +288,13 @@ module Sequel
279
288
  #
280
289
  # Sequel.string_to_datetime('2010-09-10 10:20:30') # Time.local(2010, 09, 10, 10, 20, 30)
281
290
  def string_to_datetime(string)
282
- begin
283
- if datetime_class == DateTime
284
- DateTime.parse(string, convert_two_digit_years)
285
- else
286
- datetime_class.parse(string)
287
- end
288
- rescue => e
289
- raise convert_exception_class(e, InvalidValue)
291
+ if datetime_class == DateTime
292
+ DateTime.parse(string, convert_two_digit_years)
293
+ else
294
+ datetime_class.parse(string)
290
295
  end
296
+ rescue => e
297
+ raise convert_exception_class(e, InvalidValue)
291
298
  end
292
299
 
293
300
  # Converts the given +string+ into a <tt>Sequel::SQLTime</tt> object.
@@ -295,11 +302,9 @@ module Sequel
295
302
  # v = Sequel.string_to_time('10:20:30') # Sequel::SQLTime.parse('10:20:30')
296
303
  # DB.literal(v) # => '10:20:30'
297
304
  def string_to_time(string)
298
- begin
299
- SQLTime.parse(string)
300
- rescue => e
301
- raise convert_exception_class(e, InvalidValue)
302
- end
305
+ SQLTime.parse(string)
306
+ rescue => e
307
+ raise convert_exception_class(e, InvalidValue)
303
308
  end
304
309
 
305
310
  # Unless in single threaded mode, protects access to any mutable
@@ -389,6 +394,11 @@ module Sequel
389
394
 
390
395
  private
391
396
 
397
+ # Return a hash of date information parsed from the given string.
398
+ def _date_parse(string)
399
+ Date._parse(string)
400
+ end
401
+
392
402
  # Helper method that the database adapter class methods that are added to Sequel via
393
403
  # metaprogramming use to parse arguments.
394
404
  def adapter_method(adapter, *args, &block)