sequel 3.44.0 → 3.45.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +44 -0
- data/Rakefile +12 -4
- data/doc/reflection.rdoc +3 -3
- data/doc/release_notes/3.45.0.txt +179 -0
- data/doc/schema_modification.rdoc +1 -1
- data/doc/transactions.rdoc +23 -0
- data/lib/sequel/adapters/db2.rb +1 -0
- data/lib/sequel/adapters/ibmdb.rb +19 -3
- data/lib/sequel/adapters/jdbc.rb +15 -0
- data/lib/sequel/adapters/jdbc/derby.rb +1 -5
- data/lib/sequel/adapters/jdbc/h2.rb +1 -0
- data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -1
- data/lib/sequel/adapters/jdbc/jtds.rb +5 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +5 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +7 -1
- data/lib/sequel/adapters/jdbc/sqlite.rb +1 -1
- data/lib/sequel/adapters/jdbc/transactions.rb +28 -1
- data/lib/sequel/adapters/mysql.rb +4 -0
- data/lib/sequel/adapters/mysql2.rb +5 -1
- data/lib/sequel/adapters/oracle.rb +18 -0
- data/lib/sequel/adapters/postgres.rb +11 -1
- data/lib/sequel/adapters/shared/access.rb +14 -2
- data/lib/sequel/adapters/shared/cubrid.rb +1 -11
- data/lib/sequel/adapters/shared/db2.rb +11 -6
- data/lib/sequel/adapters/shared/mssql.rb +10 -10
- data/lib/sequel/adapters/shared/mysql.rb +11 -1
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +17 -1
- data/lib/sequel/adapters/shared/oracle.rb +16 -15
- data/lib/sequel/adapters/shared/postgres.rb +91 -59
- data/lib/sequel/adapters/shared/sqlite.rb +1 -4
- data/lib/sequel/adapters/tinytds.rb +15 -0
- data/lib/sequel/connection_pool/threaded.rb +1 -1
- data/lib/sequel/core.rb +10 -0
- data/lib/sequel/database/connecting.rb +2 -0
- data/lib/sequel/database/misc.rb +46 -4
- data/lib/sequel/database/query.rb +33 -14
- data/lib/sequel/database/schema_methods.rb +0 -5
- data/lib/sequel/dataset/misc.rb +9 -0
- data/lib/sequel/dataset/mutation.rb +9 -7
- data/lib/sequel/dataset/sql.rb +13 -0
- data/lib/sequel/exceptions.rb +3 -0
- data/lib/sequel/extensions/connection_validator.rb +1 -1
- data/lib/sequel/extensions/date_arithmetic.rb +0 -8
- data/lib/sequel/extensions/eval_inspect.rb +2 -0
- data/lib/sequel/extensions/named_timezones.rb +18 -2
- data/lib/sequel/extensions/pg_array.rb +5 -1
- data/lib/sequel/extensions/pg_array_ops.rb +2 -0
- data/lib/sequel/extensions/pg_hstore.rb +2 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +2 -0
- data/lib/sequel/extensions/pg_json.rb +3 -1
- data/lib/sequel/extensions/pg_range.rb +2 -0
- data/lib/sequel/extensions/pg_range_ops.rb +2 -0
- data/lib/sequel/extensions/pg_row.rb +2 -0
- data/lib/sequel/extensions/pg_row_ops.rb +2 -0
- data/lib/sequel/extensions/query.rb +18 -22
- data/lib/sequel/model/associations.rb +3 -4
- data/lib/sequel/model/base.rb +2 -0
- data/lib/sequel/plugins/force_encoding.rb +2 -0
- data/lib/sequel/plugins/json_serializer.rb +155 -39
- data/lib/sequel/plugins/serialization.rb +14 -2
- data/lib/sequel/plugins/unlimited_update.rb +31 -0
- data/lib/sequel/plugins/validation_class_methods.rb +6 -4
- data/lib/sequel/plugins/xml_serializer.rb +133 -30
- data/lib/sequel/sql.rb +2 -0
- data/lib/sequel/timezones.rb +4 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mysql_spec.rb +0 -11
- data/spec/adapters/postgres_spec.rb +86 -54
- data/spec/adapters/spec_helper.rb +6 -0
- data/spec/core/connection_pool_spec.rb +16 -0
- data/spec/core/database_spec.rb +77 -1
- data/spec/core/dataset_spec.rb +30 -15
- data/spec/core/expression_filters_spec.rb +55 -13
- data/spec/core/mock_adapter_spec.rb +4 -0
- data/spec/core/schema_spec.rb +0 -2
- data/spec/core/spec_helper.rb +5 -0
- data/spec/core_extensions_spec.rb +33 -28
- data/spec/extensions/constraint_validations_spec.rb +2 -2
- data/spec/extensions/core_refinements_spec.rb +12 -12
- data/spec/extensions/json_serializer_spec.rb +137 -31
- data/spec/extensions/named_timezones_spec.rb +10 -0
- data/spec/extensions/pg_auto_parameterize_spec.rb +5 -0
- data/spec/extensions/pg_json_spec.rb +14 -0
- data/spec/extensions/pg_row_spec.rb +11 -0
- data/spec/extensions/pretty_table_spec.rb +2 -2
- data/spec/extensions/query_spec.rb +11 -8
- data/spec/extensions/serialization_spec.rb +20 -0
- data/spec/extensions/spec_helper.rb +8 -2
- data/spec/extensions/sql_expr_spec.rb +1 -1
- data/spec/extensions/unlimited_update_spec.rb +20 -0
- data/spec/extensions/xml_serializer_spec.rb +68 -16
- data/spec/integration/dataset_test.rb +28 -0
- data/spec/integration/spec_helper.rb +6 -0
- data/spec/integration/transaction_test.rb +39 -0
- data/spec/model/model_spec.rb +1 -1
- data/spec/sequel_coverage.rb +15 -0
- 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
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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.
|
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
|
-
#
|
517
|
-
#
|
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
|
-
#
|
533
|
-
#
|
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
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
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?
|
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(:
|
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
|
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 =
|
33
|
+
@timeout = Float(opts[:pool_timeout] || 5)
|
34
34
|
@sleep_time = Float(opts[:pool_sleep_time] || 0.001)
|
35
35
|
end
|
36
36
|
|
data/lib/sequel/core.rb
CHANGED
@@ -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.
|