sequel 4.42.1 → 4.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +35 -1
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/doc/release_notes/4.43.0.txt +87 -0
  6. data/doc/sql.rdoc +26 -27
  7. data/doc/testing.rdoc +2 -0
  8. data/doc/validations.rdoc +1 -1
  9. data/lib/sequel/adapters/ado.rb +5 -0
  10. data/lib/sequel/adapters/cubrid.rb +5 -0
  11. data/lib/sequel/adapters/ibmdb.rb +5 -0
  12. data/lib/sequel/adapters/jdbc.rb +6 -0
  13. data/lib/sequel/adapters/jdbc/derby.rb +5 -0
  14. data/lib/sequel/adapters/jdbc/hsqldb.rb +9 -5
  15. data/lib/sequel/adapters/jdbc/postgresql.rb +1 -1
  16. data/lib/sequel/adapters/jdbc/sqlite.rb +5 -0
  17. data/lib/sequel/adapters/jdbc/transactions.rb +5 -0
  18. data/lib/sequel/adapters/mock.rb +12 -9
  19. data/lib/sequel/adapters/mysql.rb +6 -0
  20. data/lib/sequel/adapters/mysql2.rb +7 -2
  21. data/lib/sequel/adapters/oracle.rb +5 -0
  22. data/lib/sequel/adapters/shared/db2.rb +7 -1
  23. data/lib/sequel/adapters/shared/mssql.rb +5 -0
  24. data/lib/sequel/adapters/shared/mysql.rb +8 -1
  25. data/lib/sequel/adapters/shared/oracle.rb +20 -12
  26. data/lib/sequel/adapters/shared/postgres.rb +11 -2
  27. data/lib/sequel/adapters/shared/sqlanywhere.rb +6 -0
  28. data/lib/sequel/adapters/shared/sqlite.rb +29 -0
  29. data/lib/sequel/adapters/sqlanywhere.rb +5 -0
  30. data/lib/sequel/adapters/sqlite.rb +13 -0
  31. data/lib/sequel/connection_pool/sharded_single.rb +5 -0
  32. data/lib/sequel/connection_pool/sharded_threaded.rb +5 -0
  33. data/lib/sequel/connection_pool/single.rb +15 -6
  34. data/lib/sequel/database/dataset.rb +3 -0
  35. data/lib/sequel/database/misc.rb +22 -1
  36. data/lib/sequel/database/query.rb +2 -4
  37. data/lib/sequel/dataset/actions.rb +0 -1
  38. data/lib/sequel/dataset/misc.rb +2 -4
  39. data/lib/sequel/dataset/query.rb +23 -6
  40. data/lib/sequel/extensions/_model_constraint_validations.rb +16 -0
  41. data/lib/sequel/extensions/_model_pg_row.rb +47 -0
  42. data/lib/sequel/extensions/looser_typecasting.rb +2 -0
  43. data/lib/sequel/extensions/migration.rb +12 -1
  44. data/lib/sequel/extensions/pg_array.rb +6 -0
  45. data/lib/sequel/extensions/pg_enum.rb +2 -1
  46. data/lib/sequel/extensions/pg_range.rb +6 -0
  47. data/lib/sequel/extensions/pg_row.rb +8 -0
  48. data/lib/sequel/model/associations.rb +3 -1
  49. data/lib/sequel/model/base.rb +14 -3
  50. data/lib/sequel/plugins/constraint_validations.rb +1 -8
  51. data/lib/sequel/plugins/instance_filters.rb +1 -1
  52. data/lib/sequel/plugins/pg_row.rb +1 -40
  53. data/lib/sequel/plugins/prepared_statements.rb +51 -20
  54. data/lib/sequel/plugins/prepared_statements_associations.rb +22 -4
  55. data/lib/sequel/plugins/prepared_statements_with_pk.rb +9 -1
  56. data/lib/sequel/plugins/sharding.rb +5 -0
  57. data/lib/sequel/plugins/update_primary_key.rb +1 -1
  58. data/lib/sequel/version.rb +2 -2
  59. data/spec/adapters/spec_helper.rb +4 -0
  60. data/spec/core/connection_pool_spec.rb +10 -0
  61. data/spec/core/database_spec.rb +29 -0
  62. data/spec/extensions/blacklist_security_spec.rb +4 -4
  63. data/spec/extensions/defaults_setter_spec.rb +1 -1
  64. data/spec/extensions/force_encoding_spec.rb +3 -2
  65. data/spec/extensions/identifier_mangling_spec.rb +7 -0
  66. data/spec/extensions/instance_filters_spec.rb +1 -0
  67. data/spec/extensions/migration_spec.rb +19 -0
  68. data/spec/extensions/pg_array_spec.rb +5 -0
  69. data/spec/extensions/pg_range_spec.rb +5 -0
  70. data/spec/extensions/pg_row_spec.rb +9 -0
  71. data/spec/extensions/prepared_statements_associations_spec.rb +45 -1
  72. data/spec/extensions/prepared_statements_spec.rb +138 -41
  73. data/spec/extensions/prepared_statements_with_pk_spec.rb +7 -0
  74. data/spec/extensions/serialization_spec.rb +6 -6
  75. data/spec/extensions/single_table_inheritance_spec.rb +3 -3
  76. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  77. data/spec/integration/associations_test.rb +2 -2
  78. data/spec/integration/dataset_test.rb +0 -4
  79. data/spec/integration/eager_loader_test.rb +5 -5
  80. data/spec/integration/plugin_test.rb +8 -6
  81. data/spec/integration/schema_test.rb +2 -2
  82. data/spec/integration/spec_helper.rb +10 -0
  83. data/spec/integration/timezone_test.rb +1 -1
  84. data/spec/integration/transaction_test.rb +5 -5
  85. data/spec/model/associations_spec.rb +13 -6
  86. data/spec/model/base_spec.rb +1 -1
  87. data/spec/model/hooks_spec.rb +4 -4
  88. data/spec/model/model_spec.rb +2 -2
  89. data/spec/model/record_spec.rb +17 -18
  90. metadata +6 -2
