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.
- checksums.yaml +4 -4
- data/bin/sequel +9 -4
- data/lib/sequel/adapters/ado.rb +1 -1
- data/lib/sequel/adapters/ibmdb.rb +1 -0
- data/lib/sequel/adapters/jdbc/db2.rb +2 -2
- data/lib/sequel/adapters/jdbc/derby.rb +3 -3
- data/lib/sequel/adapters/jdbc/h2.rb +2 -2
- data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -2
- data/lib/sequel/adapters/jdbc/jtds.rb +2 -2
- data/lib/sequel/adapters/jdbc/mysql.rb +1 -1
- data/lib/sequel/adapters/jdbc/oracle.rb +5 -5
- data/lib/sequel/adapters/jdbc/postgresql.rb +5 -5
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +6 -6
- data/lib/sequel/adapters/jdbc/sqlite.rb +2 -2
- data/lib/sequel/adapters/jdbc/sqlserver.rb +2 -2
- data/lib/sequel/adapters/jdbc.rb +8 -8
- data/lib/sequel/adapters/mysql2.rb +8 -1
- data/lib/sequel/adapters/shared/access.rb +1 -0
- data/lib/sequel/adapters/shared/db2.rb +1 -1
- data/lib/sequel/adapters/shared/mssql.rb +18 -5
- data/lib/sequel/adapters/shared/mysql.rb +8 -4
- data/lib/sequel/adapters/shared/oracle.rb +1 -0
- data/lib/sequel/adapters/shared/postgres.rb +106 -13
- data/lib/sequel/adapters/shared/sqlite.rb +4 -2
- data/lib/sequel/adapters/sqlite.rb +4 -0
- data/lib/sequel/adapters/trilogy.rb +1 -2
- data/lib/sequel/connection_pool/sharded_threaded.rb +26 -10
- data/lib/sequel/connection_pool/threaded.rb +26 -10
- data/lib/sequel/connection_pool.rb +2 -2
- data/lib/sequel/core.rb +15 -0
- data/lib/sequel/database/connecting.rb +20 -26
- data/lib/sequel/database/dataset_defaults.rb +3 -3
- data/lib/sequel/database/misc.rb +46 -10
- data/lib/sequel/database/query.rb +11 -11
- data/lib/sequel/database/schema_generator.rb +8 -0
- data/lib/sequel/database/schema_methods.rb +17 -1
- data/lib/sequel/dataset/actions.rb +9 -1
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +1 -1
- data/lib/sequel/dataset/prepared_statements.rb +2 -1
- data/lib/sequel/dataset/query.rb +9 -5
- data/lib/sequel/dataset/sql.rb +25 -5
- data/lib/sequel/extensions/caller_logging.rb +2 -0
- data/lib/sequel/extensions/connection_validator.rb +15 -10
- data/lib/sequel/extensions/dataset_run.rb +41 -0
- data/lib/sequel/extensions/migration.rb +23 -3
- data/lib/sequel/extensions/null_dataset.rb +2 -2
- data/lib/sequel/extensions/pg_auto_parameterize.rb +1 -1
- data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +93 -10
- data/lib/sequel/extensions/pg_enum.rb +3 -3
- data/lib/sequel/extensions/pg_json_ops.rb +642 -9
- data/lib/sequel/extensions/pg_row.rb +3 -1
- data/lib/sequel/extensions/pg_schema_caching.rb +90 -0
- data/lib/sequel/extensions/provenance.rb +2 -0
- data/lib/sequel/extensions/query_blocker.rb +172 -0
- data/lib/sequel/extensions/schema_caching.rb +24 -9
- data/lib/sequel/extensions/schema_dumper.rb +16 -4
- data/lib/sequel/extensions/sqlite_json_ops.rb +1 -1
- data/lib/sequel/extensions/stdio_logger.rb +48 -0
- data/lib/sequel/extensions/string_agg.rb +17 -4
- data/lib/sequel/extensions/temporarily_release_connection.rb +178 -0
- data/lib/sequel/extensions/virtual_row_method_block.rb +1 -0
- data/lib/sequel/model/associations.rb +28 -3
- data/lib/sequel/model/base.rb +67 -18
- data/lib/sequel/plugins/association_pks.rb +1 -1
- data/lib/sequel/plugins/column_encryption.rb +1 -1
- data/lib/sequel/plugins/composition.rb +1 -1
- data/lib/sequel/plugins/defaults_setter.rb +16 -4
- data/lib/sequel/plugins/enum.rb +1 -1
- data/lib/sequel/plugins/forbid_lazy_load.rb +14 -1
- data/lib/sequel/plugins/input_transformer.rb +1 -1
- data/lib/sequel/plugins/inspect_pk.rb +44 -0
- data/lib/sequel/plugins/instance_filters.rb +4 -1
- data/lib/sequel/plugins/inverted_subsets.rb +1 -0
- data/lib/sequel/plugins/lazy_attributes.rb +1 -1
- data/lib/sequel/plugins/nested_attributes.rb +10 -5
- data/lib/sequel/plugins/optimistic_locking.rb +2 -0
- data/lib/sequel/plugins/paged_operations.rb +5 -2
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +6 -1
- data/lib/sequel/plugins/pg_auto_validate_enums.rb +88 -0
- data/lib/sequel/plugins/pg_eager_any_typed_array.rb +95 -0
- data/lib/sequel/plugins/rcte_tree.rb +1 -1
- data/lib/sequel/plugins/serialization.rb +11 -5
- data/lib/sequel/plugins/sql_comments.rb +7 -2
- data/lib/sequel/plugins/static_cache_cache.rb +50 -13
- data/lib/sequel/plugins/subset_conditions.rb +85 -5
- data/lib/sequel/plugins/subset_static_cache.rb +263 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +6 -2
- data/lib/sequel/plugins/validate_associated.rb +1 -1
- data/lib/sequel/sql.rb +16 -6
- data/lib/sequel/version.rb +1 -1
- metadata +12 -234
- data/CHANGELOG +0 -1355
- data/README.rdoc +0 -936
- data/doc/advanced_associations.rdoc +0 -884
- data/doc/association_basics.rdoc +0 -1859
- data/doc/bin_sequel.rdoc +0 -146
- data/doc/cheat_sheet.rdoc +0 -255
- data/doc/code_order.rdoc +0 -102
- data/doc/core_extensions.rdoc +0 -405
- data/doc/dataset_basics.rdoc +0 -96
- data/doc/dataset_filtering.rdoc +0 -222
- data/doc/extensions.rdoc +0 -77
- data/doc/fork_safety.rdoc +0 -84
- data/doc/mass_assignment.rdoc +0 -98
- data/doc/migration.rdoc +0 -660
- data/doc/model_dataset_method_design.rdoc +0 -129
- data/doc/model_hooks.rdoc +0 -254
- data/doc/model_plugins.rdoc +0 -270
- data/doc/mssql_stored_procedures.rdoc +0 -43
- data/doc/object_model.rdoc +0 -563
- data/doc/opening_databases.rdoc +0 -436
- data/doc/postgresql.rdoc +0 -611
- data/doc/prepared_statements.rdoc +0 -144
- data/doc/querying.rdoc +0 -1070
- data/doc/reflection.rdoc +0 -120
- data/doc/release_notes/5.0.0.txt +0 -159
- data/doc/release_notes/5.1.0.txt +0 -31
- data/doc/release_notes/5.10.0.txt +0 -84
- data/doc/release_notes/5.11.0.txt +0 -83
- data/doc/release_notes/5.12.0.txt +0 -141
- data/doc/release_notes/5.13.0.txt +0 -27
- data/doc/release_notes/5.14.0.txt +0 -63
- data/doc/release_notes/5.15.0.txt +0 -39
- data/doc/release_notes/5.16.0.txt +0 -110
- data/doc/release_notes/5.17.0.txt +0 -31
- data/doc/release_notes/5.18.0.txt +0 -69
- data/doc/release_notes/5.19.0.txt +0 -28
- data/doc/release_notes/5.2.0.txt +0 -33
- data/doc/release_notes/5.20.0.txt +0 -89
- data/doc/release_notes/5.21.0.txt +0 -87
- data/doc/release_notes/5.22.0.txt +0 -48
- data/doc/release_notes/5.23.0.txt +0 -56
- data/doc/release_notes/5.24.0.txt +0 -56
- data/doc/release_notes/5.25.0.txt +0 -32
- data/doc/release_notes/5.26.0.txt +0 -35
- data/doc/release_notes/5.27.0.txt +0 -21
- data/doc/release_notes/5.28.0.txt +0 -16
- data/doc/release_notes/5.29.0.txt +0 -22
- data/doc/release_notes/5.3.0.txt +0 -121
- data/doc/release_notes/5.30.0.txt +0 -20
- data/doc/release_notes/5.31.0.txt +0 -148
- data/doc/release_notes/5.32.0.txt +0 -46
- data/doc/release_notes/5.33.0.txt +0 -24
- data/doc/release_notes/5.34.0.txt +0 -40
- data/doc/release_notes/5.35.0.txt +0 -56
- data/doc/release_notes/5.36.0.txt +0 -60
- data/doc/release_notes/5.37.0.txt +0 -30
- data/doc/release_notes/5.38.0.txt +0 -28
- data/doc/release_notes/5.39.0.txt +0 -19
- data/doc/release_notes/5.4.0.txt +0 -80
- data/doc/release_notes/5.40.0.txt +0 -40
- data/doc/release_notes/5.41.0.txt +0 -25
- data/doc/release_notes/5.42.0.txt +0 -136
- data/doc/release_notes/5.43.0.txt +0 -98
- data/doc/release_notes/5.44.0.txt +0 -32
- data/doc/release_notes/5.45.0.txt +0 -34
- data/doc/release_notes/5.46.0.txt +0 -87
- data/doc/release_notes/5.47.0.txt +0 -59
- data/doc/release_notes/5.48.0.txt +0 -14
- data/doc/release_notes/5.49.0.txt +0 -59
- data/doc/release_notes/5.5.0.txt +0 -61
- data/doc/release_notes/5.50.0.txt +0 -78
- data/doc/release_notes/5.51.0.txt +0 -47
- data/doc/release_notes/5.52.0.txt +0 -87
- data/doc/release_notes/5.53.0.txt +0 -23
- data/doc/release_notes/5.54.0.txt +0 -27
- data/doc/release_notes/5.55.0.txt +0 -21
- data/doc/release_notes/5.56.0.txt +0 -51
- data/doc/release_notes/5.57.0.txt +0 -23
- data/doc/release_notes/5.58.0.txt +0 -31
- data/doc/release_notes/5.59.0.txt +0 -73
- data/doc/release_notes/5.6.0.txt +0 -31
- data/doc/release_notes/5.60.0.txt +0 -22
- data/doc/release_notes/5.61.0.txt +0 -43
- data/doc/release_notes/5.62.0.txt +0 -132
- data/doc/release_notes/5.63.0.txt +0 -33
- data/doc/release_notes/5.64.0.txt +0 -50
- data/doc/release_notes/5.65.0.txt +0 -21
- data/doc/release_notes/5.66.0.txt +0 -24
- data/doc/release_notes/5.67.0.txt +0 -32
- data/doc/release_notes/5.68.0.txt +0 -61
- data/doc/release_notes/5.69.0.txt +0 -26
- data/doc/release_notes/5.7.0.txt +0 -108
- data/doc/release_notes/5.70.0.txt +0 -35
- data/doc/release_notes/5.71.0.txt +0 -21
- data/doc/release_notes/5.72.0.txt +0 -33
- data/doc/release_notes/5.73.0.txt +0 -66
- data/doc/release_notes/5.74.0.txt +0 -45
- data/doc/release_notes/5.75.0.txt +0 -35
- data/doc/release_notes/5.76.0.txt +0 -86
- data/doc/release_notes/5.77.0.txt +0 -63
- data/doc/release_notes/5.78.0.txt +0 -67
- data/doc/release_notes/5.79.0.txt +0 -28
- data/doc/release_notes/5.8.0.txt +0 -170
- data/doc/release_notes/5.80.0.txt +0 -40
- data/doc/release_notes/5.9.0.txt +0 -99
- data/doc/schema_modification.rdoc +0 -679
- data/doc/security.rdoc +0 -443
- data/doc/sharding.rdoc +0 -286
- data/doc/sql.rdoc +0 -648
- data/doc/testing.rdoc +0 -190
- data/doc/thread_safety.rdoc +0 -15
- data/doc/transactions.rdoc +0 -250
- data/doc/validations.rdoc +0 -558
- 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 :
|
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]} #{
|
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/
|
1666
|
+
when /\Ainterval\z/i
|
1655
1667
|
:interval
|
1656
|
-
when /\Acitext\z/
|
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
|
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
|
-
#
|
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
|
-
|
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
|
2359
|
+
def _merge_do_nothing_sql(sql, data)
|
2294
2360
|
sql << " THEN DO NOTHING"
|
2295
2361
|
end
|
2296
|
-
|
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
|
|
@@ -197,11 +197,8 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
197
197
|
timeout = @timeout
|
198
198
|
timer = Sequel.start_timer
|
199
199
|
|
200
|
-
|
201
|
-
|
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
|
-
|
215
|
-
|
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
|
-
|
147
|
-
|
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
|
-
|
161
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
274
|
-
#
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
#
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
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
|
data/lib/sequel/database/misc.rb
CHANGED
@@ -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
|
-
|
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
|
254
|
-
#
|
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
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
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
|
-
|
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/
|
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)/
|
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)/
|
395
|
+
when /\A(int(eger)?|(big|small|tiny)int)/i
|
396
396
|
:integer
|
397
|
-
when /\Adate\z/
|
397
|
+
when /\Adate\z/i
|
398
398
|
:date
|
399
|
-
when /\A((small)?datetime
|
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/
|
401
|
+
when /\Atime( with(out)? time zone)?\z/i
|
402
402
|
:time
|
403
|
-
when /\A(bool(ean)?)\z/
|
403
|
+
when /\A(bool(ean)?)\z/i
|
404
404
|
:boolean
|
405
|
-
when /\A(real|float( unsigned)?|double( precision)?|double\(\d+,\d+\)( unsigned)?)\z/
|
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/
|
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/
|
409
|
+
when /bytea|blob|image|(var)?binary/i
|
410
410
|
:blob
|
411
|
-
when /\Aenum/
|
411
|
+
when /\Aenum/i
|
412
412
|
:enum
|
413
413
|
end
|
414
414
|
end
|