sequel 5.85.0 → 5.87.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ecb752c20a2b59df814e873f04ac143aadd875b503b2400cca8929d9eabff572
4
- data.tar.gz: d528c6d03626b725f27599c8cf624311517e4f4fe67c12139a2e815278fbb707
3
+ metadata.gz: 8be61078e55201001f1041d09be334411bfaef071e6dbeb3c9082c21b0e70dce
4
+ data.tar.gz: c877f3832d2ef28b14d7037e6bbf7bbe90d2d6f6391736bc3b9e6bdc142b5ba5
5
5
  SHA512:
6
- metadata.gz: 4fcb91f8acb96a4a57152c9a0f41e78af1b42d6c393f8c90b2099e00d632676096c8ac27b0e31699fd31be0e22912b55e4372353fef93e967e548741dbc6406e
7
- data.tar.gz: e2545b4dcca9ca1a13f47cbfdf2f38802d2564f7f0766d526e5041b93a94142560517f5d3ef5c9a5d2692e67a482c8044b4812c064fd35c2edf0c19a3df6b25c
6
+ metadata.gz: 7b032d7f8987e0727cc79b98f7f6d5319cd1dc13657c3a2ebc16bff4276d26eb368f1a498d51e27df0aea89184d0b811f6a05355cf3f7a400b45cd8707061f63
7
+ data.tar.gz: f2a1326e3635a7840e220b368459407409391507a3e87cb85ac01f4360d5762a9bdf15ee350032875f9c6d202f03b868fd6c7af43e72bf80cca007edd34cc14e
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- Sequel::JDBC.load_driver('com.ibm.db2.jcc.DB2Driver')
3
+ Sequel::JDBC.load_driver('Java::ComIbmDb2Jcc::DB2Driver')
4
4
  require_relative '../shared/db2'
5
5
  require_relative 'transactions'
6
6
 
@@ -25,7 +25,7 @@ module Sequel
25
25
  end
26
26
  end
27
27
  db.extend_datasets Sequel::DB2::DatasetMethods
28
- com.ibm.db2.jcc.DB2Driver
28
+ Java::ComIbmDb2Jcc::DB2Driver
29
29
  end
30
30
  end
31
31
 
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- Sequel::JDBC.load_driver('org.apache.derby.jdbc.EmbeddedDriver', :Derby)
3
+ Sequel::JDBC.load_driver('Java::OrgApacheDerbyJdbc::EmbeddedDriver', :Derby)
4
4
  require_relative 'transactions'
5
5
  require_relative '../utils/columns_limit_1'
6
6
 
@@ -10,7 +10,7 @@ module Sequel
10
10
  DATABASE_SETUP[:derby] = proc do |db|
11
11
  db.extend(Sequel::JDBC::Derby::DatabaseMethods)
12
12
  db.dataset_class = Sequel::JDBC::Derby::Dataset
13
- org.apache.derby.jdbc.EmbeddedDriver
13
+ Java::OrgApacheDerbyJdbc::EmbeddedDriver
14
14
  end
15
15
  end
16
16
 
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- Sequel::JDBC.load_driver('org.h2.Driver', :H2)
3
+ Sequel::JDBC.load_driver('Java::OrgH2::Driver', :H2)
4
4
  require_relative '../../extensions/auto_cast_date_and_time'
5
5
 
6
6
  module Sequel
@@ -9,7 +9,7 @@ module Sequel
9
9
  DATABASE_SETUP[:h2] = proc do |db|
10
10
  db.extend(Sequel::JDBC::H2::DatabaseMethods)
11
11
  db.dataset_class = Sequel::JDBC::H2::Dataset
12
- org.h2.Driver
12
+ Java::OrgH2::Driver
13
13
  end
14
14
  end
15
15
 
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- Sequel::JDBC.load_driver('org.hsqldb.jdbcDriver', :HSQLDB)
3
+ Sequel::JDBC.load_driver('Java::OrgHsqldb::jdbcDriver', :HSQLDB)
4
4
  require_relative 'transactions'
5
5
  require_relative '../../extensions/auto_cast_date_and_time'
6
6
 
@@ -10,7 +10,7 @@ module Sequel
10
10
  DATABASE_SETUP[:hsqldb] = proc do |db|
11
11
  db.extend(Sequel::JDBC::HSQLDB::DatabaseMethods)
12
12
  db.dataset_class = Sequel::JDBC::HSQLDB::Dataset
13
- org.hsqldb.jdbcDriver
13
+ Java::OrgHsqldb::jdbcDriver
14
14
  end
15
15
  end
16
16
 
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- Sequel::JDBC.load_driver('Java::net.sourceforge.jtds.jdbc.Driver', :JTDS)
3
+ Sequel::JDBC.load_driver('Java::NetSourceforgeJtdsJdbc::Driver', :JTDS)
4
4
  require_relative 'mssql'
5
5
 
6
6
  module Sequel
