sequel 3.21.0 → 3.31.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.
- data/CHANGELOG +485 -3
- data/README.rdoc +20 -6
- data/Rakefile +37 -19
- data/bin/sequel +1 -5
- data/doc/association_basics.rdoc +249 -43
- data/doc/dataset_basics.rdoc +4 -4
- data/doc/dataset_filtering.rdoc +8 -0
- data/doc/mass_assignment.rdoc +54 -0
- data/doc/migration.rdoc +15 -538
- data/doc/model_hooks.rdoc +72 -27
- data/doc/opening_databases.rdoc +89 -50
- data/doc/prepared_statements.rdoc +40 -13
- data/doc/reflection.rdoc +8 -2
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/release_notes/3.30.0.txt +135 -0
- data/doc/release_notes/3.31.0.txt +146 -0
- data/doc/schema_modification.rdoc +547 -0
- data/doc/sharding.rdoc +7 -1
- data/doc/testing.rdoc +129 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/ado.rb +40 -18
- data/lib/sequel/adapters/amalgalite.rb +17 -9
- data/lib/sequel/adapters/db2.rb +175 -86
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do/mysql.rb +5 -7
- data/lib/sequel/adapters/do/postgres.rb +0 -5
- data/lib/sequel/adapters/do/sqlite.rb +0 -5
- data/lib/sequel/adapters/do.rb +17 -36
- data/lib/sequel/adapters/firebird.rb +27 -208
- data/lib/sequel/adapters/ibmdb.rb +448 -0
- data/lib/sequel/adapters/informix.rb +6 -23
- data/lib/sequel/adapters/jdbc/as400.rb +16 -34
- data/lib/sequel/adapters/jdbc/db2.rb +45 -0
- data/lib/sequel/adapters/jdbc/derby.rb +247 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
- data/lib/sequel/adapters/jdbc/h2.rb +56 -29
- data/lib/sequel/adapters/jdbc/hsqldb.rb +170 -0
- data/lib/sequel/adapters/jdbc/informix.rb +26 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
- data/lib/sequel/adapters/jdbc/mysql.rb +11 -11
- data/lib/sequel/adapters/jdbc/oracle.rb +81 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +9 -13
- data/lib/sequel/adapters/jdbc/sqlite.rb +2 -6
- data/lib/sequel/adapters/jdbc/sqlserver.rb +71 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +231 -78
- data/lib/sequel/adapters/mock.rb +335 -0
- data/lib/sequel/adapters/mysql.rb +82 -184
- data/lib/sequel/adapters/mysql2.rb +26 -25
- data/lib/sequel/adapters/odbc/db2.rb +17 -0
- data/lib/sequel/adapters/odbc/mssql.rb +1 -6
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +3 -5
- data/lib/sequel/adapters/oracle.rb +351 -53
- data/lib/sequel/adapters/postgres.rb +194 -33
- data/lib/sequel/adapters/shared/access.rb +32 -9
- data/lib/sequel/adapters/shared/db2.rb +321 -0
- data/lib/sequel/adapters/shared/firebird.rb +221 -0
- data/lib/sequel/adapters/shared/informix.rb +53 -0
- data/lib/sequel/adapters/shared/mssql.rb +231 -106
- data/lib/sequel/adapters/shared/mysql.rb +181 -63
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
- data/lib/sequel/adapters/shared/oracle.rb +231 -41
- data/lib/sequel/adapters/shared/postgres.rb +214 -82
- data/lib/sequel/adapters/shared/progress.rb +4 -7
- data/lib/sequel/adapters/shared/sqlite.rb +201 -57
- data/lib/sequel/adapters/sqlite.rb +29 -21
- data/lib/sequel/adapters/swift/mysql.rb +12 -7
- data/lib/sequel/adapters/swift/postgres.rb +0 -5
- data/lib/sequel/adapters/swift/sqlite.rb +8 -6
- data/lib/sequel/adapters/swift.rb +5 -5
- data/lib/sequel/adapters/tinytds.rb +160 -19
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
- data/lib/sequel/ast_transformer.rb +190 -0
- data/lib/sequel/connection_pool/threaded.rb +3 -2
- data/lib/sequel/connection_pool.rb +1 -1
- data/lib/sequel/core.rb +46 -5
- data/lib/sequel/database/connecting.rb +5 -6
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +59 -1
- data/lib/sequel/database/logging.rb +1 -1
- data/lib/sequel/database/misc.rb +104 -19
- data/lib/sequel/database/query.rb +153 -58
- data/lib/sequel/database/schema_generator.rb +17 -7
- data/lib/sequel/database/schema_methods.rb +59 -29
- data/lib/sequel/dataset/actions.rb +231 -76
- data/lib/sequel/dataset/features.rb +91 -8
- data/lib/sequel/dataset/graph.rb +38 -11
- data/lib/sequel/dataset/misc.rb +50 -23
- data/lib/sequel/dataset/mutation.rb +5 -6
- data/lib/sequel/dataset/prepared_statements.rb +48 -27
- data/lib/sequel/dataset/query.rb +236 -43
- data/lib/sequel/dataset/sql.rb +665 -253
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/extensions/migration.rb +6 -4
- data/lib/sequel/extensions/named_timezones.rb +5 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +1020 -337
- data/lib/sequel/model/base.rb +414 -132
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model.rb +15 -8
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/identity_map.rb +114 -7
- data/lib/sequel/plugins/many_through_many.rb +65 -7
- data/lib/sequel/plugins/prepared_statements.rb +151 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/rcte_tree.rb +29 -15
- data/lib/sequel/plugins/serialization.rb +88 -44
- data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
- data/lib/sequel/plugins/sharding.rb +21 -54
- data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +2 -2
- data/lib/sequel/plugins/xml_serializer.rb +3 -3
- data/lib/sequel/sql.rb +109 -52
- data/lib/sequel/timezones.rb +57 -38
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/mssql_spec.rb +111 -29
- data/spec/adapters/mysql_spec.rb +135 -123
- data/spec/adapters/oracle_spec.rb +49 -77
- data/spec/adapters/postgres_spec.rb +267 -78
- data/spec/adapters/spec_helper.rb +6 -5
- data/spec/adapters/sqlite_spec.rb +35 -21
- data/spec/core/connection_pool_spec.rb +71 -92
- data/spec/core/core_sql_spec.rb +21 -30
- data/spec/core/database_spec.rb +788 -512
- data/spec/core/dataset_spec.rb +1073 -1053
- data/spec/core/expression_filters_spec.rb +159 -42
- data/spec/core/mock_adapter_spec.rb +419 -0
- data/spec/core/object_graph_spec.rb +86 -114
- data/spec/core/schema_generator_spec.rb +3 -3
- data/spec/core/schema_spec.rb +298 -38
- data/spec/core/spec_helper.rb +6 -48
- data/spec/extensions/association_autoreloading_spec.rb +1 -10
- data/spec/extensions/association_dependencies_spec.rb +2 -12
- data/spec/extensions/association_pks_spec.rb +66 -32
- data/spec/extensions/caching_spec.rb +23 -50
- data/spec/extensions/class_table_inheritance_spec.rb +31 -83
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/composition_spec.rb +18 -13
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/hook_class_methods_spec.rb +65 -91
- data/spec/extensions/identity_map_spec.rb +114 -22
- data/spec/extensions/instance_filters_spec.rb +11 -21
- data/spec/extensions/instance_hooks_spec.rb +53 -0
- data/spec/extensions/json_serializer_spec.rb +4 -5
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +17 -39
- data/spec/extensions/many_through_many_spec.rb +283 -250
- data/spec/extensions/migration_spec.rb +35 -32
- data/spec/extensions/named_timezones_spec.rb +23 -3
- data/spec/extensions/nested_attributes_spec.rb +97 -88
- data/spec/extensions/optimistic_locking_spec.rb +9 -20
- data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
- data/spec/extensions/prepared_statements_spec.rb +53 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
- data/spec/extensions/pretty_table_spec.rb +1 -6
- data/spec/extensions/rcte_tree_spec.rb +41 -43
- data/spec/extensions/schema_dumper_spec.rb +5 -8
- data/spec/extensions/schema_spec.rb +13 -21
- data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
- data/spec/extensions/serialization_spec.rb +72 -43
- data/spec/extensions/sharding_spec.rb +66 -140
- data/spec/extensions/single_table_inheritance_spec.rb +18 -29
- data/spec/extensions/spec_helper.rb +13 -61
- data/spec/extensions/sql_expr_spec.rb +20 -60
- data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/timestamps_spec.rb +6 -6
- data/spec/extensions/to_dot_spec.rb +4 -7
- data/spec/extensions/touch_spec.rb +13 -14
- data/spec/extensions/tree_spec.rb +11 -26
- data/spec/extensions/typecast_on_load_spec.rb +1 -6
- data/spec/extensions/update_primary_key_spec.rb +30 -24
- data/spec/extensions/validation_class_methods_spec.rb +30 -51
- data/spec/extensions/validation_helpers_spec.rb +16 -35
- data/spec/extensions/xml_serializer_spec.rb +16 -4
- data/spec/integration/associations_test.rb +537 -21
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +461 -65
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/model_test.rb +80 -1
- data/spec/integration/plugin_test.rb +304 -116
- data/spec/integration/prepared_statement_test.rb +212 -122
- data/spec/integration/schema_test.rb +161 -30
- data/spec/integration/spec_helper.rb +39 -30
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +172 -5
- data/spec/integration/type_test.rb +17 -3
- data/spec/model/association_reflection_spec.rb +201 -7
- data/spec/model/associations_spec.rb +865 -662
- data/spec/model/base_spec.rb +275 -95
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +677 -676
- data/spec/model/hooks_spec.rb +309 -67
- data/spec/model/model_spec.rb +222 -168
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +381 -218
- data/spec/model/spec_helper.rb +13 -71
- data/spec/model/validations_spec.rb +11 -0
- metadata +115 -73
|
@@ -241,16 +241,6 @@ module Sequel
|
|
|
241
241
|
self << drop_language_sql(name, opts)
|
|
242
242
|
end
|
|
243
243
|
|
|
244
|
-
# Remove the cached entries for primary keys and sequences when dropping a table.
|
|
245
|
-
def drop_table(*names)
|
|
246
|
-
names.each do |name|
|
|
247
|
-
name = quote_schema_table(name)
|
|
248
|
-
@primary_keys.delete(name)
|
|
249
|
-
@primary_key_sequences.delete(name)
|
|
250
|
-
end
|
|
251
|
-
super
|
|
252
|
-
end
|
|
253
|
-
|
|
254
244
|
# Drops a trigger from the database. Arguments:
|
|
255
245
|
# * table : table from which to drop the trigger
|
|
256
246
|
# * name : name of the trigger to drop
|
|
@@ -294,6 +284,16 @@ module Sequel
|
|
|
294
284
|
dataset.from(:pg_class).join(:pg_locks, :relation=>:relfilenode).select(:pg_class__relname, Sequel::SQL::ColumnAll.new(:pg_locks))
|
|
295
285
|
end
|
|
296
286
|
|
|
287
|
+
# Notifies the given channel. See the PostgreSQL NOTIFY documentation. Options:
|
|
288
|
+
#
|
|
289
|
+
# :payload :: The payload string to use for the NOTIFY statement. Only supported
|
|
290
|
+
# in PostgreSQL 9.0+.
|
|
291
|
+
# :server :: The server to which to send the NOTIFY statement, if the sharding support
|
|
292
|
+
# is being used.
|
|
293
|
+
def notify(channel, opts={})
|
|
294
|
+
execute_ddl("NOTIFY #{channel}#{", #{literal(opts[:payload].to_s)}" if opts[:payload]}", opts)
|
|
295
|
+
end
|
|
296
|
+
|
|
297
297
|
# Return primary key for the given table.
|
|
298
298
|
def primary_key(table, opts={})
|
|
299
299
|
quoted_table = quote_schema_table(table)
|
|
@@ -345,8 +345,11 @@ module Sequel
|
|
|
345
345
|
(conn.server_version rescue nil) if conn.respond_to?(:server_version)
|
|
346
346
|
end
|
|
347
347
|
unless @server_version
|
|
348
|
-
m = /PostgreSQL (\d+)\.(\d+)(?:(?:rc\d+)|\.(\d+))?/.match(fetch('SELECT version()').single_value)
|
|
349
|
-
|
|
348
|
+
@server_version = if m = /PostgreSQL (\d+)\.(\d+)(?:(?:rc\d+)|\.(\d+))?/.match(fetch('SELECT version()').single_value)
|
|
349
|
+
(m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
|
|
350
|
+
else
|
|
351
|
+
0
|
|
352
|
+
end
|
|
350
353
|
end
|
|
351
354
|
@server_version
|
|
352
355
|
end
|
|
@@ -358,6 +361,11 @@ module Sequel
|
|
|
358
361
|
@supports_prepared_transactions = self['SHOW max_prepared_transactions'].get.to_i > 0
|
|
359
362
|
end
|
|
360
363
|
|
|
364
|
+
# PostgreSQL supports CREATE TABLE IF NOT EXISTS on 9.1+
|
|
365
|
+
def supports_create_table_if_not_exists?
|
|
366
|
+
server_version >= 90100
|
|
367
|
+
end
|
|
368
|
+
|
|
361
369
|
# PostgreSQL supports savepoints
|
|
362
370
|
def supports_savepoints?
|
|
363
371
|
true
|
|
@@ -368,30 +376,24 @@ module Sequel
|
|
|
368
376
|
true
|
|
369
377
|
end
|
|
370
378
|
|
|
371
|
-
#
|
|
379
|
+
# Array of symbols specifying table names in the current database.
|
|
380
|
+
# The dataset used is yielded to the block if one is provided,
|
|
381
|
+
# otherwise, an array of symbols of table names is returned.
|
|
372
382
|
#
|
|
373
383
|
# Options:
|
|
374
384
|
# * :schema - The schema to search (default_schema by default)
|
|
375
385
|
# * :server - The server to use
|
|
376
|
-
def
|
|
377
|
-
|
|
378
|
-
schema, table = schema_and_table(table)
|
|
379
|
-
opts[:schema] ||= schema
|
|
380
|
-
tables(opts){|ds| !ds.first(:relname=>im.call(table)).nil?}
|
|
386
|
+
def tables(opts={}, &block)
|
|
387
|
+
pg_class_relname('r', opts, &block)
|
|
381
388
|
end
|
|
382
|
-
|
|
383
|
-
# Array of symbols specifying
|
|
384
|
-
# The dataset used is yielded to the block if one is provided,
|
|
385
|
-
# otherwise, an array of symbols of table names is returned.
|
|
389
|
+
|
|
390
|
+
# Array of symbols specifying view names in the current database.
|
|
386
391
|
#
|
|
387
392
|
# Options:
|
|
388
393
|
# * :schema - The schema to search (default_schema by default)
|
|
389
394
|
# * :server - The server to use
|
|
390
|
-
def
|
|
391
|
-
|
|
392
|
-
ds = filter_schema(ds, opts)
|
|
393
|
-
m = output_identifier_meth
|
|
394
|
-
block_given? ? yield(ds) : ds.map{|r| m.call(r[:relname])}
|
|
395
|
+
def views(opts={})
|
|
396
|
+
pg_class_relname('v', opts)
|
|
395
397
|
end
|
|
396
398
|
|
|
397
399
|
private
|
|
@@ -399,8 +401,8 @@ module Sequel
|
|
|
399
401
|
# If the :prepare option is given and we aren't in a savepoint,
|
|
400
402
|
# prepare the transaction for a two-phase commit.
|
|
401
403
|
def commit_transaction(conn, opts={})
|
|
402
|
-
if opts[:prepare] &&
|
|
403
|
-
log_connection_execute(conn, "PREPARE TRANSACTION #{literal(
|
|
404
|
+
if (s = opts[:prepare]) && @transactions[conn][:savepoint_level] <= 1
|
|
405
|
+
log_connection_execute(conn, "PREPARE TRANSACTION #{literal(s)}")
|
|
404
406
|
else
|
|
405
407
|
super
|
|
406
408
|
end
|
|
@@ -454,11 +456,6 @@ module Sequel
|
|
|
454
456
|
"DROP LANGUAGE#{' IF EXISTS' if opts[:if_exists]} #{name}#{' CASCADE' if opts[:cascade]}"
|
|
455
457
|
end
|
|
456
458
|
|
|
457
|
-
# Always CASCADE the table drop
|
|
458
|
-
def drop_table_sql(name)
|
|
459
|
-
"DROP TABLE #{quote_schema_table(name)} CASCADE"
|
|
460
|
-
end
|
|
461
|
-
|
|
462
459
|
# SQL for dropping a trigger from the database.
|
|
463
460
|
def drop_trigger_sql(table, name, opts={})
|
|
464
461
|
"DROP TRIGGER#{' IF EXISTS' if opts[:if_exists]} #{name} ON #{quote_schema_table(table)}#{' CASCADE' if opts[:cascade]}"
|
|
@@ -499,7 +496,7 @@ module Sequel
|
|
|
499
496
|
filter = " WHERE #{filter_expr(filter)}" if filter
|
|
500
497
|
case index_type
|
|
501
498
|
when :full_text
|
|
502
|
-
expr = "(to_tsvector(#{literal(index[:language] || 'simple')}, #{dataset.send(:full_text_string_join, cols)}))"
|
|
499
|
+
expr = "(to_tsvector(#{literal(index[:language] || 'simple')}, #{literal(dataset.send(:full_text_string_join, cols))}))"
|
|
503
500
|
index_type = :gin
|
|
504
501
|
when :spatial
|
|
505
502
|
index_type = :gist
|
|
@@ -540,12 +537,29 @@ module Sequel
|
|
|
540
537
|
conn.execute(sql)
|
|
541
538
|
end
|
|
542
539
|
|
|
540
|
+
# Backbone of the tables and views support.
|
|
541
|
+
def pg_class_relname(type, opts)
|
|
542
|
+
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)
|
|
543
|
+
ds = filter_schema(ds, opts)
|
|
544
|
+
m = output_identifier_meth
|
|
545
|
+
block_given? ? yield(ds) : ds.map{|r| m.call(r[:relname])}
|
|
546
|
+
end
|
|
547
|
+
|
|
543
548
|
# Use a dollar sign instead of question mark for the argument
|
|
544
549
|
# placeholder.
|
|
545
550
|
def prepared_arg_placeholder
|
|
546
551
|
PREPARED_ARG_PLACEHOLDER
|
|
547
552
|
end
|
|
548
553
|
|
|
554
|
+
# Remove the cached entries for primary keys and sequences when a table is
|
|
555
|
+
# changed.
|
|
556
|
+
def remove_cached_schema(table)
|
|
557
|
+
tab = quote_schema_table(table)
|
|
558
|
+
@primary_keys.delete(tab)
|
|
559
|
+
@primary_key_sequences.delete(tab)
|
|
560
|
+
super
|
|
561
|
+
end
|
|
562
|
+
|
|
549
563
|
# SQL DDL statement for renaming a table. PostgreSQL doesn't allow you to change a table's schema in
|
|
550
564
|
# a rename table operation, so speciying a new schema in new_name will not have an effect.
|
|
551
565
|
def rename_table_sql(name, new_name)
|
|
@@ -560,13 +574,14 @@ module Sequel
|
|
|
560
574
|
|
|
561
575
|
# The dataset used for parsing table schemas, using the pg_* system catalogs.
|
|
562
576
|
def schema_parse_table(table_name, opts)
|
|
563
|
-
m = output_identifier_meth
|
|
564
|
-
m2 = input_identifier_meth
|
|
577
|
+
m = output_identifier_meth(opts[:dataset])
|
|
578
|
+
m2 = input_identifier_meth(opts[:dataset])
|
|
565
579
|
ds = metadata_dataset.select(:pg_attribute__attname___name,
|
|
566
580
|
SQL::Function.new(:format_type, :pg_type__oid, :pg_attribute__atttypmod).as(:db_type),
|
|
567
581
|
SQL::Function.new(:pg_get_expr, :pg_attrdef__adbin, :pg_class__oid).as(:default),
|
|
568
582
|
SQL::BooleanExpression.new(:NOT, :pg_attribute__attnotnull).as(:allow_null),
|
|
569
|
-
SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(:pg_attribute__attnum => SQL::Function.new(:ANY, :pg_index__indkey)), false).as(:primary_key)
|
|
583
|
+
SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(:pg_attribute__attnum => SQL::Function.new(:ANY, :pg_index__indkey)), false).as(:primary_key),
|
|
584
|
+
:pg_namespace__nspname).
|
|
570
585
|
from(:pg_class).
|
|
571
586
|
join(:pg_attribute, :attrelid=>:oid).
|
|
572
587
|
join(:pg_type, :oid=>:atttypid).
|
|
@@ -578,7 +593,16 @@ module Sequel
|
|
|
578
593
|
filter(:pg_class__relname=>m2.call(table_name)).
|
|
579
594
|
order(:pg_attribute__attnum)
|
|
580
595
|
ds = filter_schema(ds, opts)
|
|
596
|
+
current_schema = nil
|
|
581
597
|
ds.map do |row|
|
|
598
|
+
sch = row.delete(:nspname)
|
|
599
|
+
if current_schema
|
|
600
|
+
if sch != current_schema
|
|
601
|
+
raise Error, "columns from tables in two separate schema were returned (please specify a schema): #{current_schema.inspect}, #{sch.inspect}"
|
|
602
|
+
end
|
|
603
|
+
else
|
|
604
|
+
current_schema = sch
|
|
605
|
+
end
|
|
582
606
|
row[:default] = nil if blank_object?(row[:default])
|
|
583
607
|
row[:type] = schema_column_type(row[:db_type])
|
|
584
608
|
[m.call(row.delete(:name)), row]
|
|
@@ -627,35 +651,59 @@ module Sequel
|
|
|
627
651
|
BOOL_FALSE = 'false'.freeze
|
|
628
652
|
BOOL_TRUE = 'true'.freeze
|
|
629
653
|
COMMA_SEPARATOR = ', '.freeze
|
|
630
|
-
DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'from using where')
|
|
654
|
+
DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'delete from using where')
|
|
655
|
+
DELETE_CLAUSE_METHODS_91 = Dataset.clause_methods(:delete, %w'with delete from using where returning')
|
|
631
656
|
EXCLUSIVE = 'EXCLUSIVE'.freeze
|
|
632
657
|
EXPLAIN = 'EXPLAIN '.freeze
|
|
633
658
|
EXPLAIN_ANALYZE = 'EXPLAIN ANALYZE '.freeze
|
|
634
659
|
FOR_SHARE = ' FOR SHARE'.freeze
|
|
660
|
+
INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'insert into columns values')
|
|
661
|
+
INSERT_CLAUSE_METHODS_82 = Dataset.clause_methods(:insert, %w'insert into columns values returning')
|
|
662
|
+
INSERT_CLAUSE_METHODS_91 = Dataset.clause_methods(:insert, %w'with insert into columns values returning')
|
|
635
663
|
LOCK = 'LOCK TABLE %s IN %s MODE'.freeze
|
|
636
664
|
NULL = LiteralString.new('NULL').freeze
|
|
637
665
|
PG_TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S".freeze
|
|
638
666
|
QUERY_PLAN = 'QUERY PLAN'.to_sym
|
|
639
667
|
ROW_EXCLUSIVE = 'ROW EXCLUSIVE'.freeze
|
|
640
668
|
ROW_SHARE = 'ROW SHARE'.freeze
|
|
641
|
-
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'distinct columns from join where group having compounds order limit lock')
|
|
642
|
-
SELECT_CLAUSE_METHODS_84 = Dataset.clause_methods(:select, %w'with distinct columns from join where group having window compounds order limit lock')
|
|
669
|
+
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select distinct columns from join where group having compounds order limit lock')
|
|
670
|
+
SELECT_CLAUSE_METHODS_84 = Dataset.clause_methods(:select, %w'with select distinct columns from join where group having window compounds order limit lock')
|
|
643
671
|
SHARE = 'SHARE'.freeze
|
|
644
672
|
SHARE_ROW_EXCLUSIVE = 'SHARE ROW EXCLUSIVE'.freeze
|
|
645
673
|
SHARE_UPDATE_EXCLUSIVE = 'SHARE UPDATE EXCLUSIVE'.freeze
|
|
646
674
|
SQL_WITH_RECURSIVE = "WITH RECURSIVE ".freeze
|
|
647
|
-
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'table set from where')
|
|
675
|
+
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'update table set from where')
|
|
676
|
+
UPDATE_CLAUSE_METHODS_91 = Dataset.clause_methods(:update, %w'with update table set from where returning')
|
|
677
|
+
SPACE = Dataset::SPACE
|
|
678
|
+
FROM = Dataset::FROM
|
|
679
|
+
APOS = Dataset::APOS
|
|
680
|
+
APOS_RE = Dataset::APOS_RE
|
|
681
|
+
DOUBLE_APOS = Dataset::DOUBLE_APOS
|
|
682
|
+
PAREN_OPEN = Dataset::PAREN_OPEN
|
|
683
|
+
PAREN_CLOSE = Dataset::PAREN_CLOSE
|
|
684
|
+
COMMA = Dataset::COMMA
|
|
685
|
+
AS = Dataset::AS
|
|
686
|
+
XOR_OP = ' # '.freeze
|
|
687
|
+
CRLF = "\r\n".freeze
|
|
688
|
+
BLOB_RE = /[\000-\037\047\134\177-\377]/n.freeze
|
|
689
|
+
WINDOW = " WINDOW ".freeze
|
|
690
|
+
EMPTY_STRING = ''.freeze
|
|
648
691
|
|
|
649
692
|
# Shared methods for prepared statements when used with PostgreSQL databases.
|
|
650
693
|
module PreparedStatementMethods
|
|
651
694
|
# Override insert action to use RETURNING if the server supports it.
|
|
695
|
+
def run(&block)
|
|
696
|
+
if @prepared_type == :insert && supports_insert_select?
|
|
697
|
+
fetch_rows(prepared_sql){|r| return r.values.first}
|
|
698
|
+
else
|
|
699
|
+
super
|
|
700
|
+
end
|
|
701
|
+
end
|
|
702
|
+
|
|
652
703
|
def prepared_sql
|
|
653
704
|
return @prepared_sql if @prepared_sql
|
|
705
|
+
@opts[:returning] = insert_pk if @prepared_type == :insert && supports_insert_select?
|
|
654
706
|
super
|
|
655
|
-
if @prepared_type == :insert and !@opts[:disable_insert_returning] and server_version >= 80200
|
|
656
|
-
@prepared_sql = insert_returning_pk_sql(*@prepared_modify_values)
|
|
657
|
-
meta_def(:insert_returning_pk_sql){|*args| prepared_sql}
|
|
658
|
-
end
|
|
659
707
|
@prepared_sql
|
|
660
708
|
end
|
|
661
709
|
end
|
|
@@ -677,10 +725,16 @@ module Sequel
|
|
|
677
725
|
|
|
678
726
|
# Handle converting the ruby xor operator (^) into the
|
|
679
727
|
# PostgreSQL xor operator (#).
|
|
680
|
-
def
|
|
728
|
+
def complex_expression_sql_append(sql, op, args)
|
|
681
729
|
case op
|
|
682
730
|
when :^
|
|
683
|
-
|
|
731
|
+
j = XOR_OP
|
|
732
|
+
c = false
|
|
733
|
+
args.each do |a|
|
|
734
|
+
sql << j if c
|
|
735
|
+
literal_append(sql, a)
|
|
736
|
+
c ||= true
|
|
737
|
+
end
|
|
684
738
|
else
|
|
685
739
|
super
|
|
686
740
|
end
|
|
@@ -693,7 +747,7 @@ module Sequel
|
|
|
693
747
|
|
|
694
748
|
# Return the results of an EXPLAIN query as a string
|
|
695
749
|
def explain(opts={})
|
|
696
|
-
with_sql((opts[:analyze] ? EXPLAIN_ANALYZE : EXPLAIN) + select_sql).map(QUERY_PLAN).join(
|
|
750
|
+
with_sql((opts[:analyze] ? EXPLAIN_ANALYZE : EXPLAIN) + select_sql).map(QUERY_PLAN).join(CRLF)
|
|
697
751
|
end
|
|
698
752
|
|
|
699
753
|
# Return a cloned dataset which will use FOR SHARE to lock returned rows.
|
|
@@ -705,29 +759,34 @@ module Sequel
|
|
|
705
759
|
# in 8.3 by default, and available for earlier versions as an add-on).
|
|
706
760
|
def full_text_search(cols, terms, opts = {})
|
|
707
761
|
lang = opts[:language] || 'simple'
|
|
708
|
-
|
|
762
|
+
terms = terms.join(' | ') if terms.is_a?(Array)
|
|
763
|
+
filter("to_tsvector(?, ?) @@ to_tsquery(?, ?)", lang, full_text_string_join(cols), lang, terms)
|
|
709
764
|
end
|
|
710
765
|
|
|
711
766
|
# Insert given values into the database.
|
|
712
|
-
def insert(*values)
|
|
713
|
-
if @opts[:
|
|
714
|
-
|
|
715
|
-
elsif
|
|
716
|
-
|
|
767
|
+
def insert(*values, &block)
|
|
768
|
+
if @opts[:returning]
|
|
769
|
+
super
|
|
770
|
+
elsif !@opts[:sql] && supports_insert_select?
|
|
771
|
+
returning(insert_pk).insert(*values){|r| return r.values.first}
|
|
772
|
+
elsif (f = opts[:from]) && !f.empty?
|
|
773
|
+
v = if values.size == 1
|
|
774
|
+
values.first
|
|
775
|
+
elsif values.size == 2 && values.all?{|v0| v0.is_a?(Array)}
|
|
776
|
+
Hash[*values.first.zip(values.last).flatten]
|
|
777
|
+
else
|
|
778
|
+
values
|
|
779
|
+
end
|
|
780
|
+
execute_insert(insert_sql(*values), :table=>f.first, :values=>v)
|
|
717
781
|
else
|
|
718
|
-
|
|
782
|
+
super
|
|
719
783
|
end
|
|
720
784
|
end
|
|
721
785
|
|
|
722
|
-
# Use the RETURNING clause to return the columns listed in returning.
|
|
723
|
-
def insert_returning_sql(returning, *values)
|
|
724
|
-
"#{insert_sql(*values)} RETURNING #{column_list(Array(returning))}"
|
|
725
|
-
end
|
|
726
|
-
|
|
727
786
|
# Insert a record returning the record inserted
|
|
728
787
|
def insert_select(*values)
|
|
729
|
-
return
|
|
730
|
-
|
|
788
|
+
return unless supports_insert_select?
|
|
789
|
+
returning.insert(*values){|r| return r}
|
|
731
790
|
end
|
|
732
791
|
|
|
733
792
|
# Locks all tables in the dataset's FROM clause (but not in JOINs) with
|
|
@@ -749,9 +808,17 @@ module Sequel
|
|
|
749
808
|
return super if server_version < 80200
|
|
750
809
|
|
|
751
810
|
# postgresql 8.2 introduces support for multi-row insert
|
|
752
|
-
|
|
811
|
+
sql = LiteralString.new('VALUES ')
|
|
812
|
+
expression_list_append(sql, values.map{|r| Array(r)})
|
|
813
|
+
[insert_sql(columns, sql)]
|
|
753
814
|
end
|
|
754
815
|
|
|
816
|
+
# PostgreSQL supports using the WITH clause in subqueries if it
|
|
817
|
+
# supports using WITH at all (i.e. on PostgreSQL 8.4+).
|
|
818
|
+
def supports_cte_in_subqueries?
|
|
819
|
+
supports_cte?
|
|
820
|
+
end
|
|
821
|
+
|
|
755
822
|
# DISTINCT ON is a PostgreSQL extension
|
|
756
823
|
def supports_distinct_on?
|
|
757
824
|
true
|
|
@@ -762,6 +829,14 @@ module Sequel
|
|
|
762
829
|
true
|
|
763
830
|
end
|
|
764
831
|
|
|
832
|
+
def supports_returning?(type)
|
|
833
|
+
if type == :insert
|
|
834
|
+
server_version >= 80200 && !opts[:disable_insert_returning]
|
|
835
|
+
else
|
|
836
|
+
server_version >= 80200
|
|
837
|
+
end
|
|
838
|
+
end
|
|
839
|
+
|
|
765
840
|
# PostgreSQL supports timezones in literal timestamps
|
|
766
841
|
def supports_timestamp_timezones?
|
|
767
842
|
true
|
|
@@ -777,16 +852,40 @@ module Sequel
|
|
|
777
852
|
clone(:window=>(@opts[:window]||[]) + [[name, SQL::Window.new(opts)]])
|
|
778
853
|
end
|
|
779
854
|
|
|
855
|
+
protected
|
|
856
|
+
|
|
857
|
+
# If returned primary keys are requested, use RETURNING unless already set on the
|
|
858
|
+
# dataset. If RETURNING is already set, use existing returning values. If RETURNING
|
|
859
|
+
# is only set to return a single columns, return an array of just that column.
|
|
860
|
+
# Otherwise, return an array of hashes.
|
|
861
|
+
def _import(columns, values, opts={})
|
|
862
|
+
if server_version >= 80200
|
|
863
|
+
if opts[:return] == :primary_key && !@opts[:returning]
|
|
864
|
+
returning(insert_pk)._import(columns, values, opts)
|
|
865
|
+
elsif @opts[:returning]
|
|
866
|
+
statements = multi_insert_sql(columns, values)
|
|
867
|
+
@db.transaction(opts.merge(:server=>@opts[:server])) do
|
|
868
|
+
statements.map{|st| returning_fetch_rows(st)}
|
|
869
|
+
end.first.map{|v| v.length == 1 ? v.values.first : v}
|
|
870
|
+
else
|
|
871
|
+
super
|
|
872
|
+
end
|
|
873
|
+
else
|
|
874
|
+
super
|
|
875
|
+
end
|
|
876
|
+
end
|
|
877
|
+
|
|
780
878
|
private
|
|
781
879
|
|
|
782
880
|
# PostgreSQL allows deleting from joined datasets
|
|
783
881
|
def delete_clause_methods
|
|
784
|
-
DELETE_CLAUSE_METHODS
|
|
882
|
+
server_version >= 90100 ? DELETE_CLAUSE_METHODS_91 : DELETE_CLAUSE_METHODS
|
|
785
883
|
end
|
|
786
884
|
|
|
787
885
|
# Only include the primary table in the main delete clause
|
|
788
886
|
def delete_from_sql(sql)
|
|
789
|
-
sql <<
|
|
887
|
+
sql << FROM
|
|
888
|
+
source_list_append(sql, @opts[:from][0..0])
|
|
790
889
|
end
|
|
791
890
|
|
|
792
891
|
# Use USING to specify additional tables in a delete query
|
|
@@ -794,26 +893,38 @@ module Sequel
|
|
|
794
893
|
join_from_sql(:USING, sql)
|
|
795
894
|
end
|
|
796
895
|
|
|
797
|
-
#
|
|
798
|
-
def
|
|
896
|
+
# PostgreSQL allows a RETURNING clause.
|
|
897
|
+
def insert_clause_methods
|
|
898
|
+
if server_version >= 90100
|
|
899
|
+
INSERT_CLAUSE_METHODS_91
|
|
900
|
+
elsif server_version >= 80200
|
|
901
|
+
INSERT_CLAUSE_METHODS_82
|
|
902
|
+
else
|
|
903
|
+
INSERT_CLAUSE_METHODS
|
|
904
|
+
end
|
|
905
|
+
end
|
|
906
|
+
|
|
907
|
+
# Return the primary key to use for RETURNING in an INSERT statement
|
|
908
|
+
def insert_pk
|
|
799
909
|
pk = db.primary_key(opts[:from].first) if opts[:from] && !opts[:from].empty?
|
|
800
|
-
|
|
910
|
+
pk ? Sequel::SQL::Identifier.new(pk) : NULL
|
|
801
911
|
end
|
|
802
|
-
|
|
912
|
+
|
|
803
913
|
# For multiple table support, PostgreSQL requires at least
|
|
804
914
|
# two from tables, with joins allowed.
|
|
805
915
|
def join_from_sql(type, sql)
|
|
806
916
|
if(from = @opts[:from][1..-1]).empty?
|
|
807
917
|
raise(Error, 'Need multiple FROM tables if updating/deleting a dataset with JOINs') if @opts[:join]
|
|
808
918
|
else
|
|
809
|
-
sql <<
|
|
919
|
+
sql << SPACE << type.to_s << SPACE
|
|
920
|
+
source_list_append(sql, from)
|
|
810
921
|
select_join_sql(sql)
|
|
811
922
|
end
|
|
812
923
|
end
|
|
813
924
|
|
|
814
925
|
# Use a generic blob quoting method, hopefully overridden in one of the subadapter methods
|
|
815
|
-
def
|
|
816
|
-
|
|
926
|
+
def literal_blob_append(sql, v)
|
|
927
|
+
sql << APOS << v.gsub(BLOB_RE){|b| "\\#{("%o" % b[0..1].unpack("C")[0]).rjust(3, '0')}"} << APOS
|
|
817
928
|
end
|
|
818
929
|
|
|
819
930
|
# PostgreSQL uses FALSE for false values
|
|
@@ -822,8 +933,8 @@ module Sequel
|
|
|
822
933
|
end
|
|
823
934
|
|
|
824
935
|
# Assume that SQL standard quoting is on, per Sequel's defaults
|
|
825
|
-
def
|
|
826
|
-
|
|
936
|
+
def literal_string_append(sql, v)
|
|
937
|
+
sql << APOS << v.gsub(APOS_RE, DOUBLE_APOS) << APOS
|
|
827
938
|
end
|
|
828
939
|
|
|
829
940
|
# PostgreSQL uses FALSE for false values
|
|
@@ -836,6 +947,14 @@ module Sequel
|
|
|
836
947
|
server_version >= 80400 ? SELECT_CLAUSE_METHODS_84 : SELECT_CLAUSE_METHODS
|
|
837
948
|
end
|
|
838
949
|
|
|
950
|
+
# PostgreSQL requires parentheses around compound datasets if they use
|
|
951
|
+
# CTEs, and using them in other places doesn't hurt.
|
|
952
|
+
def compound_dataset_sql_append(sql, ds)
|
|
953
|
+
sql << PAREN_OPEN
|
|
954
|
+
super
|
|
955
|
+
sql << PAREN_CLOSE
|
|
956
|
+
end
|
|
957
|
+
|
|
839
958
|
# Support FOR SHARE locking when using the :share lock style.
|
|
840
959
|
def select_lock_sql(sql)
|
|
841
960
|
@opts[:lock] == :share ? (sql << FOR_SHARE) : super
|
|
@@ -843,14 +962,26 @@ module Sequel
|
|
|
843
962
|
|
|
844
963
|
# SQL fragment for named window specifications
|
|
845
964
|
def select_window_sql(sql)
|
|
846
|
-
|
|
965
|
+
if ws = @opts[:window]
|
|
966
|
+
sql << WINDOW
|
|
967
|
+
c = false
|
|
968
|
+
co = COMMA
|
|
969
|
+
as = AS
|
|
970
|
+
ws.map do |name, window|
|
|
971
|
+
sql << co if c
|
|
972
|
+
literal_append(sql, name)
|
|
973
|
+
sql << as
|
|
974
|
+
literal_append(sql, window)
|
|
975
|
+
c ||= true
|
|
976
|
+
end
|
|
977
|
+
end
|
|
847
978
|
end
|
|
848
979
|
|
|
849
980
|
# Use WITH RECURSIVE instead of WITH if any of the CTEs is recursive
|
|
850
981
|
def select_with_sql_base
|
|
851
982
|
opts[:with].any?{|w| w[:recursive]} ? SQL_WITH_RECURSIVE : super
|
|
852
983
|
end
|
|
853
|
-
|
|
984
|
+
|
|
854
985
|
# The version of the database server
|
|
855
986
|
def server_version
|
|
856
987
|
db.server_version(@opts[:server])
|
|
@@ -858,15 +989,15 @@ module Sequel
|
|
|
858
989
|
|
|
859
990
|
# Concatenate the expressions with a space in between
|
|
860
991
|
def full_text_string_join(cols)
|
|
861
|
-
cols = Array(cols).map{|x| SQL::Function.new(:COALESCE, x,
|
|
862
|
-
cols = cols.zip([
|
|
992
|
+
cols = Array(cols).map{|x| SQL::Function.new(:COALESCE, x, EMPTY_STRING)}
|
|
993
|
+
cols = cols.zip([SPACE] * cols.length).flatten
|
|
863
994
|
cols.pop
|
|
864
|
-
|
|
995
|
+
SQL::StringExpression.new(:'||', *cols)
|
|
865
996
|
end
|
|
866
997
|
|
|
867
998
|
# PostgreSQL splits the main table from the joined tables
|
|
868
999
|
def update_clause_methods
|
|
869
|
-
UPDATE_CLAUSE_METHODS
|
|
1000
|
+
server_version >= 90100 ? UPDATE_CLAUSE_METHODS_91 : UPDATE_CLAUSE_METHODS
|
|
870
1001
|
end
|
|
871
1002
|
|
|
872
1003
|
# Use FROM to specify additional tables in an update query
|
|
@@ -876,7 +1007,8 @@ module Sequel
|
|
|
876
1007
|
|
|
877
1008
|
# Only include the primary table in the main update clause
|
|
878
1009
|
def update_table_sql(sql)
|
|
879
|
-
sql <<
|
|
1010
|
+
sql << SPACE
|
|
1011
|
+
source_list_append(sql, @opts[:from][0..0])
|
|
880
1012
|
end
|
|
881
1013
|
end
|
|
882
1014
|
end
|
|
@@ -6,12 +6,6 @@ module Sequel
|
|
|
6
6
|
def database_type
|
|
7
7
|
:progress
|
|
8
8
|
end
|
|
9
|
-
|
|
10
|
-
def dataset(opts = nil)
|
|
11
|
-
ds = super
|
|
12
|
-
ds.extend(DatasetMethods)
|
|
13
|
-
ds
|
|
14
|
-
end
|
|
15
9
|
end
|
|
16
10
|
|
|
17
11
|
module DatasetMethods
|
|
@@ -37,7 +31,10 @@ module Sequel
|
|
|
37
31
|
# The Progress adapter targets Progress 9, so it silently ignores the option.
|
|
38
32
|
def select_limit_sql(sql)
|
|
39
33
|
raise(Error, "OFFSET not supported") if @opts[:offset]
|
|
40
|
-
#
|
|
34
|
+
# if l = @opts[:limit]
|
|
35
|
+
# sql << " TOP "
|
|
36
|
+
# literal_append(sql, l)
|
|
37
|
+
# end
|
|
41
38
|
end
|
|
42
39
|
end
|
|
43
40
|
end
|