sequel 3.44.0 → 3.45.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 (97) hide show
  1. data/CHANGELOG +44 -0
  2. data/Rakefile +12 -4
  3. data/doc/reflection.rdoc +3 -3
  4. data/doc/release_notes/3.45.0.txt +179 -0
  5. data/doc/schema_modification.rdoc +1 -1
  6. data/doc/transactions.rdoc +23 -0
  7. data/lib/sequel/adapters/db2.rb +1 -0
  8. data/lib/sequel/adapters/ibmdb.rb +19 -3
  9. data/lib/sequel/adapters/jdbc.rb +15 -0
  10. data/lib/sequel/adapters/jdbc/derby.rb +1 -5
  11. data/lib/sequel/adapters/jdbc/h2.rb +1 -0
  12. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -1
  13. data/lib/sequel/adapters/jdbc/jtds.rb +5 -0
  14. data/lib/sequel/adapters/jdbc/mysql.rb +5 -0
  15. data/lib/sequel/adapters/jdbc/oracle.rb +7 -1
  16. data/lib/sequel/adapters/jdbc/sqlite.rb +1 -1
  17. data/lib/sequel/adapters/jdbc/transactions.rb +28 -1
  18. data/lib/sequel/adapters/mysql.rb +4 -0
  19. data/lib/sequel/adapters/mysql2.rb +5 -1
  20. data/lib/sequel/adapters/oracle.rb +18 -0
  21. data/lib/sequel/adapters/postgres.rb +11 -1
  22. data/lib/sequel/adapters/shared/access.rb +14 -2
  23. data/lib/sequel/adapters/shared/cubrid.rb +1 -11
  24. data/lib/sequel/adapters/shared/db2.rb +11 -6
  25. data/lib/sequel/adapters/shared/mssql.rb +10 -10
  26. data/lib/sequel/adapters/shared/mysql.rb +11 -1
  27. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +17 -1
  28. data/lib/sequel/adapters/shared/oracle.rb +16 -15
  29. data/lib/sequel/adapters/shared/postgres.rb +91 -59
  30. data/lib/sequel/adapters/shared/sqlite.rb +1 -4
  31. data/lib/sequel/adapters/tinytds.rb +15 -0
  32. data/lib/sequel/connection_pool/threaded.rb +1 -1
  33. data/lib/sequel/core.rb +10 -0
  34. data/lib/sequel/database/connecting.rb +2 -0
  35. data/lib/sequel/database/misc.rb +46 -4
  36. data/lib/sequel/database/query.rb +33 -14
  37. data/lib/sequel/database/schema_methods.rb +0 -5
  38. data/lib/sequel/dataset/misc.rb +9 -0
  39. data/lib/sequel/dataset/mutation.rb +9 -7
  40. data/lib/sequel/dataset/sql.rb +13 -0
  41. data/lib/sequel/exceptions.rb +3 -0
  42. data/lib/sequel/extensions/connection_validator.rb +1 -1
  43. data/lib/sequel/extensions/date_arithmetic.rb +0 -8
  44. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  45. data/lib/sequel/extensions/named_timezones.rb +18 -2
  46. data/lib/sequel/extensions/pg_array.rb +5 -1
  47. data/lib/sequel/extensions/pg_array_ops.rb +2 -0
  48. data/lib/sequel/extensions/pg_hstore.rb +2 -0
  49. data/lib/sequel/extensions/pg_hstore_ops.rb +2 -0
  50. data/lib/sequel/extensions/pg_json.rb +3 -1
  51. data/lib/sequel/extensions/pg_range.rb +2 -0
  52. data/lib/sequel/extensions/pg_range_ops.rb +2 -0
  53. data/lib/sequel/extensions/pg_row.rb +2 -0
  54. data/lib/sequel/extensions/pg_row_ops.rb +2 -0
  55. data/lib/sequel/extensions/query.rb +18 -22
  56. data/lib/sequel/model/associations.rb +3 -4
  57. data/lib/sequel/model/base.rb +2 -0
  58. data/lib/sequel/plugins/force_encoding.rb +2 -0
  59. data/lib/sequel/plugins/json_serializer.rb +155 -39
  60. data/lib/sequel/plugins/serialization.rb +14 -2
  61. data/lib/sequel/plugins/unlimited_update.rb +31 -0
  62. data/lib/sequel/plugins/validation_class_methods.rb +6 -4
  63. data/lib/sequel/plugins/xml_serializer.rb +133 -30
  64. data/lib/sequel/sql.rb +2 -0
  65. data/lib/sequel/timezones.rb +4 -0
  66. data/lib/sequel/version.rb +1 -1
  67. data/spec/adapters/mysql_spec.rb +0 -11
  68. data/spec/adapters/postgres_spec.rb +86 -54
  69. data/spec/adapters/spec_helper.rb +6 -0
  70. data/spec/core/connection_pool_spec.rb +16 -0
  71. data/spec/core/database_spec.rb +77 -1
  72. data/spec/core/dataset_spec.rb +30 -15
  73. data/spec/core/expression_filters_spec.rb +55 -13
  74. data/spec/core/mock_adapter_spec.rb +4 -0
  75. data/spec/core/schema_spec.rb +0 -2
  76. data/spec/core/spec_helper.rb +5 -0
  77. data/spec/core_extensions_spec.rb +33 -28
  78. data/spec/extensions/constraint_validations_spec.rb +2 -2
  79. data/spec/extensions/core_refinements_spec.rb +12 -12
  80. data/spec/extensions/json_serializer_spec.rb +137 -31
  81. data/spec/extensions/named_timezones_spec.rb +10 -0
  82. data/spec/extensions/pg_auto_parameterize_spec.rb +5 -0
  83. data/spec/extensions/pg_json_spec.rb +14 -0
  84. data/spec/extensions/pg_row_spec.rb +11 -0
  85. data/spec/extensions/pretty_table_spec.rb +2 -2
  86. data/spec/extensions/query_spec.rb +11 -8
  87. data/spec/extensions/serialization_spec.rb +20 -0
  88. data/spec/extensions/spec_helper.rb +8 -2
  89. data/spec/extensions/sql_expr_spec.rb +1 -1
  90. data/spec/extensions/unlimited_update_spec.rb +20 -0
  91. data/spec/extensions/xml_serializer_spec.rb +68 -16
  92. data/spec/integration/dataset_test.rb +28 -0
  93. data/spec/integration/spec_helper.rb +6 -0
  94. data/spec/integration/transaction_test.rb +39 -0
  95. data/spec/model/model_spec.rb +1 -1
  96. data/spec/sequel_coverage.rb +15 -0
  97. metadata +8 -3
