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
@@ -1075,6 +1075,7 @@ module Sequel
1075
1075
  pg_attribute[:attname].as(:name),
1076
1076
  SQL::Cast.new(pg_attribute[:atttypid], :integer).as(:oid),
1077
1077
  SQL::Cast.new(basetype[:oid], :integer).as(:base_oid),
1078
+ SQL::Function.new(:col_description, pg_class[:oid], pg_attribute[:attnum]).as(:comment),
1078
1079
  SQL::Function.new(:format_type, basetype[:oid], pg_type[:typtypmod]).as(:db_base_type),
1079
1080
  SQL::Function.new(:format_type, pg_type[:oid], pg_attribute[:atttypmod]).as(:db_type),
1080
1081
  SQL::Function.new(:pg_get_expr, pg_attrdef[:adbin], pg_class[:oid]).as(:default),
@@ -1270,21 +1271,32 @@ module Sequel
1270
1271
 
1271
1272
  # Handle exclusion constraints.
1272
1273
  def constraint_definition_sql(constraint)
1273
- case constraint[:type]
1274
+ case type = constraint[:type]
1274
1275
  when :exclude
1275
1276
  elements = constraint[:elements].map{|c, op| "#{literal(c)} WITH #{op}"}.join(', ')
1276
1277
  sql = String.new
1277
1278
  sql << "#{"CONSTRAINT #{quote_identifier(constraint[:name])} " if constraint[:name]}EXCLUDE USING #{constraint[:using]||'gist'} (#{elements})#{" WHERE #{filter_expr(constraint[:where])}" if constraint[:where]}"
1278
1279
  constraint_deferrable_sql_append(sql, constraint[:deferrable])
1279
1280
  sql
1280
- when :foreign_key, :check
1281
+ when :primary_key, :unique
1282
+ if using_index = constraint[:using_index]
1283
+ sql = String.new
1284
+ sql << "CONSTRAINT #{quote_identifier(constraint[:name])} " if constraint[:name]
1285
+ if type == :primary_key
1286
+ sql << primary_key_constraint_sql_fragment(constraint)
1287
+ else
1288
+ sql << unique_constraint_sql_fragment(constraint)
1289
+ end
1290
+ sql << " USING INDEX " << quote_identifier(using_index)
1291
+ else
1292
+ super
1293
+ end
1294
+ else # when :foreign_key, :check
1281
1295
  sql = super
1282
1296
  if constraint[:not_valid]
1283
1297
  sql << " NOT VALID"
1284
1298
  end
1285
1299
  sql
1286
- else
1287
- super
1288
1300
  end
1289
1301
  end
1290
1302
 
@@ -1429,7 +1441,7 @@ module Sequel
1429
1441
  'UNLOGGED '
1430
1442
  end
1431
1443
 
1432
- "CREATE #{prefix_sql}TABLE#{' IF NOT EXISTS' if options[:if_not_exists]} #{options[:temp] ? quote_identifier(name) : quote_schema_table(name)}"
1444
+ "CREATE #{prefix_sql}TABLE#{' IF NOT EXISTS' if options[:if_not_exists]} #{create_table_table_name_sql(name, options)}"
1433
1445
  end
1434
1446
 
1435
1447
  # SQL for creating a table with PostgreSQL specific options
@@ -1651,9 +1663,9 @@ module Sequel
1651
1663
  # Handle interval and citext types.
1652
1664
  def schema_column_type(db_type)
1653
1665
  case db_type
1654
- when /\Ainterval\z/io
1666
+ when /\Ainterval\z/i
1655
1667
  :interval
1656
- when /\Acitext\z/io
1668
+ when /\Acitext\z/i
1657
1669
  :string
1658
1670
  else
1659
1671
  super
@@ -2058,6 +2070,29 @@ module Sequel
2058
2070
  nil
2059
2071
  end
2060
2072
 