@@ -10,7 +10,7 @@ module Sequel
10
10
  db.extend(Sequel::JDBC::JTDS::DatabaseMethods)
11
11
  db.extend_datasets Sequel::MSSQL::DatasetMethods
12
12
  db.send(:set_mssql_unicode_strings)
13
- Java::net.sourceforge.jtds.jdbc.Driver
13
+ Java::NetSourceforgeJtdsJdbc::Driver
14
14
  end
15
15
  end
16
16
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Sequel
4
4
  module JDBC
5
- driver = Sequel::JDBC.load_driver(%w'com.mysql.cj.jdbc.Driver com.mysql.jdbc.Driver', :MySQL)
5
+ driver = Sequel::JDBC.load_driver(%w'Java::ComMysqlCjJdbc::Driver Java::ComMysqlJdbc::Driver', :MySQL)
6
6
  require_relative '../shared/mysql'
7
7
 
8
8
  Sequel.synchronize do
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- Sequel::JDBC.load_driver('Java::oracle.jdbc.driver.OracleDriver')
3
+ Sequel::JDBC.load_driver('Java::OracleJdbcDriver::OracleDriver')
4
4
  require_relative '../shared/oracle'
5
5
  require_relative 'transactions'
6
6
 
@@ -10,12 +10,12 @@ module Sequel
10
10
  DATABASE_SETUP[:oracle] = proc do |db|
11
11
  db.extend(Sequel::JDBC::Oracle::DatabaseMethods)
12
12
  db.dataset_class = Sequel::JDBC::Oracle::Dataset
13
- Java::oracle.jdbc.driver.OracleDriver
13
+ Java::OracleJdbcDriver::OracleDriver
14
14
  end
15
15
  end
16
16
 
17
17
  module Oracle
18
- JAVA_BIG_DECIMAL_CONSTRUCTOR = java.math.BigDecimal.java_class.constructor(Java::long).method(:new_instance)
18
+ JAVA_BIG_DECIMAL_CONSTRUCTOR = Java::JavaMath::BigDecimal.java_class.constructor(Java::long).method(:new_instance)
19
19
  ORACLE_DECIMAL = Object.new
20
20
  def ORACLE_DECIMAL.call(r, i)
21
21
  if v = r.getBigDecimal(i)
@@ -76,7 +76,7 @@ module Sequel
76
76
  rs = log_connection_yield(sql, conn){stmt.executeQuery(sql)}
77
77
  rs.next
78
78
  rs.getLong(1)
79
- rescue java.sql.SQLException
79
+ rescue Java::JavaSql::SQLException
80
80
  nil
81
81
  end
82
82
  end
@@ -122,7 +122,7 @@ module Sequel
122
122
  NUMERIC_TYPE = Java::JavaSQL::Types::NUMERIC
123
123
  TIMESTAMP_TYPE = Java::JavaSQL::Types::TIMESTAMP
124
124
  CLOB_TYPE = Java::JavaSQL::Types::CLOB
125
- TIMESTAMPTZ_TYPES = [Java::oracle.jdbc.OracleTypes::TIMESTAMPTZ, Java::oracle.jdbc.OracleTypes::TIMESTAMPLTZ].freeze
125
+ TIMESTAMPTZ_TYPES = [Java::OracleJdbc::OracleTypes::TIMESTAMPTZ, Java::OracleJdbc::OracleTypes::TIMESTAMPLTZ].freeze
126
126
 
127
127
  def type_convertor(map, meta, type, i)
128
128
  case type
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- Sequel::JDBC.load_driver('org.postgresql.Driver', :Postgres)
3
+ Sequel::JDBC.load_driver('Java::OrgPostgresql::Driver', :Postgres)
4
4
  require_relative '../shared/postgres'
5
5
 
6
6
  module Sequel
@@ -9,7 +9,7 @@ module Sequel
9
9
  DATABASE_SETUP[:postgresql] = proc do |db|
10
10
  db.dataset_class = Sequel::JDBC::Postgres::Dataset
11
11
  db.extend(Sequel::JDBC::Postgres::DatabaseMethods)
12
- org.postgresql.Driver
12
+ Java::OrgPostgresql::Driver
13
13
  end
14
14
  end
15
15
 
@@ -43,7 +43,7 @@ module Sequel
43
43
 
44
44
  synchronize(opts[:server]) do |conn|
45
45
  begin
46
- copy_manager = org.postgresql.copy.CopyManager.new(conn)
46
+ copy_manager = Java::OrgPostgresqlCopy::CopyManager.new(conn)
47
47
  copier = copy_manager.copy_in(copy_into_sql(table, opts))
48
48
  if defined?(yield)
49
49
  while buf = yield
@@ -74,7 +74,7 @@ module Sequel
74
74
  # See Sequel::Postgres::Adapter#copy_table
75
75
  def copy_table(table, opts=OPTS)