@@ -54,6 +54,11 @@ module Sequel
54
54
  true
55
55
  end
56
56
 
57
+ # DB2 supports transaction isolation levels.
58
+ def supports_transaction_isolation_levels?
59
+ true
60
+ end
61
+
57
62
  private
58
63
 
59
64
  # Handle Oracle specific ALTER TABLE SQL
@@ -141,6 +146,7 @@ module Sequel
141
146
  /integrity constraint .+ violated/ => ForeignKeyConstraintViolation,
142
147
  /check constraint .+ violated/ => CheckConstraintViolation,
143
148
  /cannot insert NULL into|cannot update .+ to NULL/ => NotNullConstraintViolation,
149
+ /can't serialize access for this transaction/ => SerializationFailure,
144
150
  }.freeze
145
151
  def database_error_regexps
146
152
  DATABASE_ERROR_REGEXPS
@@ -159,6 +165,16 @@ module Sequel
159
165
  super
160
166
  end
161
167
 
168
+ TRANSACTION_ISOLATION_LEVELS = {:uncommitted=>'READ COMMITTED'.freeze,
169
+ :committed=>'READ COMMITTED'.freeze,
170
+ :repeatable=>'SERIALIZABLE'.freeze,
171
+ :serializable=>'SERIALIZABLE'.freeze}
172
+ # Oracle doesn't support READ UNCOMMITTED OR REPEATABLE READ transaction
173
+ # isolation levels, so upgrade to the next highest level in those cases.
174
+ def set_transaction_isolation_sql(level)
175
+ "SET TRANSACTION ISOLATION LEVEL #{TRANSACTION_ISOLATION_LEVELS[level]}"
176
+ end
177
+
162
178
  def sequence_for_table(table)