2073
+ # Support MERGE RETURNING on PostgreSQL 17+.
2074
+ def merge(&block)
2075
+ sql = merge_sql
2076
+ if uses_returning?(:merge)
2077
+ returning_fetch_rows(sql, &block)
2078
+ else
2079
+ execute_ddl(sql)
2080
+ end
2081
+ end
2082
+
2083
+ # Return a dataset with a WHEN NOT MATCHED BY SOURCE THEN DELETE clause added to the
2084
+ # MERGE statement. If a block is passed, treat it as a virtual row and
2085
+ # use it as additional conditions for the match.
2086
+ #
2087
+ # merge_delete_not_matched_by_source
2088
+ # # WHEN NOT MATCHED BY SOURCE THEN DELETE
2089
+ #
2090
+ # merge_delete_not_matched_by_source{a > 30}
2091
+ # # WHEN NOT MATCHED BY SOURCE AND (a > 30) THEN DELETE
2092
+ def merge_delete_when_not_matched_by_source(&block)
2093
+ _merge_when(:type=>:delete_not_matched_by_source, &block)
2094
+ end
2095
+
2061
2096
  # Return a dataset with a WHEN MATCHED THEN DO NOTHING clause added to the
2062
2097
  # MERGE statement. If a block is passed, treat it as a virtual row and
2063
2098
  # use it as additional conditions for the match.
@@ -2084,15 +2119,41 @@ module Sequel
2084
2119
  _merge_when(:type=>:not_matched, &block)
2085
2120
  end
2086
2121
 
2122
+ # Return a dataset with a WHEN NOT MATCHED BY SOURCE THEN DO NOTHING clause added to the
2123
+ # MERGE BY SOURCE statement. If a block is passed, treat it as a virtual row and
2124
+ # use it as additional conditions for the match.
2125
+ #
2126
+ # merge_do_nothing_when_not_matched_by_source
2127
+ # # WHEN NOT MATCHED BY SOURCE THEN DO NOTHING
2128
+ #
2129
+ # merge_do_nothing_when_not_matched_by_source{a > 30}
2130
+ # # WHEN NOT MATCHED BY SOURCE AND (a > 30) THEN DO NOTHING
2131
+ def merge_do_nothing_when_not_matched_by_source(&block)
2132
+ _merge_when(:type=>:not_matched_by_source, &block)
2133
+ end
2134
+
2087
2135
  # Support OVERRIDING USER|SYSTEM VALUE for MERGE INSERT.
2088
2136
  def merge_insert(*values, &block)
2089
2137
  h = {:type=>:insert, :values=>values}
2090
- if override = @opts[:override]
2138
+ if @opts[:override]
2091
2139
  h[:override] = insert_override_sql(String.new)
2092
2140
  end
2093
2141
  _merge_when(h, &block)
2094
2142
  end
2095
2143
 
2144
+ # Return a dataset with a WHEN NOT MATCHED BY SOURCE THEN UPDATE clause added to the
2145
+ # MERGE statement. If a block is passed, treat it as a virtual row and
2146
+ # use it as additional conditions for the match.
2147
+ #
2148
+ # merge_update_not_matched_by_source(i1: Sequel[:i1]+:i2+10, a: Sequel[:a]+:b+20)
2149
+ # # WHEN NOT MATCHED BY SOURCE THEN UPDATE SET i1 = (i1 + i2 + 10), a = (a + b + 20)
2150
+ #
2151
+ # merge_update_not_matched_by_source(i1: :i2){a > 30}
2152
+ # # WHEN NOT MATCHED BY SOURCE AND (a > 30) THEN UPDATE SET i1 = i2
2153
+ def merge_update_when_not_matched_by_source(values, &block)
2154
+ _merge_when(:type=>:update_not_matched_by_source, :values=>values, &block)
2155
+ end
2156
+
2096
2157
  # Use OVERRIDING USER VALUE for INSERT statements, so that identity columns
2097
2158
  # always use the user supplied value, and an error is not raised for identity
2098
2159
  # columns that are GENERATED ALWAYS.
@@ -2170,9 +2231,14 @@ module Sequel
2170
2231
  true
2171
2232
  end
2172
2233
 
2173
- # Returning is always supported.
2234
+ # MERGE RETURNING is supported on PostgreSQL 17+. Other RETURNING is supported
2235
+ # on all supported PostgreSQL versions.
2174
2236
  def supports_returning?(type)