@@ -283,6 +283,12 @@ module Sequel
283
283
  execute(sql, opts)
284
284
  end
285
285
 
286
+ def freeze
287
+ @type_convertor_map.freeze
288
+ @basic_type_convertor_map.freeze
289
+ super
290
+ end
291
+
286
292
  # Use the JDBC metadata to get a list of foreign keys for the table.
287
293
  def foreign_key_list(table, opts=OPTS)
288
294
  m = output_identifier_meth
@@ -34,6 +34,11 @@ module Sequel
34
34
  :derby
35
35
  end
36
36
 
37
+ def freeze
38
+ svn_version
39
+ super
40
+ end
41
+
37
42
  # Derby uses an IDENTITY sequence for autoincrementing columns.
38
43
  def serial_primary_key_options
39
44
  {:primary_key => true, :type => :integer, :identity=>true, :start_with=>1}
@@ -26,6 +26,11 @@ module Sequel
26
26
  :hsqldb
27
27
  end
28
28
 
29
+ def freeze
30
+ db_version
31
+ super
32
+ end
33
+
29
34
  # HSQLDB uses an IDENTITY sequence as the default value for primary
30
35
  # key columns.
31
36
  def serial_primary_key_options
@@ -34,11 +39,10 @@ module Sequel
34
39
 
35
40
  # The version of the database, as an integer (e.g 2.2.5 -> 20205)
36
41
  def db_version
37
- @db_version ||= begin
38
- v = get{DATABASE_VERSION(){}}
39
- if v =~ /(\d+)\.(\d+)\.(\d+)/
40
- $1.to_i * 10000 + $2.to_i * 100 + $3.to_i
41
- end
42
+ return @db_version if defined?(@db_version)
43
+ v = get{DATABASE_VERSION(){}}
44
+ @db_version = if v =~ /(\d+)\.(\d+)\.(\d+)/
45
+ $1.to_i * 10000 + $2.to_i * 100 + $3.to_i
42
46
  end
43
47
  end
44
48
 
@@ -9,8 +9,8 @@ module Sequel
9
9
  module JDBC
10
10
  Sequel.synchronize do
11
11
  DATABASE_SETUP[:postgresql] = proc do |db|
12
- db.extend(Sequel::JDBC::Postgres::DatabaseMethods)
13
12
  db.dataset_class = Sequel::JDBC::Postgres::Dataset
13
+ db.extend(Sequel::JDBC::Postgres::DatabaseMethods)
14
14
  org.postgresql.Driver
15
15
  end
16
16
  end
@@ -90,6 +90,11 @@ module Sequel
90
90
  end
91
91
  end
92
92
  end
93
+
94
+ # The result code for the exception, if the jdbc driver supports result codes for exceptions.
95
+ def sqlite_error_code(exception)
96
+ exception.resultCode.code if exception.respond_to?(:resultCode)
97
+ end
93
98
  end
94
99
  end
95
100
  end
@@ -10,6 +10,11 @@ module Sequel
10
10
  TRANSACTION_ROLLBACK_SP = 'Transaction.rollback_savepoint'.freeze
11
11
  TRANSACTION_SAVEPOINT= 'Transaction.savepoint'.freeze