163
179
  return nil unless autosequence
164
180
  @primary_key_sequences.fetch(table) do |key|
@@ -217,12 +233,6 @@ module Sequel
217
233
  FROM = Dataset::FROM
218
234
  BITCOMP_OPEN = "((0 - ".freeze
219
235
  BITCOMP_CLOSE = ") - 1)".freeze
220
- ILIKE_0 = "(UPPER(".freeze
221
- ILIKE_1 = ") ".freeze
222
- ILIKE_2 = ' UPPER('.freeze
223
- ILIKE_3 = "))".freeze
224
- LIKE = 'LIKE'.freeze
225
- NOT_LIKE = 'NOT LIKE'.freeze
226
236
  TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S%N %z'".freeze
227
237
  TIMESTAMP_OFFSET_FORMAT = "%+03i:%02i".freeze
228
238
  BOOL_FALSE = "'N'".freeze
@@ -230,7 +240,6 @@ module Sequel
230
240
  HSTAR = "H*".freeze
231
241
  DUAL = ['DUAL'.freeze].freeze
232
242
 
233
- # Oracle needs to emulate bitwise operators and ILIKE/NOT ILIKE operators.
234
243
  def complex_expression_sql_append(sql, op, args)
235
244
  case op
236
245
  when :&
@@ -249,14 +258,6 @@ module Sequel
249
258
  sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} / power(2, #{literal b}))"}
250
259
  when :%
251
260
  sql << complex_expression_arg_pairs(args){|a, b| "MOD(#{literal(a)}, #{literal(b)})"}
252
- when :ILIKE, :'NOT ILIKE'
253
- sql << ILIKE_0
254
- literal_append(sql, args.at(0))
255
- sql << ILIKE_1
256
- sql << (op == :ILIKE ? LIKE : NOT_LIKE)
257
- sql<< ILIKE_2
258
- literal_append(sql, args.at(1))
259
- sql << ILIKE_3
260
261
  else
261
262
  super
262
263
  end
@@ -4,15 +4,16 @@ module Sequel
4
4
  # Top level module for holding all PostgreSQL-related modules and classes
5
5
  # for Sequel. There are a few module level accessors that are added via
6
6
  # metaprogramming. These are:
7
- # * client_min_messages (only available when using the native adapter) -
8
- # Change the minimum level of messages that PostgreSQL will send to the
9
- # the client. The PostgreSQL default is NOTICE, the Sequel default is
10
- # WARNING. Set to nil to not change the server default.
11
- # * force_standard_strings - Set to false to not force the use of
12
- # standard strings
13
- # * use_iso_date_format (only available when using the native adapter) -
14
- # Set to false to not change the date format to
15
- # ISO. This disables one of Sequel's optimizations.
7
+ #
8
+ # client_min_messages :: Change the minimum level of messages that PostgreSQL will send to the
9
+ # the client. The PostgreSQL default is NOTICE, the Sequel default is
10
+ # WARNING. Set to nil to not change the server default. Overridable on
11
+ # a per instance basis via the :client_min_messages option.
12
+ # force_standard_strings :: Set to false to not force the use of standard strings. Overridable
13
+ # on a per instance basis via the :force_standard_strings option.
14
+ # use_iso_date_format :: (only available when using the native adapter)
15
+ # Set to false to not change the date format to
16
+ # ISO. This disables one of Sequel's optimizations.
16
17
  #
17
18
  # Changes in these settings only affect future connections. To make
18
19
  # sure that they are applied, they should generally be called right
@@ -290,14 +291,13 @@ module Sequel
290
291
  # :name, :on_delete, :on_update, and :deferrable entries in the hashes.
291
292
  def foreign_key_list(table, opts={})
292
293
  m = output_identifier_meth
293
- im = input_identifier_meth
294
- schema, table = schema_and_table(table)
294
+ schema, _ = opts.fetch(:schema, schema_and_table(table))
295
295
  range = 0...32
296
296
 
297
297
  base_ds = metadata_dataset.
298
- where(:cl__relkind=>'r', :co__contype=>'f', :cl__relname=>im.call(table)).
299
298
  from(:pg_constraint___co).
300
- join(:pg_class___cl, :oid=>:conrelid)
299
+ join(:pg_class___cl, :oid=>:conrelid).
300
+ where(:cl__relkind=>'r', :co__contype=>'f', :cl__oid=>regclass_oid(table))
301
301
 
302
302
  # We split the parsing into two separate queries, which are merged manually later.
303
303
  # This is because PostgreSQL stores both the referencing and referenced columns in
@@ -320,8 +320,6 @@ module Sequel
320
320
  # If a schema is given, we only search in that schema, and the returned :table
321
321
  # entry is schema qualified as well.
322
322
  if schema
323
- ds = ds.join(:pg_namespace___nsp, :oid=>:cl__relnamespace).
324
- where(:nsp__nspname=>im.call(schema))
325
323
  ref_ds = ref_ds.join(:pg_namespace___nsp2, :oid=>:cl2__relnamespace).
326
324
  select_more(:nsp2__nspname___schema)
327
325
  end
@@ -347,21 +345,18 @@ module Sequel
347
345
  # Use the pg_* system tables to determine indexes on a table
348
346
  def indexes(table, opts={})
349
347
  m = output_identifier_meth
350
- im = input_identifier_meth
351
- schema, table = schema_and_table(table)
352
348
  range = 0...32
353
349
  attnums = server_version >= 80100 ? SQL::Function.new(:ANY, :ind__indkey) : range.map{|x| SQL::Subscript.new(:ind__indkey, [x])}
354
350
  ds = metadata_dataset.
355
351
  from(:pg_class___tab).
356
- join(:pg_index___ind, :indrelid=>:oid, im.call(table)=>:relname).
352
+ join(:pg_index___ind, :indrelid=>:oid).
357
353
  join(:pg_class___indc, :oid=>:indexrelid).
358
354
  join(:pg_attribute___att, :attrelid=>:tab__oid, :attnum=>attnums).
359
355
  left_join(:pg_constraint___con, :conname=>:indc__relname).
360
- filter(:indc__relkind=>'i', :ind__indisprimary=>false, :indexprs=>nil, :indpred=>nil, :indisvalid=>true).
356
+ filter(:indc__relkind=>'i', :ind__indisprimary=>false, :indexprs=>nil, :indpred=>nil, :indisvalid=>true, :tab__oid=>regclass_oid(table, opts)).
361
357
  order(:indc__relname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:ind__indkey, [x]), x]}, 32, :att__attnum)).