76
76
  synchronize(opts[:server]) do |conn|
77
- copy_manager = org.postgresql.copy.CopyManager.new(conn)
77
+ copy_manager = Java::OrgPostgresqlCopy::CopyManager.new(conn)
78
78
  copier = copy_manager.copy_out(copy_table_sql(table, opts))
79
79
  begin
80
80
  if defined?(yield)
@@ -148,7 +148,7 @@ module Sequel
148
148
  # and set that as the prepared statement argument.
149
149
  def set_ps_arg(cps, arg, i)
150
150
  if v = bound_variable_arg(arg, nil)
151
- obj = org.postgresql.util.PGobject.new
151
+ obj = Java::OrgPostgresqlUtil::PGobject.new
152
152
  obj.setType("unknown")
153
153
  obj.setValue(v)
154
154
  cps.setObject(i, obj)
@@ -6,12 +6,12 @@ require_relative 'transactions'
6
6
  module Sequel
7
7
  module JDBC
8
8
  drv = [
9
- lambda{Java::sybase.jdbc4.sqlanywhere.IDriver},
10
- lambda{Java::ianywhere.ml.jdbcodbc.jdbc4.IDriver},
11
- lambda{Java::sybase.jdbc.sqlanywhere.IDriver},
12
- lambda{Java::ianywhere.ml.jdbcodbc.jdbc.IDriver},
13
- lambda{Java::com.sybase.jdbc4.jdbc.Sybdriver},
14
- lambda{Java::com.sybase.jdbc3.jdbc.Sybdriver}
9
+ lambda{Java::SybaseJdbc4Sqlanywhere::IDriver},
10
+ lambda{Java::IanywhereMlJdbcodbcJdbc4::IDriver},
11
+ lambda{Java::SybaseJdbcSqlanywhere::IDriver},
12
+ lambda{Java::IanywhereMlJdbcodbcJdbc::IDriver},
13
+ lambda{Java::ComSybaseJdbc4Jdbc::Sybdriver},
14
+ lambda{Java::ComSybaseJdbc3Jdbc::Sybdriver}
15
15
  ].each do |class_proc|
16
16
  begin
17
17
  break class_proc.call
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- Sequel::JDBC.load_driver('org.sqlite.JDBC', :SQLite3)
3
+ Sequel::JDBC.load_driver('Java::OrgSqlite::JDBC', :SQLite3)
4
4
  require_relative '../shared/sqlite'
5
5
 
6
6
  module Sequel
@@ -10,7 +10,7 @@ module Sequel
10
10
  db.extend(Sequel::JDBC::SQLite::DatabaseMethods)
11
11
  db.extend_datasets Sequel::SQLite::DatasetMethods
12
12
  db.set_integer_booleans
13
- org.sqlite.JDBC
13
+ Java::OrgSqlite::JDBC
14
14
  end
15
15
  end
16
16
 
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- Sequel::JDBC.load_driver('com.microsoft.sqlserver.jdbc.SQLServerDriver')
3
+ Sequel::JDBC.load_driver('Java::ComMicrosoftSqlserverJdbc::SQLServerDriver')
4
4
  require_relative 'mssql'
5
5
 
6
6
  module Sequel
@@ -10,7 +10,7 @@ module Sequel
10
10
  db.extend(Sequel::JDBC::SQLServer::DatabaseMethods)
11
11
  db.extend_datasets Sequel::MSSQL::DatasetMethods
12
12
  db.send(:set_mssql_unicode_strings)
13
- com.microsoft.sqlserver.jdbc.SQLServerDriver
13
+ Java::ComMicrosoftSqlserverJdbc::SQLServerDriver
14
14
  end
15
15
  end
16
16
 
@@ -20,7 +20,7 @@ module Sequel
20
20
 
21
21
  # Create custom NativeException alias for nicer access, and also so that
22
22
  # JRuby 9.2+ so it doesn't use the deprecated ::NativeException
23
- NativeException = java.lang.Exception
23
+ NativeException = Java::JavaLang::Exception
24
24
 
25
25
  # Default database error classes
26
26
  DATABASE_ERROR_CLASSES = [NativeException]
@@ -227,7 +227,7 @@ module Sequel
227
227
  raise e unless driver
228
228
  # If the DriverManager can't get the connection - use the connect
229
229
  # method of the driver. (This happens under Tomcat for instance)
230
- props = java.util.Properties.new
230
+ props = Java::JavaUtil::Properties.new
231
231
  if opts && opts[:user] && opts[:password]
232
232
  props.setProperty("user", opts[:user])
233
233
  props.setProperty("password", opts[:password])
@@ -506,7 +506,7 @@ module Sequel
506
506
  # Gets the connection from JNDI.
507
507
  def get_connection_from_jndi
508
508
  jndi_name = JNDI_URI_REGEXP.match(uri)[1]