12
12
 
13
+ def freeze
14
+ supports_savepoints?
15
+ super
16
+ end
17
+
13
18
  # Check the JDBC DatabaseMetaData for savepoint support
14
19
  def supports_savepoints?
15
20
  return @supports_savepoints if defined?(@supports_savepoints)
@@ -48,7 +48,15 @@ module Sequel
48
48
  # the value returned
49
49
  # Class :: Should be an Exception subclass, will create a new
50
50
  # instance an raise it wrapped in a DatabaseError.
51
- attr_writer :autoid
51
+ def autoid=(v)
52
+ @autoid = case v
53
+ when Integer
54
+ i = v - 1
55
+ proc{Sequel.synchronize{i+=1}}
56
+ else
57
+ v
58
+ end
59
+ end
52
60
 
53
61
  # Set the columns to set in the dataset when the dataset fetches
54
62
  # rows. Argument types supported:
@@ -133,13 +141,8 @@ module Sequel
133
141
  private
134
142
 
135
143
  def _autoid(sql, v, ds=nil)
136
- case v
137
- when Integer
138
- if ds
139
- ds.send(:cache_set, :_autoid, ds.autoid + 1) if ds.autoid.is_a?(Integer)
140
- else
141
- @autoid += 1
142
- end
144
+ if ds
145
+ ds.send(:cache_set, :_autoid, ds.autoid + 1) if ds.autoid.is_a?(Integer)
143
146
  v
144
147
  else
145
148
  _nextres(v, sql, nil)
@@ -192,7 +195,7 @@ module Sequel
192
195
  if f < Exception
193
196
  raise f
194
197
  else
195
- raise Error, "Invalid @autoid/@numrows attribute: #{v.inspect}"
198
+ raise Error, "Invalid @fetch attribute: #{v.inspect}"
196
199
  end
197
200
  when nil
198
201
  # nothing
@@ -163,6 +163,12 @@ module Sequel
163
163
  execute(sql, opts){|c| return c.insert_id}
164
164
  end
165
165
 
166
+ def freeze
167
+ server_version
168
+ @conversion_procs.freeze
169
+ super
170
+ end
171
+
166
172
  # Return the version of the MySQL server two which we are connecting.
167
173
  def server_version(server=nil)
168
174
  @server_version ||= (synchronize(server){|conn| conn.server_version if conn.respond_to?(:server_version)} || super)
@@ -47,7 +47,7 @@ module Sequel
47
47
  conn.query_options.merge!(:symbolize_keys=>true, :cache_rows=>false)
48
48
 
49
49
  if NativePreparedStatements
50
- @default_query_options ||= conn.query_options.dup
50
+ conn.instance_variable_set(:@sequel_default_query_options, conn.query_options.dup)
51
51
  end
52
52
 
53
53
  sqls = mysql_connection_setting_sqls
@@ -76,6 +76,11 @@ module Sequel
76
76
  execute(sql, opts){|c| return c.last_id}
77
77
  end
78
78
 
79
+ def freeze
80
+ server_version
81
+ super
82
+ end
83
+
79
84
  # Return the version of the MySQL server to which we are connecting.
80
85
  def server_version(server=nil)
81
86
  @server_version ||= (synchronize(server){|conn| conn.server_info[:id]} || super)
@@ -158,7 +163,7 @@ module Sequel
158
163
  raise_error(e)
159
164
  ensure
160
165
  if stmt
161
- conn.query_options.replace(@default_query_options)
166
+ conn.query_options.replace(conn.instance_variable_get(:@sequel_default_query_options))
162
167
  stmt.close if close_stmt
163
168
  end
164
169
  end
@@ -71,6 +71,11 @@ module Sequel
71
71
  _execute(:insert, sql, opts)
72
72
  end
73
73
 
74
+ def freeze
75
+ @conversion_procs.freeze
76
+ super
77
+ end
78
+
74
79
  private
75
80
 
76
81
  def _execute(type, sql, opts=OPTS, &block)
@@ -26,11 +26,17 @@ module Sequel
26
26
  # Return the database version as a string. Don't rely on this,
27
27
  # it may return an integer in the future.
28
28
  def db2_version
29
- return @db2_version if @db2_version
29
+ return @db2_version if defined?(@db2_version)
30
30
  @db2_version = metadata_dataset.with_sql("select service_level from sysibmadm.env_inst_info").first[:service_level]
31
31
  end
32
32
  alias_method :server_version, :db2_version
33
33
 
34
+ def freeze
35
+ db2_version
36
+ offset_strategy
37
+ super
38
+ end
39
+
34
40
  # Use SYSIBM.SYSCOLUMNS to get the information on the tables.
