sequel 5.85.0 → 5.87.0

Sign up to get free protection for your applications and to get access to all the features.
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