2175
- true
2237
+ if type == :merge
2238
+ server_version >= 170000
2239
+ else
2240
+ true
2241
+ end
2176
2242
  end
2177
2243
 
2178
2244
  # PostgreSQL supports pattern matching via regular expressions
@@ -2281,7 +2347,7 @@ module Sequel
2281
2347
 
2282
2348
  # Append the INSERT sql used in a MERGE
2283
2349
  def _merge_insert_sql(sql, data)
2284
- sql << " THEN INSERT "
2350
+ sql << " THEN INSERT"
2285
2351
  columns, values = _parse_insert_sql_args(data[:values])
2286
2352
  _insert_columns_sql(sql, columns)
2287
2353
  if override = data[:override]
@@ -2290,10 +2356,15 @@ module Sequel
2290
2356
  _insert_values_sql(sql, values)
2291
2357
  end
2292
2358
 
2293
- def _merge_matched_sql(sql, data)
2359
+ def _merge_do_nothing_sql(sql, data)
2294
2360
  sql << " THEN DO NOTHING"
2295
2361
  end
2296
- alias _merge_not_matched_sql _merge_matched_sql
2362
+
2363
+ # Support MERGE RETURNING on PostgreSQL 17+.
2364
+ def _merge_when_sql(sql)
2365
+ super
2366
+ insert_returning_sql(sql) if uses_returning?(:merge)
2367
+ end
2297
2368
 
2298
2369
  # Format TRUNCATE statement with PostgreSQL specific options.
2299
2370
  def _truncate_sql(table)
@@ -2328,6 +2399,25 @@ module Sequel
2328
2399
  join_from_sql(:USING, sql)
2329
2400
  end
2330
2401
 
2402
+ # Handle column aliases containing data types, useful for selecting from functions
2403
+ # that return the record data type.
2404
+ def derived_column_list_sql_append(sql, column_aliases)
2405
+ c = false
2406
+ comma = ', '
2407
+ column_aliases.each do |a|
2408
+ sql << comma if c
2409
+ if a.is_a?(Array)
2410
+ raise Error, "column aliases specified as arrays must have only 2 elements, the first is alias name and the second is data type" unless a.length == 2
2411
+ a, type = a
2412
+ identifier_append(sql, a)
2413
+ sql << " " << db.cast_type_literal(type).to_s
2414
+ else
2415
+ identifier_append(sql, a)
2416
+ end
2417
+ c ||= true
2418
+ end
2419
+ end
2420
+
2331
2421
  # Add ON CONFLICT clause if it should be used
2332
2422
  def insert_conflict_sql(sql)
2333
2423
  if opts = @opts[:insert_conflict]
@@ -2371,6 +2461,9 @@ module Sequel
2371
2461
  # Return the primary key to use for RETURNING in an INSERT statement
2372
2462
  def insert_pk
2373
2463
  (f = opts[:from]) && !f.empty? && (t = f.first)
2464
+
2465
+ t = t.call(self) if t.is_a? Sequel::SQL::DelayedEvaluation
2466
+
2374
2467
  case t
2375
2468
  when Symbol, String, SQL::Identifier, SQL::QualifiedIdentifier
2376
2469
  if pk = db.primary_key(t)
@@ -245,7 +245,7 @@ module Sequel
245
245
  super
246
246
  end
247
247
  when :drop_column
248
- if sqlite_version >= 33500
248
+ if sqlite_version >= 33500 && !indexes(table).any?{|_, h| h[:columns].include?(op[:name])}
249
249
  super
250
250
  else
251
251
  ocp = lambda{|oc| oc.delete_if{|c| c.to_s == op[:name].to_s}}
@@ -349,7 +349,7 @@ module Sequel
349
349
  ps
350
350
  end
351
351
 
352
- # Support creating STRICT AND/OR WITHOUT ROWID tables via :strict and :without_rowid options
352
+ # Support creating STRICT AND/OR WITHOUT ROWID tables via :strict and :without_rowid options, and VIRTUAL tables with :using option.
353
353
  def create_table_sql(name, generator, options)