362
358
  select(:indc__relname___name, :ind__indisunique___unique, :att__attname___column, :con__condeferrable___deferrable)
363
359
 
364
- ds.join!(:pg_namespace___nsp, :oid=>:tab__relnamespace, :nspname=>schema.to_s) if schema
365
360
  ds.filter!(:indisready=>true, :indcheckxmin=>false) if server_version >= 80300
366
361
 
367
362
  indexes = {}
@@ -391,9 +386,7 @@ module Sequel
391
386
  def primary_key(table, opts={})
392
387
  quoted_table = quote_schema_table(table)
393
388
  Sequel.synchronize{return @primary_keys[quoted_table] if @primary_keys.has_key?(quoted_table)}
394
- schema, table = schema_and_table(table)
395
- sql = "#{SELECT_PK_SQL} AND pg_class.relname = #{literal(table)}"
396
- sql << " AND pg_namespace.nspname = #{literal(schema)}" if schema
389
+ sql = "#{SELECT_PK_SQL} AND pg_class.oid = #{literal(regclass_oid(table, opts))}"
397
390
  value = fetch(sql).single_value
398
391
  Sequel.synchronize{@primary_keys[quoted_table] = value}
399
392
  end
@@ -402,16 +395,12 @@ module Sequel
402
395
  def primary_key_sequence(table, opts={})
