sequel 5.80.0 → 5.92.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 (205) hide show
  1. checksums.yaml +4 -4
  2. data/bin/sequel +9 -4
  3. data/lib/sequel/adapters/ado.rb +1 -1
  4. data/lib/sequel/adapters/ibmdb.rb +1 -0
  5. data/lib/sequel/adapters/jdbc/db2.rb +2 -2
  6. data/lib/sequel/adapters/jdbc/derby.rb +3 -3
  7. data/lib/sequel/adapters/jdbc/h2.rb +2 -2
  8. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -2
  9. data/lib/sequel/adapters/jdbc/jtds.rb +2 -2
  10. data/lib/sequel/adapters/jdbc/mysql.rb +1 -1
  11. data/lib/sequel/adapters/jdbc/oracle.rb +5 -5
  12. data/lib/sequel/adapters/jdbc/postgresql.rb +5 -5
  13. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +6 -6
  14. data/lib/sequel/adapters/jdbc/sqlite.rb +2 -2
  15. data/lib/sequel/adapters/jdbc/sqlserver.rb +2 -2
  16. data/lib/sequel/adapters/jdbc.rb +8 -8
  17. data/lib/sequel/adapters/mysql2.rb +8 -1
  18. data/lib/sequel/adapters/shared/access.rb +1 -0
  19. data/lib/sequel/adapters/shared/db2.rb +1 -1
  20. data/lib/sequel/adapters/shared/mssql.rb +18 -5
  21. data/lib/sequel/adapters/shared/mysql.rb +8 -4
  22. data/lib/sequel/adapters/shared/oracle.rb +1 -0
  23. data/lib/sequel/adapters/shared/postgres.rb +106 -13
  24. data/lib/sequel/adapters/shared/sqlite.rb +4 -2
  25. data/lib/sequel/adapters/sqlite.rb +4 -0
  26. data/lib/sequel/adapters/trilogy.rb +1 -2
  27. data/lib/sequel/connection_pool/sharded_threaded.rb +26 -10
  28. data/lib/sequel/connection_pool/threaded.rb +26 -10
  29. data/lib/sequel/connection_pool.rb +2 -2
  30. data/lib/sequel/core.rb +15 -0
  31. data/lib/sequel/database/connecting.rb +20 -26
  32. data/lib/sequel/database/dataset_defaults.rb +3 -3
  33. data/lib/sequel/database/misc.rb +46 -10
  34. data/lib/sequel/database/query.rb +11 -11
  35. data/lib/sequel/database/schema_generator.rb +8 -0
  36. data/lib/sequel/database/schema_methods.rb +17 -1
  37. data/lib/sequel/dataset/actions.rb +9 -1
  38. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +1 -1
  39. data/lib/sequel/dataset/prepared_statements.rb +2 -1
  40. data/lib/sequel/dataset/query.rb +9 -5
  41. data/lib/sequel/dataset/sql.rb +25 -5
  42. data/lib/sequel/extensions/caller_logging.rb +2 -0
  43. data/lib/sequel/extensions/connection_validator.rb +15 -10
  44. data/lib/sequel/extensions/dataset_run.rb +41 -0
  45. data/lib/sequel/extensions/migration.rb +23 -3
  46. data/lib/sequel/extensions/null_dataset.rb +2 -2
  47. data/lib/sequel/extensions/pg_auto_parameterize.rb +1 -1
  48. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +93 -10
  49. data/lib/sequel/extensions/pg_enum.rb +3 -3
  50. data/lib/sequel/extensions/pg_json_ops.rb +642 -9
  51. data/lib/sequel/extensions/pg_row.rb +3 -1
  52. data/lib/sequel/extensions/pg_schema_caching.rb +90 -0
  53. data/lib/sequel/extensions/provenance.rb +2 -0
  54. data/lib/sequel/extensions/query_blocker.rb +172 -0
  55. data/lib/sequel/extensions/schema_caching.rb +24 -9
  56. data/lib/sequel/extensions/schema_dumper.rb +16 -4
  57. data/lib/sequel/extensions/sqlite_json_ops.rb +1 -1
  58. data/lib/sequel/extensions/stdio_logger.rb +48 -0
  59. data/lib/sequel/extensions/string_agg.rb +17 -4
  60. data/lib/sequel/extensions/temporarily_release_connection.rb +178 -0
  61. data/lib/sequel/extensions/virtual_row_method_block.rb +1 -0
  62. data/lib/sequel/model/associations.rb +28 -3
  63. data/lib/sequel/model/base.rb +67 -18
  64. data/lib/sequel/plugins/association_pks.rb +1 -1
  65. data/lib/sequel/plugins/column_encryption.rb +1 -1
  66. data/lib/sequel/plugins/composition.rb +1 -1
  67. data/lib/sequel/plugins/defaults_setter.rb +16 -4
  68. data/lib/sequel/plugins/enum.rb +1 -1
  69. data/lib/sequel/plugins/forbid_lazy_load.rb +14 -1
  70. data/lib/sequel/plugins/input_transformer.rb +1 -1
  71. data/lib/sequel/plugins/inspect_pk.rb +44 -0
  72. data/lib/sequel/plugins/instance_filters.rb +4 -1
  73. data/lib/sequel/plugins/inverted_subsets.rb +1 -0
  74. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  75. data/lib/sequel/plugins/nested_attributes.rb +10 -5
  76. data/lib/sequel/plugins/optimistic_locking.rb +2 -0
  77. data/lib/sequel/plugins/paged_operations.rb +5 -2
  78. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +6 -1
  79. data/lib/sequel/plugins/pg_auto_validate_enums.rb +88 -0
  80. data/lib/sequel/plugins/pg_eager_any_typed_array.rb +95 -0
  81. data/lib/sequel/plugins/rcte_tree.rb +1 -1
  82. data/lib/sequel/plugins/serialization.rb +11 -5
  83. data/lib/sequel/plugins/sql_comments.rb +7 -2
  84. data/lib/sequel/plugins/static_cache_cache.rb +50 -13
  85. data/lib/sequel/plugins/subset_conditions.rb +85 -5
  86. data/lib/sequel/plugins/subset_static_cache.rb +263 -0
  87. data/lib/sequel/plugins/tactical_eager_loading.rb +6 -2
  88. data/lib/sequel/plugins/validate_associated.rb +1 -1
  89. data/lib/sequel/sql.rb +16 -6
  90. data/lib/sequel/version.rb +1 -1
  91. metadata +12 -234
  92. data/CHANGELOG +0 -1355
  93. data/README.rdoc +0 -936
  94. data/doc/advanced_associations.rdoc +0 -884
  95. data/doc/association_basics.rdoc +0 -1859
  96. data/doc/bin_sequel.rdoc +0 -146
  97. data/doc/cheat_sheet.rdoc +0 -255
  98. data/doc/code_order.rdoc +0 -102
  99. data/doc/core_extensions.rdoc +0 -405
  100. data/doc/dataset_basics.rdoc +0 -96
  101. data/doc/dataset_filtering.rdoc +0 -222
  102. data/doc/extensions.rdoc +0 -77
  103. data/doc/fork_safety.rdoc +0 -84
  104. data/doc/mass_assignment.rdoc +0 -98
  105. data/doc/migration.rdoc +0 -660
  106. data/doc/model_dataset_method_design.rdoc +0 -129
  107. data/doc/model_hooks.rdoc +0 -254
  108. data/doc/model_plugins.rdoc +0 -270
  109. data/doc/mssql_stored_procedures.rdoc +0 -43
  110. data/doc/object_model.rdoc +0 -563
  111. data/doc/opening_databases.rdoc +0 -436
  112. data/doc/postgresql.rdoc +0 -611
  113. data/doc/prepared_statements.rdoc +0 -144
  114. data/doc/querying.rdoc +0 -1070
  115. data/doc/reflection.rdoc +0 -120
  116. data/doc/release_notes/5.0.0.txt +0 -159
  117. data/doc/release_notes/5.1.0.txt +0 -31
  118. data/doc/release_notes/5.10.0.txt +0 -84
  119. data/doc/release_notes/5.11.0.txt +0 -83
  120. data/doc/release_notes/5.12.0.txt +0 -141
  121. data/doc/release_notes/5.13.0.txt +0 -27
  122. data/doc/release_notes/5.14.0.txt +0 -63
  123. data/doc/release_notes/5.15.0.txt +0 -39
  124. data/doc/release_notes/5.16.0.txt +0 -110
  125. data/doc/release_notes/5.17.0.txt +0 -31
  126. data/doc/release_notes/5.18.0.txt +0 -69
  127. data/doc/release_notes/5.19.0.txt +0 -28
  128. data/doc/release_notes/5.2.0.txt +0 -33
  129. data/doc/release_notes/5.20.0.txt +0 -89
  130. data/doc/release_notes/5.21.0.txt +0 -87
  131. data/doc/release_notes/5.22.0.txt +0 -48
  132. data/doc/release_notes/5.23.0.txt +0 -56
  133. data/doc/release_notes/5.24.0.txt +0 -56
  134. data/doc/release_notes/5.25.0.txt +0 -32
  135. data/doc/release_notes/5.26.0.txt +0 -35
  136. data/doc/release_notes/5.27.0.txt +0 -21
  137. data/doc/release_notes/5.28.0.txt +0 -16
  138. data/doc/release_notes/5.29.0.txt +0 -22
  139. data/doc/release_notes/5.3.0.txt +0 -121
  140. data/doc/release_notes/5.30.0.txt +0 -20
  141. data/doc/release_notes/5.31.0.txt +0 -148
  142. data/doc/release_notes/5.32.0.txt +0 -46
  143. data/doc/release_notes/5.33.0.txt +0 -24
  144. data/doc/release_notes/5.34.0.txt +0 -40
  145. data/doc/release_notes/5.35.0.txt +0 -56
  146. data/doc/release_notes/5.36.0.txt +0 -60
  147. data/doc/release_notes/5.37.0.txt +0 -30
  148. data/doc/release_notes/5.38.0.txt +0 -28
  149. data/doc/release_notes/5.39.0.txt +0 -19
  150. data/doc/release_notes/5.4.0.txt +0 -80
  151. data/doc/release_notes/5.40.0.txt +0 -40
  152. data/doc/release_notes/5.41.0.txt +0 -25
  153. data/doc/release_notes/5.42.0.txt +0 -136
  154. data/doc/release_notes/5.43.0.txt +0 -98
  155. data/doc/release_notes/5.44.0.txt +0 -32
  156. data/doc/release_notes/5.45.0.txt +0 -34
  157. data/doc/release_notes/5.46.0.txt +0 -87
  158. data/doc/release_notes/5.47.0.txt +0 -59
  159. data/doc/release_notes/5.48.0.txt +0 -14
  160. data/doc/release_notes/5.49.0.txt +0 -59
  161. data/doc/release_notes/5.5.0.txt +0 -61
  162. data/doc/release_notes/5.50.0.txt +0 -78
  163. data/doc/release_notes/5.51.0.txt +0 -47
  164. data/doc/release_notes/5.52.0.txt +0 -87
  165. data/doc/release_notes/5.53.0.txt +0 -23
  166. data/doc/release_notes/5.54.0.txt +0 -27
  167. data/doc/release_notes/5.55.0.txt +0 -21
  168. data/doc/release_notes/5.56.0.txt +0 -51
  169. data/doc/release_notes/5.57.0.txt +0 -23
  170. data/doc/release_notes/5.58.0.txt +0 -31
  171. data/doc/release_notes/5.59.0.txt +0 -73
  172. data/doc/release_notes/5.6.0.txt +0 -31
  173. data/doc/release_notes/5.60.0.txt +0 -22
  174. data/doc/release_notes/5.61.0.txt +0 -43
  175. data/doc/release_notes/5.62.0.txt +0 -132
  176. data/doc/release_notes/5.63.0.txt +0 -33
  177. data/doc/release_notes/5.64.0.txt +0 -50
  178. data/doc/release_notes/5.65.0.txt +0 -21
  179. data/doc/release_notes/5.66.0.txt +0 -24
  180. data/doc/release_notes/5.67.0.txt +0 -32
  181. data/doc/release_notes/5.68.0.txt +0 -61
  182. data/doc/release_notes/5.69.0.txt +0 -26
  183. data/doc/release_notes/5.7.0.txt +0 -108
  184. data/doc/release_notes/5.70.0.txt +0 -35
  185. data/doc/release_notes/5.71.0.txt +0 -21
  186. data/doc/release_notes/5.72.0.txt +0 -33
  187. data/doc/release_notes/5.73.0.txt +0 -66
  188. data/doc/release_notes/5.74.0.txt +0 -45
  189. data/doc/release_notes/5.75.0.txt +0 -35
  190. data/doc/release_notes/5.76.0.txt +0 -86
  191. data/doc/release_notes/5.77.0.txt +0 -63
  192. data/doc/release_notes/5.78.0.txt +0 -67
  193. data/doc/release_notes/5.79.0.txt +0 -28
  194. data/doc/release_notes/5.8.0.txt +0 -170
  195. data/doc/release_notes/5.80.0.txt +0 -40
  196. data/doc/release_notes/5.9.0.txt +0 -99
  197. data/doc/schema_modification.rdoc +0 -679
  198. data/doc/security.rdoc +0 -443
  199. data/doc/sharding.rdoc +0 -286
  200. data/doc/sql.rdoc +0 -648
  201. data/doc/testing.rdoc +0 -190
  202. data/doc/thread_safety.rdoc +0 -15
  203. data/doc/transactions.rdoc +0 -250
  204. data/doc/validations.rdoc +0 -558
  205. data/doc/virtual_rows.rdoc +0 -265