354
354
  if options[:strict] && options[:without_rowid]
355
355
  "#{super} STRICT, WITHOUT ROWID"
@@ -357,6 +357,8 @@ module Sequel
357
357
  "#{super} STRICT"
358
358
  elsif options[:without_rowid]
359
359
  "#{super} WITHOUT ROWID"
360
+ elsif options[:using]
361
+ "CREATE VIRTUAL TABLE#{' IF NOT EXISTS' if options[:if_not_exists]} #{create_table_table_name_sql(name, options)} USING #{options[:using]}"
360
362
  else
361
363
  super
362
364
  end
@@ -109,6 +109,8 @@ module Sequel
109
109
  # :database :: database name (filename or ':memory:' or file: URI)
110
110
  # :readonly :: open database in read-only mode; useful for reading
111
111
  # static data that you do not want to modify
112
+ # :disable_dqs :: disable double quoted strings in DDL and DML statements
113
+ # (requires SQLite 3.29.0+ and sqlite3 gem version 1.4.3+).
112
114
  # :timeout :: how long to wait for the database to be available if it
113
115
  # is locked, given in milliseconds (default is 5000)
114
116
  # :setup_regexp_function :: enable use of Regexp objects with SQL
@@ -128,6 +130,8 @@ module Sequel
128
130
  opts[:database] = ':memory:' if blank_object?(opts[:database])
129
131
  sqlite3_opts = {}
130
132
  sqlite3_opts[:readonly] = typecast_value_boolean(opts[:readonly]) if opts.has_key?(:readonly)
133
+ # SEQUEL6: Make strict: true the default behavior
134
+ sqlite3_opts[:strict] = typecast_value_boolean(opts[:disable_dqs]) if opts.has_key?(:disable_dqs)
131
135
  db = ::SQLite3::Database.new(opts[:database].to_s, sqlite3_opts)
132
136
  db.busy_timeout(typecast_value_integer(opts.fetch(:timeout, 5000)))
133
137
 
@@ -62,8 +62,7 @@ module Sequel
62
62
  private
63
63
 
64
64
  def database_specific_error_class(exception, opts)
65
- case exception.message
66
- when /1205 - Lock wait timeout exceeded; try restarting transaction\z/
65
+ if exception.error_code == 1205
67
66
  DatabaseLockTimeout
68
67
  else
69
68
  super
@@ -197,11 +197,8 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
197
197
  timeout = @timeout
198
198
  timer = Sequel.start_timer
199
199
 
200
- sync do
201
- @waiters[server].wait(@mutex, timeout)
202
- if conn = next_available(server)
203
- return(allocated(server)[thread] = conn)
204
- end
200
+ if conn = acquire_available(thread, server, timeout)
201
+ return conn
205
202
  end
206
203
 
207
204
  until conn = assign_connection(thread, server)
@@ -211,11 +208,8 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
211
208
 
212
209
  # It's difficult to get to this point, it can only happen if there is a race condition
213
210
  # where a connection cannot be acquired even after the thread is signalled by the condition variable
214
- sync do
215
- @waiters[server].wait(@mutex, timeout - elapsed)
216
- if conn = next_available(server)
217
- return(allocated(server)[thread] = conn)
218
- end
211
+ if conn = acquire_available(thread, server, timeout - elapsed)
212
+ return conn
219
213
  end
220
214
  # :nocov:
221
215
  end
@@ -223,6 +217,28 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
223
217
  conn
224
218
  end
225
219
 
220
+ # Acquire a connection if one is already available, or waiting until it becomes available.
221
+ def acquire_available(thread, server, timeout)
222
+ sync do
223
+ # Check if connection was checked in between when assign_connection failed and now.
224
+ # This is very unlikely, but necessary to prevent a situation where the waiter
225
+ # will wait for a connection even though one has already been checked in.
226
+ # :nocov:
227
+ if conn = next_available(server)
228
+ return(allocated(server)[thread] = conn)
229
+ end
230
+ # :nocov:
231
+
232
+ @waiters[server].wait(@mutex, timeout)
233
+
234
+ # Connection still not available, could be because a connection was disconnected,
235
+ # may have to retry assign_connection to see if a new connection can be made.
236
+ if conn = next_available(server)
237
+ return(allocated(server)[thread] = conn)
238
+ end
239
+ end
240
+ end
241
+
226
242
  # Assign a connection to the thread, or return nil if one cannot be assigned.