509
- javax.naming.InitialContext.new.lookup(jndi_name).connection
509
+ Java::JavaxNaming::InitialContext.new.lookup(jndi_name).connection
510
510
  end
511
511
 
512
512
  # Gets the JDBC connection uri from the JNDI resource.
@@ -530,19 +530,19 @@ module Sequel
530
530
 
531
531
  # Support Date objects used in bound variables
532
532
  def java_sql_date(date)
533
- java.sql.Date.new(Time.local(date.year, date.month, date.day).to_i * 1000)
533
+ Java::JavaSql::Date.new(Time.local(date.year, date.month, date.day).to_i * 1000)
534
534
  end
535
535
 
536
536
  # Support DateTime objects used in bound variables
537
537
  def java_sql_datetime(datetime)
538
- ts = java.sql.Timestamp.new(Time.local(datetime.year, datetime.month, datetime.day, datetime.hour, datetime.min, datetime.sec).to_i * 1000)
538
+ ts = Java::JavaSql::Timestamp.new(Time.local(datetime.year, datetime.month, datetime.day, datetime.hour, datetime.min, datetime.sec).to_i * 1000)
539
539
  ts.setNanos((datetime.sec_fraction * 1000000000).to_i)
540
540
  ts
541
541
  end
542
542
 
543
543
  # Support fractional seconds for Time objects used in bound variables
544
544
  def java_sql_timestamp(time)
545
- ts = java.sql.Timestamp.new(time.to_i * 1000)
545
+ ts = Java::JavaSql::Timestamp.new(time.to_i * 1000)
546
546
  ts.setNanos(time.nsec)
547
547
  ts
548
548
  end
@@ -567,7 +567,7 @@ module Sequel
567
567
  im = input_identifier_meth(opts[:dataset])
568
568
  table = SQL::Identifier.new(im.call(table_name))
569
569
  table = SQL::QualifiedIdentifier.new(im.call(opts[:schema]), table) if opts[:schema]
570
- metadata_dataset.with_sql("DESCRIBE ?", table).map do |row|
570
+ metadata_dataset.with_sql("SHOW FULL COLUMNS FROM ?", table).map do |row|
571
571
  extra = row.delete(:Extra)
572
572
  if row[:primary_key] = row.delete(:Key) == 'PRI'
573
573
  row[:auto_increment] = !!(extra.to_s =~ /auto_increment/i)
@@ -577,10 +577,14 @@ module Sequel
577
577
  row[:generated] = !!(extra.to_s =~ /VIRTUAL|STORED|PERSISTENT/i)
578
578
  end
579
579
  row[:allow_null] = row.delete(:Null) == 'YES'
580
+ row[:comment] = row.delete(:Comment)
581
+ row[:comment] = nil if row[:comment] == ""
580
582
  row[:default] = row.delete(:Default)
581
583
  row[:db_type] = row.delete(:Type)
582
584
  row[:type] = schema_column_type(row[:db_type])
583
585
  row[:extra] = extra
586
+ row.delete(:Collation)
587
+ row.delete(:Privileges)
584
588
  [m.call(row.delete(:Field)), row]
585
589
  end
586
590
  end
@@ -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),
@@ -2430,6 +2431,9 @@ module Sequel
2430
2431
  # Return the primary key to use for RETURNING in an INSERT statement
2431
2432
  def insert_pk
2432
2433
  (f = opts[:from]) && !f.empty? && (t = f.first)
2434
+
2435
+ t = t.call(self) if t.is_a? Sequel::SQL::DelayedEvaluation
2436
+
2433
2437
  case t
2434
2438
  when Symbol, String, SQL::Identifier, SQL::QualifiedIdentifier
2435
2439
  if pk = db.primary_key(t)
@@ -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
@@ -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
 
@@ -85,7 +90,7 @@ module Sequel
85
90
  def self.options_from_uri(uri)
86
91
  uri_options = uri_to_options(uri)
87
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?
88
- uri_options.to_a.each{|k,v| uri_options[k] = URI::DEFAULT_PARSER.unescape(v) if v.is_a?(String)}
93
+ uri_options.to_a.each{|k,v| uri_options[k] = URI_PARSER.unescape(v) if v.is_a?(String)}
89
94
  uri_options
90
95
  end
91
96
  private_class_method :options_from_uri
@@ -241,9 +246,13 @@ module Sequel
241
246
  # extension does not have specific support for Database objects, an Error will be raised.
242
247
  # Returns self.
243
248
  def extension(*exts)
244
- Sequel.extension(*exts)
245
249
  exts.each do |ext|
246
- if pr = Sequel.synchronize{EXTENSIONS[ext]}
250
+ unless pr = Sequel.synchronize{EXTENSIONS[ext]}
251
+ Sequel.extension(ext)
252
+ pr = Sequel.synchronize{EXTENSIONS[ext]}
253
+ end
254
+
255
+ if pr
247
256
  if Sequel.synchronize{@loaded_extensions.include?(ext) ? false : (@loaded_extensions << ext)}