403
396
  quoted_table = quote_schema_table(table)
404
397
  Sequel.synchronize{return @primary_key_sequences[quoted_table] if @primary_key_sequences.has_key?(quoted_table)}
405
- schema, table = schema_and_table(table)
406
- table = literal(table)
407
- sql = "#{SELECT_SERIAL_SEQUENCE_SQL} AND t.relname = #{table}"
408
- sql << " AND name.nspname = #{literal(schema)}" if schema
398
+ sql = "#{SELECT_SERIAL_SEQUENCE_SQL} AND t.oid = #{literal(regclass_oid(table, opts))}"
409
399
  if pks = fetch(sql).single_record
410
400
  value = literal(SQL::QualifiedIdentifier.new(pks[:schema], pks[:sequence]))
411
401
  Sequel.synchronize{@primary_key_sequences[quoted_table] = value}
412
402
  else
413
- sql = "#{SELECT_CUSTOM_SEQUENCE_SQL} AND t.relname = #{table}"
414
- sql << " AND name.nspname = #{literal(schema)}" if schema
403
+ sql = "#{SELECT_CUSTOM_SEQUENCE_SQL} AND t.oid = #{literal(regclass_oid(table, opts))}"
415
404
  if pks = fetch(sql).single_record
416
405
  value = literal(SQL::QualifiedIdentifier.new(pks[:schema], LiteralString.new(pks[:sequence])))
417
406
  Sequel.synchronize{@primary_key_sequences[quoted_table] = value}
@@ -513,8 +502,10 @@ module Sequel
513
502
  # otherwise, an array of symbols of table names is returned.
514
503
  #
515
504
  # Options:
516
- # * :schema - The schema to search (default_schema by default)
517
- # * :server - The server to use
505
+ # :qualify :: Return the tables as Sequel::SQL::QualifiedIdentifier instances,
506
+ # using the schema the table is located in as the qualifier.
507
+ # :schema :: The schema to search (default_schema by default)
508
+ # :server :: The server to use
518
509
  def tables(opts={}, &block)
519
510
  pg_class_relname('r', opts, &block)
520
511
  end
@@ -529,8 +520,10 @@ module Sequel
529
520
  # Array of symbols specifying view names in the current database.
530
521
  #
531
522
  # Options:
532
- # * :schema - The schema to search (default_schema by default)
533
- # * :server - The server to use
523
+ # :qualify :: Return the views as Sequel::SQL::QualifiedIdentifier instances,
524
+ # using the schema the view is located in as the qualifier.
525
+ # :schema :: The schema to search (default_schema by default)
526
+ # :server :: The server to use
534
527
  def views(opts={})
535
528
  pg_class_relname('v', opts)
536
529
  end
@@ -606,8 +599,8 @@ module Sequel
606
599
  # The SQL queries to execute when starting a new connection.
607
600
  def connection_configuration_sqls
608
601
  sqls = []
609
- sqls << "SET standard_conforming_strings = ON" if Postgres.force_standard_strings
610
- if cmm = Postgres.client_min_messages
602
+ sqls << "SET standard_conforming_strings = ON" if typecast_value_boolean(@opts.fetch(:force_standard_strings, Postgres.force_standard_strings))
603
+ if (cmm = @opts.fetch(:client_min_messages, Postgres.client_min_messages)) && !cmm.to_s.empty?
611
604
  sqls << "SET client_min_messages = '#{cmm.to_s.upcase}'"
612
605
  end
613
606
  sqls
@@ -630,13 +623,26 @@ module Sequel
630
623
  end
631
624
  end
632
625
 