227
243
  # The caller should NOT have the mutex before calling this.
228
244
  def assign_connection(thread, server)
@@ -143,11 +143,8 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
143
143
  timeout = @timeout
144
144
  timer = Sequel.start_timer
145
145
 
146
- sync do
147
- @waiter.wait(@mutex, timeout)
148
- if conn = next_available
149
- return(@allocated[thread] = conn)
150
- end
146
+ if conn = acquire_available(thread, timeout)
147
+ return conn
151
148
  end
152
149
 
153
150
  until conn = assign_connection(thread)
@@ -157,11 +154,8 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
157
154
 
158
155
  # It's difficult to get to this point, it can only happen if there is a race condition
159
156
  # where a connection cannot be acquired even after the thread is signalled by the condition variable
160
- sync do
161
- @waiter.wait(@mutex, timeout - elapsed)
162
- if conn = next_available
163
- return(@allocated[thread] = conn)
164
- end
157
+ if conn = acquire_available(thread, timeout - elapsed)
158
+ return conn
165
159
  end
166
160
  # :nocov:
167
161
  end
@@ -169,6 +163,28 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
169
163
  conn
170
164
  end
171
165
 
166
+ # Acquire a connection if one is already available, or waiting until it becomes available.
167
+ def acquire_available(thread, timeout)
168
+ sync do
169
+ # Check if connection was checked in between when assign_connection failed and now.
170
+ # This is very unlikely, but necessary to prevent a situation where the waiter
171
+ # will wait for a connection even though one has already been checked in.
172
+ # :nocov:
173
+ if conn = next_available
174
+ return(@allocated[thread] = conn)
175
+ end
176
+ # :nocov:
177
+
178
+ @waiter.wait(@mutex, timeout)
179
+
180
+ # Connection still not available, could be because a connection was disconnected,
181
+ # may have to retry assign_connection to see if a new connection can be made.
182
+ if conn = next_available
183
+ return(@allocated[thread] = conn)
184
+ end
185
+ end
186
+ end
187
+
172
188
  # Assign a connection to the thread, or return nil if one cannot be assigned.
173
189
  # The caller should NOT have the mutex before calling this.
174
190
  def assign_connection(thread)
@@ -70,13 +70,13 @@ class Sequel::ConnectionPool
70
70
  else
71
71
  pc = if opts[:single_threaded]
72
72
  opts[:servers] ? :sharded_single : :single
73
- # :nocov:
74
- elsif RUBY_VERSION >= '3.4' # SEQUEL6 or maybe earlier switch to 3.2
73
+ elsif RUBY_VERSION >= '3.2'
75
74
  opts[:servers] ? :sharded_timed_queue : :timed_queue
76
75
  # :nocov:
77
76
  else
78
77
  opts[:servers] ? :sharded_threaded : :threaded
79
78
  end
79
+ # :nocov:
80
80
 
81
81
  connection_pool_class(:pool_class=>pc)
82
82
  end
data/lib/sequel/core.rb CHANGED
@@ -164,6 +164,21 @@ module Sequel
164
164
  JSON::ParserError
165
165
  end
166
166
 
167
+ if RUBY_VERSION >= '3.3'
168
+ # Create a new module using the block, and set the temporary name
169
+ # on it using the given a containing module and name.
170
+ def set_temp_name(mod)
171
+ mod.set_temporary_name(yield)
172
+ mod
173
+ end
174
+ # :nocov:
175
+ else
176
+ def set_temp_name(mod)
177
+ mod
178
+ end
179
+ end
180
+ # :nocov:
181
+
167
182
  # Convert given object to json and return the result.
168
183
  # This can be overridden to use an alternative json implementation.
169
184
  def object_to_json(obj, *args, &block)
@@ -34,10 +34,7 @@ module Sequel
34
34
  uri = URI.parse(conn_string)