248
257
  pr.call(self)
249
258
  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)
@@ -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})")
@@ -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
@@ -0,0 +1,90 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # The pg_schema_caching extension builds on top of the schema_caching
4
+ # extension, and allows it to handle custom PostgreSQL types. On
5
+ # PostgreSQL, column schema hashes include an :oid entry for the OID
6
+ # for the column's type. For custom types, this OID is dependent on
7
+ # the PostgreSQL database, so in most cases, test and development
8
+ # versions of the same database, created with the same migrations,
9
+ # will have different OIDs.
10
+ #
11
+ # To fix this case, the pg_schema_caching extension removes custom
12
+ # OIDs from the schema cache when dumping the schema, replacing them
13
+ # with a placeholder. When loading the cached schema, the Database
14
+ # object makes a single query to get the OIDs for all custom types
15
+ # used by the cached schema, and it updates all related column
16
+ # schema hashes to set the correct :oid entry for the current
17
+ # database.
18
+ #
19
+ # Related module: Sequel::Postgres::SchemaCaching
20
+
21
+ require_relative "schema_caching"
22
+
23
+ module Sequel
24
+ module Postgres
25
+ module SchemaCaching
26
+ include Sequel::SchemaCaching
27
+
28
+ private
29
+
30
+ # Load custom oids from database when loading schema cache file.
31
+ def load_schema_cache_file(file)
32
+ set_custom_oids_for_cached_schema(super)
33
+ end
34
+
35
+ # Find all column schema hashes that use custom types.
36
+ # Load the oids for custom types in a single query, and update
37
+ # each related column schema hash with the correct oid.
38
+ def set_custom_oids_for_cached_schema(schemas)
39
+ custom_oid_rows = {}
40
+
41
+ schemas.each_value do |cols|
42
+ cols.each do |_, h|
43
+ if h[:oid] == :custom
44
+ (custom_oid_rows[h[:db_type]] ||= []) << h
45
+ end
46
+ end
47
+ end
48
+
49
+ unless custom_oid_rows.empty?
50
+ from(:pg_type).where(:typname=>custom_oid_rows.keys).select_hash(:typname, :oid).each do |name, oid|
51
+ custom_oid_rows.delete(name).each do |row|
52
+ row[:oid] = oid
53
+ end
54
+ end
55
+ end
56
+
57
+ unless custom_oid_rows.empty?
58
+ warn "Could not load OIDs for the following custom types: #{custom_oid_rows.keys.sort.join(", ")}", uplevel: 3
59
+
60
+ schemas.keys.each do |k|
61
+ if schemas[k].any?{|_,h| h[:oid] == :custom}
62
+ # Remove schema entry for table, so it will be queried at runtime to get the correct oids
63
+ schemas.delete(k)
64
+ end
65
+ end
66
+ end
67
+
68
+ schemas
69
+ end
70
+
71
+ # Replace :oid entries for custom types with :custom.
72
+ def dumpable_schema_cache
73
+ sch = super
74
+
75
+ sch.each_value do |cols|
76
+ cols.each do |_, h|
77
+ if (oid = h[:oid]) && oid >= 10000
78
+ h[:oid] = :custom
79
+ end
80
+ end
81
+ end
82
+
83
+ sch
84
+ end
85
+ end
86
+ end
87
+
88
+ Database.register_extension(:pg_schema_caching, Postgres::SchemaCaching)
89
+ end
90
+
@@ -51,14 +51,7 @@ module Sequel
51
51
  module SchemaCaching
52
52
  # Dump the cached schema to the filename given in Marshal format.
53
53
  def dump_schema_cache(file)
54
- sch = {}
55
- @schemas.sort.each do |k,v|
56
- sch[k] = v.map do |c, h|
57
- h = Hash[h]
58
- h.delete(:callable_default)
59
- [c, h]
60
- end
61
- end
54
+ sch = dumpable_schema_cache
62
55
  File.open(file, 'wb'){|f| f.write(Marshal.dump(sch))}
63
56
  nil
64
57
  end
@@ -72,7 +65,7 @@ module Sequel
72
65
  # Replace the schema cache with the data from the given file, which
73
66
  # should be in Marshal format.
74
67
  def load_schema_cache(file)
75
- @schemas = Marshal.load(File.read(file))
68
+ @schemas = load_schema_cache_file(file)
76
69
  @schemas.each_value{|v| schema_post_process(v)}
77
70
  nil
78
71
  end
@@ -82,6 +75,28 @@ module Sequel
82
75
  def load_schema_cache?(file)
83
76
  load_schema_cache(file) if File.exist?(file)
84
77
  end