@@ -430,6 +430,10 @@ module Sequel
430
430
  # add_unique_constraint(:name, name: :unique_name) # ADD CONSTRAINT unique_name UNIQUE (name)
431
431
  #
432
432
  # Supports the same :deferrable option as CreateTableGenerator#column.
433
+ #
434
+ # PostgreSQL specific options:
435
+ #
436
+ # :using_index :: Use the USING INDEX clause to specify an existing unique index
433
437
  def add_unique_constraint(columns, opts = OPTS)
434
438
  @operations << {:op => :add_constraint, :type => :unique, :columns => Array(columns)}.merge!(opts)
435
439
  nil
@@ -483,6 +487,10 @@ module Sequel
483
487
  #
484
488
  # add_primary_key(:id) # ADD COLUMN id serial PRIMARY KEY
485
489
  # add_primary_key([:artist_id, :name]) # ADD PRIMARY KEY (artist_id, name)
490
+ #
491
+ # PostgreSQL specific options:
492
+ #
493
+ # :using_index :: Use the USING INDEX clause to specify an existing unique index
486
494
  def add_primary_key(name, opts = OPTS)
487
495
  return add_composite_primary_key(name, opts) if name.is_a?(Array)
488
496
  opts = @db.serial_primary_key_options.merge(opts)
