sequel 3.44.0 → 3.45.0

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