35
41
  def schema_parse_table(table, opts = OPTS)
36
42
  m = output_identifier_meth(opts[:dataset])
@@ -173,6 +173,11 @@ module Sequel
173
173
  h.values
174
174
  end
175
175
 
176
+ def freeze
177
+ server_version
178
+ super
179
+ end
180
+
176
181
  # Use the system tables to get index information
177
182
  def indexes(table, opts=OPTS)
178
183
  m = output_identifier_meth
@@ -89,6 +89,12 @@ module Sequel
89
89
  h.values
90
90
  end
91
91
 
92
+ def freeze
93
+ server_version
94
+ supports_timestamp_usecs?
95
+ super
96
+ end
97
+
92
98
  # MySQL namespaces indexes per table.
93
99
  def global_index_namespace?
94
100
  false
@@ -155,7 +161,8 @@ module Sequel
155
161
  # automatic initialization of datetime values wasn't supported to 5.6.5+,
156
162
  # and this is related to that.
157
163
  def supports_timestamp_usecs?
158
- @supports_timestamp_usecs ||= server_version >= 50605 && typecast_value_boolean(opts[:fractional_seconds])
164
+ return @supports_timestamp_usecs if defined?(@supports_timestamp_usecs)
165
+ @supports_timestamp_usecs = server_version >= 50605 && typecast_value_boolean(opts[:fractional_seconds])
159
166
  end
160
167
 
161
168
  # MySQL supports transaction isolation levels
@@ -74,6 +74,13 @@ module Sequel
74
74
  fks.values
75
75
  end
76
76
 
77
+ def freeze
78
+ current_user
79
+ server_version
80
+ @conversion_procs.freeze
81
+ super
82
+ end
83
+
77
84
  # Oracle namespaces indexes per table.
78
85
  def global_index_namespace?
79
86
  false
@@ -237,7 +244,7 @@ module Sequel
237
244
  end
238
245
 
239
246
  def remove_cached_schema(table)
240
- @primary_key_sequences.delete(table)
247
+ Sequel.synchronize{@primary_key_sequences.delete(table)}
241
248
  super
242
249
  end
243
250
 
@@ -253,19 +260,20 @@ module Sequel
253
260
 
254
261
  def sequence_for_table(table)
255
262
  return nil unless autosequence
256
- @primary_key_sequences.fetch(table) do |key|
257
- begin
258
- sch = schema(table)
259
- rescue Sequel::Error
260
- return nil
261
- end
263
+ Sequel.synchronize{return @primary_key_sequences[table] if @primary_key_sequences.has_key?(table)}
262
264
 
263
- pk = sch.select{|k, v| v[:primary_key]}
264
- @primary_key_sequences[table] = if pk.length == 1
265
- seq = "seq_#{table}_#{pk.first.first}"
266
- seq.to_sym unless from(:user_sequences).where(:sequence_name=>input_identifier_meth.call(seq)).empty?
267
- end
265
+ begin
266
+ sch = schema(table)
267
+ rescue Sequel::Error
268
+ return nil
269
+ end
270
+
271
+ pk = sch.select{|k, v| v[:primary_key]}
272
+ pks = if pk.length == 1
273
+ seq = "seq_#{table}_#{pk.first.first}"
274
+ seq.to_sym unless from(:user_sequences).where(:sequence_name=>input_identifier_meth.call(seq)).empty?
268
275
  end
276
+ Sequel.synchronize{@primary_key_sequences[table] = pks}
269
277
  end
270
278
 
271
279
  # Oracle supports CREATE OR REPLACE VIEW.
@@ -373,6 +373,13 @@ module Sequel
373
373
  h.values
374
374
  end
375
375
 
376
+ def freeze
377
+ server_version
378
+ supports_prepared_transactions?
379
+ @conversion_procs.freeze
380
+ super
381
+ end
382
+
376
383
  # Use the pg_* system tables to determine indexes on a table
377
384
  def indexes(table, opts=OPTS)
378
385
  m = output_identifier_meth
@@ -580,8 +587,9 @@ module Sequel
580
587
  # Check whether the given type name string/symbol (e.g. :hstore) is supported by
581
588
  # the database.
582
589
  def type_supported?(type)
583
- @supported_types ||= {}
584
- @supported_types.fetch(type){@supported_types[type] = (from(:pg_type).where(:typtype=>'b', :typname=>type.to_s).count > 0)}
590
+ Sequel.synchronize{return @supported_types[type] if @supported_types.has_key?(type)}
591
+ supported = from(:pg_type).where(:typtype=>'b', :typname=>type.to_s).count > 0
592
+ Sequel.synchronize{return @supported_types[type] = supported}
585
593
  end