@@ -191,6 +191,8 @@ module Sequel
191
191
  # The +any+ type is treated like a SQLite column in a non-strict table,
192
192
  # allowing any type of data to be stored. This option is supported on
193
193
  # SQLite 3.37.0+.
194
+ # :using :: Create a VIRTUAL table with the given USING clause. The value should be
195
+ # a string, as it is used directly in the SQL query.
194
196
  # :without_rowid :: Create a WITHOUT ROWID table. Every row in SQLite has a special
195
197
  # 'rowid' column, that uniquely identifies that row within the table.
196
198
  # If this option is used, the 'rowid' column is omitted, which can
@@ -775,7 +777,21 @@ module Sequel
775
777
 
776
778
  # SQL fragment for initial part of CREATE TABLE statement
777
779
  def create_table_prefix_sql(name, options)
778
- "CREATE #{temporary_table_sql if options[:temp]}TABLE#{' IF NOT EXISTS' if options[:if_not_exists]} #{options[:temp] ? quote_identifier(name) : quote_schema_table(name)}"
780
+ "CREATE #{temporary_table_sql if options[:temp]}TABLE#{' IF NOT EXISTS' if options[:if_not_exists]} #{create_table_table_name_sql(name, options)}"
781
+ end
782
+
783
+ # The SQL to use for a table name when creating a table.
784
+ # Use of the :temp option can result in different SQL,
785
+ # because the rules for temp table naming can differ
786
+ # between databases, and temp tables should not use the
787
+ # default_schema.
788
+ def create_table_table_name_sql(name, options)
789
+ options[:temp] ? create_table_temp_table_name_sql(name, options) : quote_schema_table(name)
790
+ end
791
+
792
+ # The SQL to use for the table name for a temporary table.
793
+ def create_table_temp_table_name_sql(name, _options)
794
+ name.is_a?(String) ? quote_identifier(name) : literal(name)
779
795
  end