633
- DATABASE_ERROR_REGEXPS = {
634
- /duplicate key value violates unique constraint/ => UniqueConstraintViolation,
635
- /violates foreign key constraint/ => ForeignKeyConstraintViolation,
636
- /violates check constraint/ => CheckConstraintViolation,
637
- /violates not-null constraint/ => NotNullConstraintViolation,
638
- /conflicting key value violates exclusion constraint/ => ExclusionConstraintViolation,
639
- }.freeze
626
+ EXCLUSION_CONSTRAINT_SQL_STATE = '23P01'.freeze
627
+ def database_specific_error_class_from_sqlstate(sqlstate)
628
+ if sqlstate == EXCLUSION_CONSTRAINT_SQL_STATE
629
+ ExclusionConstraintViolation
630
+ else
631
+ super
632
+ end
633
+ end
634
+
635
+ DATABASE_ERROR_REGEXPS = [
636
+ # Add this check first, since otherwise it's possible for users to control
637
+ # which exception class is generated.
638
+ [/invalid input syntax/, DatabaseError],
639
+ [/duplicate key value violates unique constraint/, UniqueConstraintViolation],
640
+ [/violates foreign key constraint/, ForeignKeyConstraintViolation],
641
+ [/violates check constraint/, CheckConstraintViolation],
642
+ [/violates not-null constraint/, NotNullConstraintViolation],
643
+ [/conflicting key value violates exclusion constraint/, ExclusionConstraintViolation],
644
+ [/could not serialize access/, SerializationFailure],
645
+ ].freeze
640
646
  def database_error_regexps
641
647
  DATABASE_ERROR_REGEXPS
642
648
  end
@@ -834,7 +840,13 @@ module Sequel
834
840
  ds = metadata_dataset.from(:pg_class).filter(:relkind=>type).select(:relname).exclude(SQL::StringExpression.like(:relname, SYSTEM_TABLE_REGEXP)).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace)
835
841
  ds = filter_schema(ds, opts)
836
842
  m = output_identifier_meth
837
- block_given? ? yield(ds) : ds.map{|r| m.call(r[:relname])}
843
+ if block_given?
844
+ yield(ds)
845
+ elsif opts[:qualify]
846
+ ds.select_append(:pg_namespace__nspname).map{|r| Sequel.qualify(m.call(r[:nspname]), m.call(r[:relname]))}
847
+ else
848
+ ds.map{|r| m.call(r[:relname])}
849
+ end
838
850
  end
839
851
 
840
852
  # Use a dollar sign instead of question mark for the argument
@@ -843,6 +855,28 @@ module Sequel
843
855
  PREPARED_ARG_PLACEHOLDER
844
856
  end
845
857
 
858
+ # Return an expression the oid for the table expr. Used by the metadata parsing
859
+ # code to disambiguate unqualified tables.
860
+ def regclass_oid(expr, opts={})
861
+ if expr.is_a?(String) && !expr.is_a?(LiteralString)
862
+ expr = Sequel.identifier(expr)
863
+ end
864
+
865
+ sch, table = schema_and_table(expr)
866
+ sch ||= opts[:schema]
867
+ if sch
868
+ expr = Sequel.qualify(sch, table)
869
+ end
870
+
871
+ expr = if ds = opts[:dataset]
872
+ ds.literal(expr)
873
+ else
874
+ literal(expr)
875
+ end
876
+
877
+ Sequel.cast(expr.to_s,:regclass).cast(:oid)
878
+ end
879
+
846
880
  # Remove the cached entries for primary keys and sequences when a table is
847
881
  # changed.
848
882
  def remove_cached_schema(table)
@@ -879,35 +913,22 @@ module Sequel
879
913
  # The dataset used for parsing table schemas, using the pg_* system catalogs.
880
914
  def schema_parse_table(table_name, opts)
881
915
  m = output_identifier_meth(opts[:dataset])