35
35
  scheme = uri.scheme
36
36
  c = adapter_class(scheme)
37
- uri_options = c.send(:uri_to_options, uri)
38
- uri.query.split('&').map{|s| s.split('=')}.each{|k,v| uri_options[k.to_sym] = v if k && !k.empty?} unless uri.query.to_s.strip.empty?
39
- uri_options.to_a.each{|k,v| uri_options[k] = URI::DEFAULT_PARSER.unescape(v) if v.is_a?(String)}
40
- opts = uri_options.merge(opts).merge!(:orig_opts=>opts.dup, :uri=>conn_string, :adapter=>scheme)
37
+ opts = c.send(:options_from_uri, uri).merge!(opts).merge!(:orig_opts=>opts.dup, :uri=>conn_string, :adapter=>scheme)
41
38
  end
42
39
  when Hash
43
40
  opts = conn_string.merge(opts)
@@ -270,28 +267,20 @@ module Sequel
270
267
  @single_threaded
271
268
  end
272
269
 
273
- if RUBY_ENGINE == 'ruby' && RUBY_VERSION < '2.5'
274
- # :nocov:
275
- def synchronize(server=nil)
276
- @pool.hold(server || :default){|conn| yield conn}
277
- end
278
- # :nocov:
279
- else
280
- # Acquires a database connection, yielding it to the passed block. This is
281
- # useful if you want to make sure the same connection is used for all
282
- # database queries in the block. It is also useful if you want to gain
283
- # direct access to the underlying connection object if you need to do
284
- # something Sequel does not natively support.
285
- #
286
- # If a server option is given, acquires a connection for that specific
287
- # server, instead of the :default server.
288
- #
289
- # DB.synchronize do |conn|
290
- # # ...
291
- # end
292
- def synchronize(server=nil, &block)
293
- @pool.hold(server || :default, &block)
294
- end
270
+ # Acquires a database connection, yielding it to the passed block. This is
271
+ # useful if you want to make sure the same connection is used for all
272
+ # database queries in the block. It is also useful if you want to gain
273
+ # direct access to the underlying connection object if you need to do
274
+ # something Sequel does not natively support.
275
+ #
276
+ # If a server option is given, acquires a connection for that specific
277
+ # server, instead of the :default server.
278
+ #
279
+ # DB.synchronize do |conn|
280
+ # # ...
281
+ # end
282
+ def synchronize(server=nil, &block)
283
+ @pool.hold(server || :default, &block)
295
284
  end
296
285
 
297
286
  # Attempts to acquire a database connection. Returns true if successful.
@@ -343,6 +332,11 @@ module Sequel
343
332
  else
344
333
  @opts.dup
345
334
  end
335
+
336
+ if pr = opts[:connect_opts_proc]
337
+ pr.call(opts)
338
+ end
339
+
346
340
  opts.delete(:servers)
347
341
  opts
348
342
  end
@@ -17,7 +17,7 @@ module Sequel
17
17
  # as the dataset class.
18
18
  def dataset_class=(c)
19
19
  unless @dataset_modules.empty?
20
- c = Class.new(c)
20
+ c = Sequel.set_temp_name(Class.new(c)){"Sequel::Dataset::_Subclass"}
21
21
  @dataset_modules.each{|m| c.send(:include, m)}
22
22
  end
23
23
  @dataset_class = c
@@ -61,10 +61,10 @@ module Sequel
61
61
  # # SELECT id, name FROM table WHERE active ORDER BY id
62
62
  def extend_datasets(mod=nil, &block)
63
63
  raise(Error, "must provide either mod or block, not both") if mod && block
64
- mod = Dataset::DatasetModule.new(&block) if block
64
+ mod = Sequel.set_temp_name(Dataset::DatasetModule.new(&block)){"Sequel::Dataset::_DatasetModule(#{block.source_location[0,2].join(':')})"} if block
65
65
  if @dataset_modules.empty?
66
66
  @dataset_modules = [mod]
67
- @dataset_class = Class.new(@dataset_class)
67
+ @dataset_class = Sequel.set_temp_name(Class.new(@dataset_class)){"Sequel::Dataset::_Subclass"}
68
68
  else