780
796
 
781
797
  # SQL fragment for initial part of CREATE VIEW statement
@@ -217,7 +217,7 @@ module Sequel
217
217
  case args.length
218
218
  when 0
219
219
  unless block
220
- return single_record
220
+ return(@opts[:sql] ? single_record! : single_record)
221
221
  end
222
222
  when 1
223
223
  arg = args[0]
@@ -282,6 +282,12 @@ module Sequel
282
282
  #
283
283
  # DB[:table].get{[sum(id).as(sum), name]} # SELECT sum(id) AS sum, name FROM table LIMIT 1
284
284
  # # => [6, 'foo']
285
+ #
286
+ # If called on a dataset with raw SQL, returns the
287
+ # first value in the dataset without changing the selection or setting a limit:
288
+ #
289
+ # DB["SELECT id FROM table"].get # SELECT id FROM table
290
+ # # => 3
285
291
  def get(column=(no_arg=true; nil), &block)
286
292
  ds = naked
287
293
  if block
@@ -289,6 +295,8 @@ module Sequel
289
295
  ds = ds.select(&block)
290
296
  column = ds.opts[:select]
291
297
  column = nil if column.is_a?(Array) && column.length < 2
298
+ elsif no_arg && opts[:sql]
299
+ return ds.single_value!
292
300
  else
293
301
  case column
294
302
  when Array
@@ -19,7 +19,7 @@ module Sequel
19
19
  def with_extend(*mods, &block)
20
20
  c = _clone(:freeze=>false)
21
21
  c.extend(*mods) unless mods.empty?
22
- c.extend(DatasetModule.new(&block)) if block
22
+ c.extend(Sequel.set_temp_name(DatasetModule.new(&block)){"Sequel::Dataset::_DatasetModule(#{block.source_location.join(':')})"}) if block
23
23
  c.freeze
24
24
  end
25
25
 
@@ -20,7 +20,8 @@ module Sequel
20
20
  def self.prepared_statements_module(code, mods, meths=DEFAULT_PREPARED_STATEMENT_MODULE_METHODS, &block)
21
21
  code = PREPARED_STATEMENT_MODULE_CODE[code] || code
22
22
 
23
- Module.new do
23
+ Module.new do
24
+ Sequel.set_temp_name(self){"Sequel::Dataset::_PreparedStatementsModule(#{block.source_location.join(':') if block})"}
24
25
  Array(mods).each do |mod|
25
26
  include mod
26
27
  end
@@ -204,7 +204,7 @@ module Sequel
204
204
  # If no related extension file exists or the extension does not have
205
205
  # specific support for Dataset objects, an error will be raised.
206
206
  def extension(*exts)
207
- Sequel.extension(*exts)
207
+ exts.each{|ext| Sequel.extension(ext) unless Sequel.synchronize{EXTENSIONS[ext]}}
208
208
  mods = exts.map{|ext| Sequel.synchronize{EXTENSION_MODULES[ext]}}
209
209
  if mods.all?
210
210
  with_extend(*mods)
@@ -1238,9 +1238,9 @@ module Sequel
1238
1238
  # Note that like Object#extend, when multiple modules are provided
1239
1239
  # as arguments the subclass includes the modules in reverse order.