78
+
79
+ private
80
+
81
+ # Return the deserialized schema cache file.
82
+ def load_schema_cache_file(file)
83
+ Marshal.load(File.read(file))
84
+ end
85
+
86
+ # A dumpable version of the schema cache.
87
+ def dumpable_schema_cache
88
+ sch = {}
89
+
90
+ @schemas.sort.each do |k,v|
91
+ sch[k] = v.map do |c, h|
92
+ h = Hash[h]
93
+ h.delete(:callable_default)
94
+ [c, h]
95
+ end
96
+ end
97
+
98
+ sch
99
+ end
85
100
  end
86
101
 
87
102
  Database.register_extension(:schema_caching, SchemaCaching)
@@ -27,6 +27,11 @@ Sequel.extension :eval_inspect
27
27
 
28
28
  module Sequel
29
29
  module SchemaDumper
30
+ # :nocov:
31
+ IGNORE_INDEX_ERRORS_KEY = RUBY_VERSION >= '3.4' ? 'ignore_index_errors: ' : ':ignore_index_errors=>'
32
+ # :nocov:
33
+ private_constant :IGNORE_INDEX_ERRORS_KEY
34
+
30
35
  # Convert the column schema information to a hash of column options, one of which must
31
36
  # be :type. The other options added should modify that type (e.g. :size). If a
32
37
  # database type is not recognized, return it as a String type.
@@ -161,7 +166,7 @@ END_MIG
161
166
  def dump_table_schema(table, options=OPTS)
162
167
  gen = dump_table_generator(table, options)
163
168
  commands = [gen.dump_columns, gen.dump_constraints, gen.dump_indexes].reject{|x| x == ''}.join("\n\n")
164
- "create_table(#{table.inspect}#{', :ignore_index_errors=>true' if !options[:same_db] && options[:indexes] != false && !gen.indexes.empty?}) do\n#{commands.gsub(/^/, ' ')}\nend"
169
+ "create_table(#{table.inspect}#{", #{IGNORE_INDEX_ERRORS_KEY}true" if !options[:same_db] && options[:indexes] != false && !gen.indexes.empty?}) do\n#{commands.gsub(/^/, ' ')}\nend"
165
170
  end
166
171
 
167
172
  private
@@ -425,6 +430,13 @@ END_MIG
425
430
 
426
431
  module Schema
427
432
  class CreateTableGenerator
433
+ # :nocov:
434
+ DEFAULT_KEY = RUBY_VERSION >= '3.4' ? 'default: ' : ':default=>'
435
+ IGNORE_ERRORS_KEY = RUBY_VERSION >= '3.4' ? 'ignore_errors: ' : ':ignore_errors=>'
436
+ # :nocov:
437
+ private_constant :DEFAULT_KEY
438
+ private_constant :IGNORE_ERRORS_KEY
439
+
428
440
  # Dump this generator's columns to a string that could be evaled inside
429
441
  # another instance to represent the same columns
430
442
  def dump_columns
@@ -508,7 +520,7 @@ END_MIG
508
520
  c = c.dup
509
521
  cols = c.delete(:columns)
510
522
  if table = options[:add_index] || options[:drop_index]
511
- "#{options[:drop_index] ? 'drop' : 'add'}_index #{table.inspect}, #{cols.inspect}#{', :ignore_errors=>true' if options[:ignore_errors]}#{opts_inspect(c)}"
523
+ "#{options[:drop_index] ? 'drop' : 'add'}_index #{table.inspect}, #{cols.inspect}#{", #{IGNORE_ERRORS_KEY}true" if options[:ignore_errors]}#{opts_inspect(c)}"
512
524
  else
513
525
  "index #{cols.inspect}#{opts_inspect(c)}"
514
526
  end
@@ -526,7 +538,7 @@ END_MIG
526
538
  if opts[:default]
527
539
  opts = opts.dup
528
540
  de = Sequel.eval_inspect(opts.delete(:default))
529
- ", :default=>#{de}#{", #{opts.inspect[1...-1]}" if opts.length > 0}"
541
+ ", #{DEFAULT_KEY}#{de}#{", #{opts.inspect[1...-1]}" if opts.length > 0}"
530
542
  else
531
543
  ", #{opts.inspect[1...-1]}" if opts.length > 0
532
544
  end
@@ -35,7 +35,7 @@
35
35
  #
36
36
  # j[1] # (json_column ->> 1)
37
37
  # j.get(1) # (json_column ->> 1)
38
- # j.get_text(1) # (json_column -> 1)
38
+ # j.get_json(1) # (json_column -> 1)
39
39
  # j.extract('$.a') # json_extract(json_column, '$.a')
40
40
  # jb.extract('$.a') # jsonb_extract(jsonb_column, '$.a')
41
41
  #
@@ -87,7 +87,7 @@ module Sequel
87
87
  attr_reader :simple_pk
88
88
 
89
89
  # Should be the literal table name if this Model's dataset is a simple table (no select, order, join, etc.),
90
- # or nil otherwise. This and simple_pk are used for an optimization in Model.[].
90
+ # or nil otherwise. This and simple_pk are used for an optimization in Model[].
91
91
  attr_reader :simple_table