69
69
  @dataset_modules << mod
70
70
  end
@@ -26,6 +26,11 @@ module Sequel
26
26
  :time=>Sequel::SQLTime, :boolean=>[TrueClass, FalseClass].freeze, :float=>Float, :decimal=>BigDecimal,
27
27
  :blob=>Sequel::SQL::Blob}.freeze
28
28
 
29
+ # :nocov:
30
+ URI_PARSER = defined?(::URI::RFC2396_PARSER) ? ::URI::RFC2396_PARSER : ::URI::DEFAULT_PARSER
31
+ # :nocov:
32
+ private_constant :URI_PARSER
33
+
29
34
  # Nested hook Proc; each new hook Proc just wraps the previous one.
30
35
  @initialize_hook = proc{|db| }
31
36
 
@@ -82,6 +87,14 @@ module Sequel
82
87
  end
83
88
  private_class_method :uri_to_options
84
89
 
90
+ def self.options_from_uri(uri)
91
+ uri_options = uri_to_options(uri)
92
+ uri.query.split('&').map{|s| s.split('=')}.each{|k,v| uri_options[k.to_sym] = v if k && !k.empty?} unless uri.query.to_s.strip.empty?
93
+ uri_options.to_a.each{|k,v| uri_options[k] = URI_PARSER.unescape(v) if v.is_a?(String)}
94
+ uri_options
95
+ end
96
+ private_class_method :options_from_uri
97
+
85
98
  # The options hash for this database
86
99
  attr_reader :opts
87
100
 
@@ -103,11 +116,18 @@ module Sequel
103
116
  # :after_connect :: A callable object called after each new connection is made, with the
104
117
  # connection object (and server argument if the callable accepts 2 arguments),
105
118
  # useful for customizations that you want to apply to all connections.
119
+ # :compare_connections_by_identity :: Whether to use compare_by_identity on hashes that use
120
+ # connection objects as keys. Defaults to true. This should only
121
+ # be set to false to work around bugs in libraries or
122
+ # ruby implementations.
106
123
  # :before_preconnect :: Callable that runs after extensions from :preconnect_extensions are loaded,
107
124
  # but before any connections are created.
108
125
  # :cache_schema :: Whether schema should be cached for this Database instance
109
126
  # :check_string_typecast_bytesize :: Whether to check the bytesize of strings before typecasting.
110
127
  # :connect_sqls :: An array of sql strings to execute on each new connection, after :after_connect runs.
128
+ # :connect_opts_proc :: Callable object for modifying options hash used when connecting, designed for
129
+ # cases where the option values (e.g. password) are automatically rotated on
130
+ # a regular basis without involvement from the application using Sequel.
111
131
  # :default_string_column_size :: The default size of string columns, 255 by default.
112
132
  # :extensions :: Extensions to load into this Database instance. Can be a symbol, array of symbols,
113
133
  # or string with extensions separated by columns. These extensions are loaded after
@@ -149,7 +169,7 @@ module Sequel
149
169
  @schemas = {}
150
170
  @prepared_statements = {}
151
171
  @transactions = {}
152
- @transactions.compare_by_identity
172
+ @transactions.compare_by_identity if typecast_value_boolean(@opts.fetch(:compare_connections_by_identity, true))
153
173
  @symbol_literal_cache = {}
154
174
 
155
175
  @timezone = nil
@@ -230,9 +250,13 @@ module Sequel
230
250
  # extension does not have specific support for Database objects, an Error will be raised.
231
251
  # Returns self.
232
252
  def extension(*exts)
233
- Sequel.extension(*exts)
234
253
  exts.each do |ext|
235
- if pr = Sequel.synchronize{EXTENSIONS[ext]}
254
+ unless pr = Sequel.synchronize{EXTENSIONS[ext]}
255
+ Sequel.extension(ext)
256
+ pr = Sequel.synchronize{EXTENSIONS[ext]}
257
+ end
258
+
259
+ if pr
236
260
  if Sequel.synchronize{@loaded_extensions.include?(ext) ? false : (@loaded_extensions << ext)}