1240
1240
  def with_extend(*mods, &block)
1241
- c = Class.new(self.class)
1241
+ c = Sequel.set_temp_name(Class.new(self.class)){"Sequel::Dataset::_Subclass"}
1242
1242
  c.include(*mods) unless mods.empty?
1243
- c.include(DatasetModule.new(&block)) if block
1243
+ c.include(Sequel.set_temp_name(DatasetModule.new(&block)){"Sequel::Dataset::_DatasetModule(#{block.source_location[0,2].join(':')})"}) if block
1244
1244
  o = c.freeze.allocate
1245
1245
  o.instance_variable_set(:@db, @db)
1246
1246
  o.instance_variable_set(:@opts, @opts)
@@ -1359,9 +1359,13 @@ module Sequel
1359
1359
  unless TRUE_FREEZE
1360
1360
  # Load the extensions into the receiver, without checking if the receiver is frozen.
1361
1361
  def _extension!(exts)
1362
- Sequel.extension(*exts)
1363
1362
  exts.each do |ext|
1364
- if pr = Sequel.synchronize{EXTENSIONS[ext]}
1363
+ unless pr = Sequel.synchronize{EXTENSIONS[ext]}
1364
+ Sequel.extension(ext)
1365
+ pr = Sequel.synchronize{EXTENSIONS[ext]}
1366
+ end
1367
+
1368
+ if pr
1365
1369
  pr.call(self)
1366
1370
  else
1367
1371
  raise(Error, "Extension #{ext} does not have specific support handling individual datasets (try: Sequel.extension #{ext.inspect})")
@@ -901,18 +901,31 @@ module Sequel
901
901
  MERGE_TYPE_SQL = {
902
902
  :insert => ' WHEN NOT MATCHED',
903
903
  :delete => ' WHEN MATCHED',
904
+ :delete_not_matched_by_source => ' WHEN NOT MATCHED BY SOURCE',
904
905
  :update => ' WHEN MATCHED',
906
+ :update_not_matched_by_source => ' WHEN NOT MATCHED BY SOURCE',
905
907
  :matched => ' WHEN MATCHED',
906
908
  :not_matched => ' WHEN NOT MATCHED',
909
+ :not_matched_by_source => ' WHEN NOT MATCHED BY SOURCE',
907
910
  }.freeze
908
911
  private_constant :MERGE_TYPE_SQL
909
912
 
913
+ MERGE_NORMALIZE_TYPE_MAP = {
914
+ :delete_not_matched_by_source => :delete,
915
+ :update_not_matched_by_source => :update,
916
+ :matched => :do_nothing,
917
+ :not_matched => :do_nothing,
918
+ :not_matched_by_source => :do_nothing,
919
+ }.freeze
920
+ private_constant :MERGE_NORMALIZE_TYPE_MAP
921
+
910
922
  # Add the WHEN clauses to the MERGE SQL
911
923
  def _merge_when_sql(sql)
912
924
  raise Error, "no WHEN [NOT] MATCHED clauses provided for MERGE" unless merge_when = @opts[:merge_when]
913
925
  merge_when.each do |data|
914
926
  type = data[:type]
915
927
  sql << MERGE_TYPE_SQL[type]
928
+ type = MERGE_NORMALIZE_TYPE_MAP[type] || type
916
929
  _merge_when_conditions_sql(sql, data)
917
930
  send(:"_merge_#{type}_sql", sql, data)
918
931
  end
@@ -1019,7 +1032,7 @@ module Sequel
1019
1032
  if column_aliases
1020
1033
  raise Error, "#{db.database_type} does not support derived column lists" unless supports_derived_column_lists?
1021
1034
  sql << '('
1022
- identifier_list_append(sql, column_aliases)
1035
+ derived_column_list_sql_append(sql, column_aliases)
1023
1036
  sql << ')'
1024
1037
  end
1025
1038
  end
@@ -1152,6 +1165,11 @@ module Sequel
1152
1165
  end
1153
1166
  end
1154
1167
 
1168
+ # Append the column aliases to the SQL.
1169
+ def derived_column_list_sql_append(sql, column_aliases)
1170
+ identifier_list_append(sql, column_aliases)
1171
+ end
1172
+
1155
1173
  # Disable caching of SQL for the current dataset
1156
1174
  def disable_sql_caching!
1157
1175
  cache_set(:_no_cache_sql, true)
@@ -1412,10 +1430,6 @@ module Sequel
1412
1430
  # calls +sql_literal+ if object responds to it, otherwise raises an error.
1413
1431
  # If a database specific type is allowed, this should be overriden in a subclass.
1414
1432
  def literal_other_append(sql, v)
1415
- # We can't be sure if v will always literalize to the same SQL, so
1416
- # don't cache SQL for a dataset that uses this.
1417
- disable_sql_caching!
1418
-
1419
1433
  if v.respond_to?(:sql_literal_append)
1420
1434
  v.sql_literal_append(self, sql)
1421
1435
  elsif v.respond_to?(:sql_literal)
@@ -1423,6 +1437,12 @@ module Sequel
1423
1437
  else