882
- m2 = input_identifier_meth(opts[:dataset])
883
916
  ds = metadata_dataset.select(:pg_attribute__attname___name,
884
917
  SQL::Cast.new(:pg_attribute__atttypid, :integer).as(:oid),
885
918
  SQL::Function.new(:format_type, :pg_type__oid, :pg_attribute__atttypmod).as(:db_type),
886
919
  SQL::Function.new(:pg_get_expr, :pg_attrdef__adbin, :pg_class__oid).as(:default),
887
920
  SQL::BooleanExpression.new(:NOT, :pg_attribute__attnotnull).as(:allow_null),
888
- SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(:pg_attribute__attnum => SQL::Function.new(:ANY, :pg_index__indkey)), false).as(:primary_key),
889
- :pg_namespace__nspname).
921
+ SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(:pg_attribute__attnum => SQL::Function.new(:ANY, :pg_index__indkey)), false).as(:primary_key)).
890
922
  from(:pg_class).
891
923
  join(:pg_attribute, :attrelid=>:oid).
892
924
  join(:pg_type, :oid=>:atttypid).
893
- join(:pg_namespace, :oid=>:pg_class__relnamespace).
894
925
  left_outer_join(:pg_attrdef, :adrelid=>:pg_class__oid, :adnum=>:pg_attribute__attnum).
895
926
  left_outer_join(:pg_index, :indrelid=>:pg_class__oid, :indisprimary=>true).
896
927
  filter(:pg_attribute__attisdropped=>false).
897
928
  filter{|o| o.pg_attribute__attnum > 0}.
898
- filter(:pg_class__relname=>m2.call(table_name)).
929
+ filter(:pg_class__oid=>regclass_oid(table_name, opts)).
899
930
  order(:pg_attribute__attnum)
900
- ds = filter_schema(ds, opts)
901
- current_schema = nil
902
931
  ds.map do |row|
903
- sch = row.delete(:nspname)
904
- if current_schema
905
- if sch != current_schema
906
- raise Error, "columns from tables in two separate schema were returned (please specify a schema): #{current_schema.inspect}, #{sch.inspect}"
907
- end
908
- else
909
- current_schema = sch
910
- end
911
932
  row[:default] = nil if blank_object?(row[:default])
912
933
  row[:type] = schema_column_type(row[:db_type])
913
934
  [m.call(row.delete(:name)), row]
@@ -1010,6 +1031,8 @@ module Sequel
1010
1031
  PAREN_OPEN = Dataset::PAREN_OPEN
1011
1032
  PAREN_CLOSE = Dataset::PAREN_CLOSE
1012
1033
  COMMA = Dataset::COMMA
1034
+ ESCAPE = Dataset::ESCAPE
1035
+ BACKSLASH = Dataset::BACKSLASH
1013
1036
  AS = Dataset::AS
1014
1037
  XOR_OP = ' # '.freeze
1015
1038
  CRLF = "\r\n".freeze
@@ -1042,7 +1065,8 @@ module Sequel
1042
1065
  end
1043
1066
 
1044
1067
  # Handle converting the ruby xor operator (^) into the
1045
- # PostgreSQL xor operator (#).
1068
+ # PostgreSQL xor operator (#), and use the ILIKE and NOT ILIKE
1069
+ # operators.
1046
1070
  def complex_expression_sql_append(sql, op, args)
1047
1071
  case op
1048
1072
  when :^
@@ -1053,6 +1077,14 @@ module Sequel
1053
1077
  literal_append(sql, a)
1054
1078
  c ||= true
1055
1079
  end
1080
+ when :ILIKE, :'NOT ILIKE'
1081
+ sql << PAREN_OPEN
1082
+ literal_append(sql, args.at(0))
1083
+ sql << SPACE << op.to_s << SPACE
1084
+ literal_append(sql, args.at(1))
1085
+ sql << ESCAPE
1086
+ literal_append(sql, BACKSLASH)
1087
+ sql << PAREN_CLOSE
1056
1088
  else
1057
1089
  super
1058
1090
  end
@@ -496,13 +496,10 @@ module Sequel
496
496
  end
497
497
  end
498
498
 
499
- # SQLite is case insensitive (depending on pragma), so use LIKE for ILIKE.
500
- # It also doesn't support a NOT LIKE b, you need to use NOT (a LIKE b).
499
+ # SQLite doesn't support a NOT LIKE b, you need to use NOT (a LIKE b).
501
500
  # It doesn't support xor or the extract function natively, so those have to be emulated.