237
261
  pr.call(self)
238
262
  end
@@ -250,15 +274,27 @@ module Sequel
250
274
  Sequel.convert_output_timestamp(v, timezone)
251
275
  end
252
276
 
253
- # Returns a string representation of the database object including the
254
- # class name and connection URI and options used when connecting (if any).
277
+ # Returns a string representation of the Database object, including
278
+ # the database type, host, database, and user, if present.
255
279
  def inspect
256
- a = []
257
- a << uri.inspect if uri
258
- if (oo = opts[:orig_opts]) && !oo.empty?
259
- a << oo.inspect
280
+ s = String.new
281
+ s << "#<#{self.class}"
282
+ s << " database_type=#{database_type}" if database_type && database_type != adapter_scheme
283
+
284
+ keys = [:host, :database, :user]
285
+ opts = self.opts
286
+ if !keys.any?{|k| opts[k]} && opts[:uri]
287
+ opts = self.class.send(:options_from_uri, URI.parse(opts[:uri]))
260
288
  end
261
- "#<#{self.class}: #{a.join(' ')}>"
289
+
290
+ keys.each do |key|
291
+ val = opts[key]
292
+ if val && val != ''
293
+ s << " #{key}=#{val}"
294
+ end
295
+ end
296
+
297
+ s << ">"
262
298
  end
263
299
 
264
300
  # Proxy the literal call to the dataset.
@@ -170,7 +170,7 @@ module Sequel
170
170
  c[:ruby_default] = column_schema_to_ruby_default(c[:default], c[:type]) unless c.has_key?(:ruby_default)
171
171
  if c[:primary_key] && !auto_increment_set
172
172
  # If adapter didn't set it, assume that integer primary keys are auto incrementing
173
- c[:auto_increment] = primary_keys == 1 && !!(c[:db_type] =~ /int/io)
173
+ c[:auto_increment] = primary_keys == 1 && !!(c[:db_type] =~ /int/i)
174
174
  end
175
175
  if !c[:max_length] && c[:type] == :string && (max_length = column_schema_max_length(c[:db_type]))
176
176
  c[:max_length] = max_length
@@ -390,25 +390,25 @@ module Sequel
390
390
  # such as :integer or :string.
391
391
  def schema_column_type(db_type)
392
392
  case db_type
393
- when /\A(character( varying)?|n?(var)?char|n?text|string|clob)/io
393
+ when /\A(character( varying)?|n?(var)?char|n?text|string|clob)/i
394
394
  :string
395
- when /\A(int(eger)?|(big|small|tiny)int)/io
395
+ when /\A(int(eger)?|(big|small|tiny)int)/i
396
396
  :integer
397
- when /\Adate\z/io
397
+ when /\Adate\z/i
398
398
  :date
399
- when /\A((small)?datetime|timestamp(\(\d\))?( with(out)? time zone)?)\z/io
399
+ when /\A((small)?datetime(\(\d\))?|timestamp(\(\d\))?( with(out)? time zone)?)\z/i
400
400
  :datetime
401
- when /\Atime( with(out)? time zone)?\z/io
401
+ when /\Atime( with(out)? time zone)?\z/i
402
402
  :time
403
- when /\A(bool(ean)?)\z/io
403
+ when /\A(bool(ean)?)\z/i
404
404
  :boolean
405
- when /\A(real|float( unsigned)?|double( precision)?|double\(\d+,\d+\)( unsigned)?)\z/io
405
+ when /\A(real|float( unsigned)?|double( precision)?|double\(\d+,\d+\)( unsigned)?)\z/i
406
406
  :float
407
- when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(-?\d+|false|true)\))?))\z/io
407
+ when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(-?\d+|false|true)\))?))\z/i
408
408
  $1 && ['0', 'false'].include?($1) ? :integer : :decimal
409
- when /bytea|blob|image|(var)?binary/io
409
+ when /bytea|blob|image|(var)?binary/i
410
410
  :blob
411
- when /\Aenum/io
411
+ when /\Aenum/i
412
412
  :enum
413
413
  end
414
414
  end