1424
1438
  raise Error, "can't express #{v.inspect} as a SQL literal"
1425
1439
  end
1440
+
1441
+ if !v.respond_to?(:sql_literal_allow_caching?) || !v.sql_literal_allow_caching?(self)
1442
+ # We can't be sure if v will always literalize to the same SQL, so
1443
+ # don't cache SQL for a dataset that uses this.
1444
+ disable_sql_caching!
1445
+ end
1426
1446
  end
1427
1447
 
1428
1448
  # SQL fragment for Sequel::SQLTime, containing just the time part
@@ -37,6 +37,7 @@ module Sequel
37
37
  module CallerLogging
38
38
  SEQUEL_LIB_PATH = (File.expand_path('../../..', __FILE__) + '/').freeze
39
39
  RUBY_STDLIB = RbConfig::CONFIG["rubylibdir"]
40
+ INTERNAL = '<internal'
40
41
 
41
42
  # A regexp of caller lines to ignore, in addition to internal Sequel and Ruby code.
42
43
  attr_accessor :caller_logging_ignore
@@ -61,6 +62,7 @@ module Sequel
61
62
  c = caller.find do |line|
62
63
  !(line.start_with?(SEQUEL_LIB_PATH) ||
63
64
  line.start_with?(RUBY_STDLIB) ||
65
+ line.start_with?(INTERNAL) ||
64
66
  (ignore && line =~ ignore))
65
67
  end
66
68
 
@@ -105,18 +105,23 @@ module Sequel
105
105
  1.times do
106
106
  if (conn = super) &&
107
107
  (timer = sync{@connection_timestamps.delete(conn)}) &&
108
- Sequel.elapsed_seconds_since(timer) > @connection_validation_timeout &&
109
- !db.valid_connection?(conn)
108
+ Sequel.elapsed_seconds_since(timer) > @connection_validation_timeout
110
109
 
111
- case pool_type
112
- when :sharded_threaded, :sharded_timed_queue
113
- sync{@allocated[a.last].delete(Sequel.current)}
114
- else
115
- sync{@allocated.delete(Sequel.current)}
116
- end
110
+ begin
111
+ valid = db.valid_connection?(conn)
112
+ ensure
113
+ unless valid
114
+ case pool_type
115
+ when :sharded_threaded, :sharded_timed_queue
116
+ sync{@allocated[a.last].delete(Sequel.current)}
117
+ else
118
+ sync{@allocated.delete(Sequel.current)}
119
+ end
117
120
 
118
- disconnect_connection(conn)
119
- redo
121
+ disconnect_connection(conn)
122
+ redo if valid == false
123
+ end
124
+ end
120
125
  end
121
126
  end
122
127
 
@@ -0,0 +1,41 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # The dataset_run extension is designed for cases where you want
4
+ # to use dataset methods to build a query, but want to run that
5
+ # query without returning a result. The most common need would
6
+ # be to easily use placeholders in an SQL string, which Database#run
7
+ # does not support directly.
8
+ #
9
+ # You can load this extension into specific datasets:
10
+ #
11
+ # ds = DB["GRANT SELECT ON ? TO ?", :table, :user]
12
+ # ds = ds.extension(:dataset_run)
13
+ # ds.run
14
+ #
15
+ # Or you can load it into all of a database's datasets, which
16
+ # is probably the desired behavior if you are using this extension:
17
+ #
18
+ # DB.extension(:dataset_run)
19
+ # DB["GRANT SELECT ON ? TO ?", :table, :user].run
20
+ #
21
+ # Related module: Sequel::DatasetRun
22
+
23
+ #
24
+ module Sequel
25
+ module DatasetRun
26
+ # Run the dataset's SQL on the database. Returns NULL. This is
27
+ # useful when you want to run SQL without returning a result.
28
+ #
29
+ # DB["GRANT SELECT ON ? TO ?", :table, :user].run
30
+ # # GRANT SELECT ON "table" TO "user"
31
+ def run
32
+ if server = @opts[:server]
33
+ db.run(sql, :server=>server)
34
+ else
35
+ db.run(sql)
36
+ end
37
+ end
38
+ end
39
+
40
+ Dataset.register_extension(:dataset_run, DatasetRun)
41
+ end
@@ -223,7 +223,7 @@ module Sequel
223
223
  @actions << [:drop_join_table, *args]
224
224
  end
225
225
 
226
- def create_table(name, opts=OPTS)
226
+ def create_table(name, opts=OPTS, &_)
227
227
  @actions << [:drop_table, name, opts]
228
228
  end
229
229
 
@@ -287,6 +287,10 @@ module Sequel
287
287
  def set_column_allow_null(name, allow_null=true)
288
288
  @actions << [:set_column_allow_null, name, !allow_null]
289
289
  end
290
+
291
+ def set_column_not_null(name)
292
+ @actions << [:set_column_allow_null, name]
293
+ end
290
294
  end
291
295
 
292
296
  # The preferred method for writing Sequel migrations, using a DSL:
@@ -371,7 +375,7 @@ module Sequel
371
375
  #
372
376
  # Part of the +migration+ extension.