92
92
 
93
93
  # Whether mass assigning via .create/.new/#set/#update should raise an error
@@ -398,7 +398,7 @@ module Sequel
398
398
  end
399
399
 
400
400
  # Finds a single record according to the supplied filter.
401
- # You are encouraged to use Model.[] or Model.first instead of this method.
401
+ # You are encouraged to use Model[] or Model.first instead of this method.
402
402
  #
403
403
  # Artist.find(name: 'Bob')
404
404
  # # SELECT * FROM artists WHERE (name = 'Bob') LIMIT 1
@@ -1311,7 +1311,7 @@ module Sequel
1311
1311
  # Returns a string representation of the model instance including
1312
1312
  # the class name and values.
1313
1313
  def inspect
1314
- "#<#{model.name} @values=#{inspect_values}>"
1314
+ "#<#{inspect_prefix} @values=#{inspect_values}>"
1315
1315
  end
1316
1316
 
1317
1317
  # Returns the keys in +values+. May not include all column names.
@@ -1994,7 +1994,12 @@ module Sequel
1994
1994
  set(h) unless h.empty?
1995
1995
  end
1996
1996
 
1997
- # Default inspection output for the values hash, overwrite to change what #inspect displays.
1997
+ # Default inspect output for the inspect, by default, just showing the class.
1998
+ def inspect_prefix
1999
+ model.name
2000
+ end
2001
+
2002
+ # Default inspect output for the values hash, overwrite to change what #inspect displays.
1998
2003
  def inspect_values
1999
2004
  @values.inspect
2000
2005
  end
@@ -0,0 +1,44 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # The inspect_pk plugin includes the pk right next to the
6
+ # model name in inspect, allowing for easily copying and
7
+ # pasting to retrieve a copy of the object:
8
+ #
9
+ # Album.with_pk(1).inspect
10
+ # # default: #<Album @values={...}>
11
+ # # with inspect_pk: #<Album[1] @values={...}>
12
+ #
13
+ # Usage:
14
+ #
15
+ # # Make all model instances include pk in inspect output
16
+ # Sequel::Model.plugin :inspect_pk
17
+ #
18
+ # # Make Album instances include pk in inspect output
19
+ # Album.plugin :inspect_pk
20
+ module InspectPk
21
+ module InstanceMethods
22
+ private
23
+
24
+ # The primary key value to include in the inspect output, if any.
25
+ # For composite primary keys, this only includes a value if all
26
+ # fields are present.
27
+ def inspect_pk
28
+ if primary_key && (pk = self.pk) && (!(Array === pk) || pk.all?)
29
+ pk
30
+ end
31
+ end
32
+
33
+ # Include the instance's primary key in the output.
34
+ def inspect_prefix
35
+ if v = inspect_pk
36
+ "#{super}[#{v.inspect}]"
37
+ else
38
+ super
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -3,18 +3,31 @@
3
3
  module Sequel
4
4
  module Plugins
5
5
  # The subset_conditions plugin creates an additional *_conditions method
6
- # for every subset created, which returns the filter conditions the subset
7
- # uses. This can be useful if you want to use the conditions for a separate
8
- # filter or combine them with OR.
6
+ # for every `subset`, `where`, and `exclude` method call in a dataset_module
7
+ # block. This method returns the filter conditions, which can be useful if
8
+ # you want to use the conditions for a separate filter or combine them with OR.
9
+ # It also supports where_all and where_any dataset_module methods for
10
+ # combining multiple dataset method filters with AND or OR.
9
11
  #
10
12
  # Usage:
11
13
  #
12
14
  # # Add subset_conditions in the Album class
13
15
  # Album.plugin :subset_conditions
14
16
  #
15
- # # This will now create a published_conditions method
16
17
  # Album.dataset_module do
17
- # subset :published, published: true
18
+ # # This will now create a published_conditions method
19
+ # where :published, published: true
20
+ #
21
+ # # This will now create a not_bad_conditions method
22
+ # exclude :not_bad, :bad
23
+ #
24
+ # # This will create good_and_available and
25
+ # # good_and_available_conditions methods
26
+ # where_all :good_and_available, :published, :not_bad
27
+ #
28
+ # # This will create good_or_available and
29
+ # # good_or_available_conditions methods
30
+ # where_any :good_or_available, :published, :not_bad
18
31
  # end
19
32
  #
20
33
  # Album.where(Album.published_conditions).sql
@@ -25,6 +38,12 @@ module Sequel
25
38
  #
26
39
  # Album.where(Album.published_conditions | {ready: true}).sql
27
40
  # # SELECT * FROM albums WHERE ((published IS TRUE) OR (ready IS TRUE))
41
+ #
42
+ # Album.good_and_available.sql
43
+ # SELECT * FROM albums WHERE ((published IS TRUE) AND NOT bad)
44
+ #
45
+ # Album.good_or_available.sql
46
+ # SELECT * FROM albums WHERE ((published IS TRUE) OR NOT bad)
28
47
  module SubsetConditions