502
501
  def complex_expression_sql_append(sql, op, args)
503
502
  case op
504
- when :ILIKE
505
- super(sql, :LIKE, args.map{|a| SQL::Function.new(:upper, a)})
506
503
  when :"NOT LIKE", :"NOT ILIKE"
507
504
  sql << NOT_SPACE
508
505
  complex_expression_sql_append(sql, (op == :"NOT ILIKE" ? :ILIKE : :LIKE), args)
@@ -109,6 +109,21 @@ module Sequel
109
109
  [TinyTds::Error]
110
110
  end
111
111
 
112
+ # Stupid MSSQL maps foreign key and check constraint violations
113
+ # to the same error code, and doesn't expose the sqlstate. Use
114
+ # database error numbers if present and unambiguous, otherwise
115
+ # fallback to the regexp mapping.
116
+ def database_specific_error_class(exception, opts)
117
+ case exception.db_error_number
118
+ when 515
119
+ NotNullConstraintViolation
120
+ when 2627
121
+ UniqueConstraintViolation
122
+ else
123
+ super
124
+ end
125
+ end
126
+
112
127
  # Return true if the :conn argument is present and not active.
113
128
  def disconnect_error?(e, opts)
114
129
  super || (opts[:conn] && !opts[:conn].active?)
@@ -30,7 +30,7 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
30
30
  @connection_handling = opts[:connection_handling]
31
31
  @available_connections = []
32
32
  @allocated = {}
33
- @timeout = Integer(opts[:pool_timeout] || 5)
33
+ @timeout = Float(opts[:pool_timeout] || 5)
34
34
  @sleep_time = Float(opts[:pool_sleep_time] || 0.001)
35
35
  end
36
36
 
@@ -208,6 +208,12 @@ module Sequel
208
208
  def self.identifier_output_method=(value)
209
209
  Database.identifier_output_method = value
210
210
  end
211
+
212
+ # Parse the string as JSON and return the result.
213
+ # This is solely for internal use, it should not be used externally.
214
+ def self.parse_json(json) # :nodoc:
215
+ JSON.parse(json, :create_additions=>false)
216
+ end
211
217
 
212
218
  # Set whether to quote identifiers for all databases by default. By default,
213
219
  # Sequel quotes identifiers in all SQL strings, so to turn that off:
@@ -312,6 +318,7 @@ module Sequel
312
318
  end
313
319
 
314
320
  if defined?(RUBY_ENGINE) && RUBY_ENGINE != 'ruby'
321
+ # :nocov:
315
322
  # Mutex used to protect mutable data structures
316
323
  @data_mutex = Mutex.new
317
324
 
@@ -321,6 +328,7 @@ module Sequel
321
328
  def self.synchronize(&block)
322
329
  @single_threaded ? yield : @data_mutex.synchronize(&block)
323
330
  end
331
+ # :nocov:
324
332
  else
325
333
  # Yield directly to the block. You don't need to synchronize
326
334
  # access on MRI because the GVL makes certain methods atomic.
@@ -424,7 +432,9 @@ module Sequel
424
432
 
425
433
  require(%w"sql connection_pool exceptions dataset database timezones ast_transformer version")
426
434
  if !defined?(::SEQUEL_NO_CORE_EXTENSIONS) && !ENV.has_key?('SEQUEL_NO_CORE_EXTENSIONS')
435
+ # :nocov:
427
436
  extension(:core_extensions)
437
+ # :nocov:
428
438
  end
429
439
 
430
440
  # Add the database adapter class methods to Sequel via metaprogramming
@@ -236,9 +236,11 @@ module Sequel
236
236
  @pool.hold(server || :default){|conn| yield conn}
237
237
  end
238
238
  else
239
+ # :nocov:
239
240
  def synchronize(server=nil, &block)
240
241
  @pool.hold(server || :default, &block)
241
242
  end
243
+ # :nocov:
242
244
  end
243
245
 
244
246
  # Attempts to acquire a database connection. Returns true if successful.