373
377
  class Migrator
374
- MIGRATION_FILE_PATTERN = /\A(\d+)_.+\.rb\z/i.freeze
378
+ MIGRATION_FILE_PATTERN = /\A(\d+)_(.+)\.rb\z/i.freeze
375
379
 
376
380
  # Mutex used around migration file loading
377
381
  MUTEX = Mutex.new
@@ -791,7 +795,23 @@ module Sequel
791
795
  next unless MIGRATION_FILE_PATTERN.match(file)
792
796
  files << File.join(directory, file)
793
797
  end
794
- files.sort_by{|f| MIGRATION_FILE_PATTERN.match(File.basename(f))[1].to_i}
798
+ files.sort! do |a, b|
799
+ a_ver, a_name = split_migration_filename(a)
800
+ b_ver, b_name = split_migration_filename(b)
801
+ x = a_ver <=> b_ver
802
+ if x.zero?
803
+ x = a_name <=> b_name
804
+ end
805
+ x
806
+ end
807
+ files
808
+ end
809
+
810
+ # Return an integer and name (without extension) for the given path.
811
+ def split_migration_filename(path)
812
+ version, name = MIGRATION_FILE_PATTERN.match(File.basename(path)).captures
813
+ version = version.to_i
814
+ [version, name]
795
815
  end
796
816
 
797
817
  # Returns tuples of migration, filename, and direction
@@ -63,12 +63,12 @@ module Sequel
63
63
  end
64
64
 
65
65
  # Return self without sending a database query, never yielding.
66
- def each
66
+ def each(&_)
67
67
  self
68
68
  end
69
69
 
70
70
  # Return nil without sending a database query, never yielding.
71
- def fetch_rows(sql)
71
+ def fetch_rows(sql, &_)
72
72
  nil
73
73
  end
74
74
 
@@ -394,7 +394,7 @@ module Sequel
394
394
  # there can be more than one parameter per column, so this doesn't prevent going
395
395
  # over the limit, though it does make it less likely.
396
396
  def default_import_slice
397
- 40
397
+ @opts[:no_auto_parameterize] ? super : 40
398
398
  end
399
399
 
400
400
  # Handle parameterization of multi_insert_sql
@@ -21,15 +21,26 @@
21
21
  # DateTime :: timestamp (or timestamptz if pg_timestamptz extension is used)
22
22
  # Sequel::SQLTime :: time
23
23
  # Sequel::SQL::Blob :: bytea
24
+ #
25
+ # Arrays of string values are not automatically converted by default, because the Ruby
26
+ # String class can represent a number of different database types. To convert
27
+ # arrays of Ruby strings to an untyped array (a query parameter with no explicit
28
+ # type cast), set the +:treat_string_list_as_untyped_array+ Database option
29
+ # before loading the extension.
24
30
  #
25
- # String values are also supported using the +text+ type, but only if the
26
- # +:treat_string_list_as_text_array+ Database option is used. This is because
27
- # treating strings as text can break programs, since the type for
28
- # literal strings in PostgreSQL is +unknown+, not +text+.
31
+ # If you will only be using arrays of Ruby strings that represent the +text+ type,
32
+ # you can use the +:treat_string_list_as_text_array+ Database option is used. This
33
+ # can break programs, since the type for literal strings in PostgreSQL is +unknown+,
34
+ # not +text+.
29
35
  #
30
- # The conversion is only done for single dimensional arrays that have more
31
- # than two elements, where all elements are of the same class (other than
32
- # nil values).
36
+ # The conversion is only done for single dimensional arrays that have two or
37
+ # more elements, where all elements are of the same class (other than
38
+ # +nil+ values). You can also do the conversion for arrays of 1 element by setting
39
+ # <tt>pg_auto_parameterize_min_array_size: 1</tt> Database option. This makes
40
+ # finding cases that need special handling easier, but it doesn't match
41
+ # how PostgreSQL internally converts the expression (PostgreSQL converts
42
+ # <tt>IN (single_value)</tt> to <tt>= single_value</tt>, not
43
+ # <tt>= ANY(ARRAY[single_value])</tt>).
33
44
  #
34
45
  # Related module: Sequel::Postgres::AutoParameterizeInArray
35
46
 
@@ -37,6 +48,47 @@ module Sequel
37
48
  module Postgres
38
49
  # Enable automatically parameterizing queries.
39
50
  module AutoParameterizeInArray