29
48
  def self.apply(model, &block)
30
49
  model.instance_exec do
@@ -42,6 +61,66 @@ module Sequel
42
61
  cond = cond.first if cond.size == 1
43
62
  define_method(:"#{name}_conditions"){filter_expr(cond, &block)}
44
63
  end
64
+
65
+ # Also create a method that returns the conditions the filter uses.
66
+ def exclude(name, *args, &block)
67
+ super
68
+ cond = args
69
+ cond = cond.first if cond.size == 1
70
+ define_method(:"#{name}_conditions"){Sequel.~(filter_expr(cond, &block))}
71
+ end
72
+
73
+ # Create a method that combines filters from already registered
74
+ # dataset methods, and filters for rows where all of the conditions
75
+ # are satisfied.
76
+ #
77
+ # Employee.dataset_module do
78
+ # where :active, active: true
79
+ # where :started, Sequel::CURRENT_DATE <= :start_date
80
+ # where_all(:active_and_started, :active, :started)
81
+ # end
82
+ #
83
+ # Employee.active_and_started.sql
84
+ # # SELECT * FROM employees WHERE ((active IS TRUE) AND (CURRENT_DATE <= start_date))
85
+ def where_all(name, *args)
86
+ _where_any_all(:&, name, args)
87
+ end
88
+
89
+ # Create a method that combines filters from already registered
90
+ # dataset methods, and filters for rows where any of the conditions
91
+ # are satisfied.
92
+ #
93
+ # Employee.dataset_module do
94
+ # where :active, active: true
95
+ # where :started, Sequel::CURRENT_DATE <= :start_date
96
+ # where_any(:active_or_started, :active, :started)
97
+ # end
98
+ #
99
+ # Employee.active_or_started.sql
100
+ # # SELECT * FROM employees WHERE ((active IS TRUE) OR (CURRENT_DATE <= start_date))
101
+ def where_any(name, *args)
102
+ _where_any_all(:|, name, args)
103
+ end
104
+
105
+ private
106
+
107
+ if RUBY_VERSION >= '2'
108
+ # Backbone of #where_any and #where_all
109
+ def _where_any_all(meth, name, args)
110
+ ds = model.dataset
111
+ # #bind used here because the dataset module may not yet be included in the model's dataset
112
+ where(name, Sequel.send(meth, *args.map{|a| self.instance_method(:"#{a}_conditions").bind(ds).call}))
113
+ end
114
+ else
115
+ # Cannot bind module method to arbitrary objects in Ruby 1.9.
116
+ # :nocov:
117
+ def _where_any_all(meth, name, args)
118
+ ds = model.dataset.clone
119
+ ds.extend(self)
120
+ where(name, Sequel.send(meth, *args.map{|a| ds.send(:"#{a}_conditions")}))
121
+ end
122
+ # :nocov:
123
+ end
45
124
  end
46
125
  end
47
126
  end
@@ -6,7 +6,7 @@ module Sequel
6
6
 
7
7
  # The minor version of Sequel. Bumped for every non-patch level
8
8
  # release, generally around once a month.
9
- MINOR = 85
9
+ MINOR = 87
10
10
 
11
11
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
12
12
  # releases that fix regressions from previous versions.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.85.0
4
+ version: 5.87.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-01 00:00:00.000000000 Z
11
+ date: 2024-12-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bigdecimal
@@ -268,6 +268,7 @@ files:
268
268
  - lib/sequel/extensions/pg_range_ops.rb
269
269
  - lib/sequel/extensions/pg_row.rb
270
270
  - lib/sequel/extensions/pg_row_ops.rb
271
+ - lib/sequel/extensions/pg_schema_caching.rb
271
272
  - lib/sequel/extensions/pg_static_cache_updater.rb
272
273
  - lib/sequel/extensions/pg_timestamptz.rb
273
274
  - lib/sequel/extensions/pretty_table.rb
@@ -353,6 +354,7 @@ files:
353
354
  - lib/sequel/plugins/input_transformer.rb
354
355
  - lib/sequel/plugins/insert_conflict.rb
355
356
  - lib/sequel/plugins/insert_returning_select.rb
357
+ - lib/sequel/plugins/inspect_pk.rb
356
358
  - lib/sequel/plugins/instance_filters.rb
357
359
  - lib/sequel/plugins/instance_hooks.rb
358
360
  - lib/sequel/plugins/instance_specific_default.rb
@@ -444,7 +446,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
444
446
  - !ruby/object:Gem::Version
445
447
  version: '0'
446
448
  requirements: []
447
- rubygems_version: 3.5.16
449
+ rubygems_version: 3.5.22
448
450
  signing_key:
449
451
  specification_version: 4
450
452
  summary: The Database Toolkit for Ruby