586
594
 
587
595
  # Creates a dataset that uses the VALUES clause:
@@ -1032,6 +1040,7 @@ module Sequel
1032
1040
  def initialize_postgres_adapter
1033
1041
  @primary_keys = {}
1034
1042
  @primary_key_sequences = {}
1043
+ @supported_types = {}
1035
1044
  @conversion_procs = PG_TYPES.dup
1036
1045
  reset_conversion_procs
1037
1046
  end
@@ -37,6 +37,12 @@ module Sequel
37
37
  :sqlanywhere
38
38
  end
39
39
 
40
+ def freeze
41
+ convert_smallint_to_bool
42
+ @conversion_procs.freeze
43
+ super
44
+ end
45
+
40
46
  def to_application_timestamp_sa(v)
41
47
  to_application_timestamp(v.to_s) if v
42
48
  end
@@ -101,6 +101,12 @@ module Sequel
101
101
  h.values
102
102
  end
103
103
 
104
+ def freeze
105
+ sqlite_version
106
+ use_timestamp_timezones?
107
+ super
108
+ end
109
+
104
110
  # Use the index_list and index_info PRAGMAs to determine the indexes on the table.
105
111
  def indexes(table, opts=OPTS)
106
112
  m = output_identifier_meth
@@ -359,6 +365,24 @@ module Sequel
359
365
  DATABASE_ERROR_REGEXPS
360
366
  end
361
367
 
368
+ # Recognize SQLite error codes if the exception provides access to them.
369
+ def database_specific_error_class(exception, opts)
370
+ case sqlite_error_code(exception)
371
+ when 1299
372
+ NotNullConstraintViolation
373
+ when 2067
374
+ UniqueConstraintViolation
375
+ when 787
376
+ ForeignKeyConstraintViolation
377
+ when 275
378
+ CheckConstraintViolation
379
+ when 19
380
+ ConstraintViolation
381
+ else
382
+ super
383
+ end
384
+ end
385
+
362
386
  # The array of column schema hashes for the current columns in the table
363
387
  def defined_columns_for(table)
364
388
  cols = parse_pragma(table, {})
@@ -497,6 +521,11 @@ module Sequel
497
521
  end
498
522
  end
499
523
 
524
+ # Don't support SQLite error codes for exceptions by default.
525
+ def sqlite_error_code(exception)
526
+ nil
527
+ end
528
+
500
529
  # Backbone of the tables and views support.
501
530
  def tables_and_views(filter, opts)
502
531
  m = output_identifier_meth
@@ -98,6 +98,11 @@ module Sequel
98
98
  end
99
99
  end
100
100
 
101
+ def freeze
102
+ @conversion_procs.freeze
103
+ super
104
+ end
105
+
101
106
  private
102
107
 
103
108
  LAST_INSERT_ID = 'SELECT @@IDENTITY'.freeze
@@ -151,6 +151,11 @@ module Sequel
151
151
  _execute(:insert, sql, opts)
152
152
  end
153
153
 
154
+ def freeze
155
+ @conversion_procs.freeze
156
+ super
157
+ end
158
+
154
159
  # Handle Integer and Float arguments, since SQLite can store timestamps as integers and floats.
155
160
  def to_application_timestamp(s)
156
161
  case s
@@ -269,6 +274,14 @@ module Sequel
269
274
  def database_error_classes
270
275
  [SQLite3::Exception, ArgumentError]
271
276
  end
277
+
278
+ # Support SQLite exception codes if ruby-sqlite3 supports them.
279
+ # This is disabled by default because ruby-sqlite3 doesn't currently
280
+ # support them (returning nil), and even if it did, it doesn't support
281
+ # extended error codes, which would lead to worse behavior.
282
+ #def sqlite_error_code(exception)
283
+ # exception.code if exception.respond_to?(:code)
284
+ #end
272
285
  end
273
286
 
274
287
  # Dataset class for SQLite datasets that use the ruby-sqlite3 driver.
@@ -43,6 +43,11 @@ class Sequel::ShardedSingleConnectionPool < Sequel::ConnectionPool
43
43
  def disconnect(opts=OPTS)
44
44
  (opts[:server] ? Array(opts[:server]) : servers).each{|s| disconnect_server(s)}
45
45
  end
46
+
47
+ def freeze
48
+ @servers.freeze
49
+ super
50
+ end
46
51
 
47
52
  # Yields the connection to the supplied block for the given server.
48
53
  # This method simulates the ConnectionPool#hold API.