51
+ module TreatStringListAsUntypedArray
52
+ # Sentinal value to use as an auto param type to use auto parameterization
53
+ # of a string array without an explicit type cast.
54
+ NO_EXPLICIT_CAST = Object.new.freeze
55
+
56
+ # Wrapper for untyped PGArray values that will be parameterized directly
57
+ # into the query. This should only be used in cases where you know the
58
+ # value should be added as a query parameter.
59
+ class ParameterizedUntypedPGArray < SQL::Wrapper
60
+ def to_s_append(ds, sql)
61
+ sql.add_arg(@value)
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ # Recognize NO_EXPLICIT_CAST sentinal value and use wrapped
68
+ # PGArray that will be parameterized into the query.
69
+ def _convert_array_to_pg_array_with_type(r, type)
70
+ if NO_EXPLICIT_CAST.equal?(type)
71
+ ParameterizedUntypedPGArray.new(Sequel.pg_array(r))
72
+ else
73
+ super
74
+ end
75
+ end
76
+
77
+ # Use a query parameter with no type cast for string arrays.
78
+ def _bound_variable_type_for_string_array(r)
79
+ NO_EXPLICIT_CAST
80
+ end
81
+ end
82
+
83
+ module TreatStringListAsTextArray
84
+ private
85
+
86
+ # Assume all string arrays used on RHS of IN/NOT IN are for type text[]
87
+ def _bound_variable_type_for_string_array(r)
88
+ "text"
89
+ end
90
+ end
91
+
40
92
  # Transform column IN (...) expressions into column = ANY($)
41
93
  # and column NOT IN (...) expressions into column != ALL($)
42
94
  # using an array bound variable for the ANY/ALL argument,
@@ -56,7 +108,7 @@ module Sequel
56
108
  op = :!=
57
109
  func = :ALL
58
110
  end
59
- args = [l, Sequel.function(func, Sequel.pg_array(r, type))]
111
+ args = [l, Sequel.function(func, _convert_array_to_pg_array_with_type(r, type))]
60
112
  end
61
113
  end
62
114
 
@@ -68,7 +120,7 @@ module Sequel
68
120
  # The bound variable type string to use for the bound variable array.
69
121
  # Returns nil if a bound variable should not be used for the array.
70
122
  def _bound_variable_type_for_array(r)
71
- return unless Array === r && r.size > 1
123
+ return unless Array === r && r.size >= pg_auto_parameterize_min_array_size
72
124
  classes = r.map(&:class)
73
125
  classes.uniq!
74
126
  classes.delete(NilClass)
@@ -81,7 +133,7 @@ module Sequel
81
133
  # arrays natively (though the SQL used is different)
82
134
  "int8"
83
135
  elsif klass == String
84
- "text" if db.typecast_value(:boolean, db.opts[:treat_string_list_as_text_array])
136
+ _bound_variable_type_for_string_array(r)
85
137
  elsif klass == BigDecimal
86
138
  "numeric"
87
139
  elsif klass == Date
@@ -100,11 +152,42 @@ module Sequel
100
152
  "bytea"
101
153
  end
102
154
  end
155
+
156
+ # Do not auto parameterize string arrays by default.
157
+ def _bound_variable_type_for_string_array(r)
158
+ nil
159
+ end
160
+
161
+ # The minimium size of array to auto parameterize.
162
+ def pg_auto_parameterize_min_array_size
163
+ 2
164
+ end
165
+
166
+ # Convert RHS of IN/NOT IN operator to PGArray with given type.
167
+ def _convert_array_to_pg_array_with_type(r, type)
168
+ Sequel.pg_array(r, type)
169
+ end
103
170
  end
104
171
  end
105
172
 
106
173
  Database.register_extension(:pg_auto_parameterize_in_array) do |db|
107
174
  db.extension(:pg_array, :pg_auto_parameterize)
108
175
  db.extend_datasets(Postgres::AutoParameterizeInArray)
176
+
177
+ if db.typecast_value(:boolean, db.opts[:treat_string_list_as_text_array])
178
+ db.extend_datasets(Postgres::AutoParameterizeInArray::TreatStringListAsTextArray)
179
+ elsif db.typecast_value(:boolean, db.opts[:treat_string_list_as_untyped_array])
180
+ db.extend_datasets(Postgres::AutoParameterizeInArray::TreatStringListAsUntypedArray)
181
+ end
182
+
183
+ if min_array_size = db.opts[:pg_auto_parameterize_min_array_size]
184
+ min_array_size = db.typecast_value(:integer, min_array_size)
185
+ mod = Module.new do
186
+ define_method(:pg_auto_parameterize_min_array_size){min_array_size}
187
+ private :pg_auto_parameterize_min_array_size
188
+ end
189
+ Sequel.set_temp_name(mod){"Sequel::Postgres::AutoParameterizeInArray::_MinArraySize#{min_array_size}"}
190
+ db.extend_datasets(mod)
191
+ end
109
192
  end
110
193
  end
@@ -149,12 +149,12 @@ module Sequel
149
149
  from(:pg_type).
150
150
  where(:oid=>enum_labels.keys).
151
151
  exclude(:typarray=>0).
152
- select_map([:typname, Sequel.cast(:typarray, Integer).as(:v)])
152
+ select_map([:typname, Sequel.cast(:typarray, Integer).as(:v), Sequel.cast(:oid, Integer).as(:sv)])
153
153
 
154
154
  existing_oids = conversion_procs.keys
155
- array_types.each do |name, oid|
155
+ array_types.each do |name, oid, scalar_oid|
156
156
  next if existing_oids.include?(oid)
157
- register_array_type(name, :oid=>oid)
157
+ register_array_type(name, :oid=>oid, :scalar_oid=>scalar_oid)
158
158
  end
159
159
  end
160
160