sequel 3.21.0 → 3.36.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 +855 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +23 -7
- data/Rakefile +42 -24
- data/bin/sequel +26 -8
- data/doc/active_record.rdoc +1 -0
- data/doc/advanced_associations.rdoc +171 -11
- data/doc/association_basics.rdoc +293 -43
- data/doc/dataset_basics.rdoc +5 -5
- data/doc/dataset_filtering.rdoc +8 -0
- data/doc/mass_assignment.rdoc +56 -0
- data/doc/migration.rdoc +85 -552
- data/doc/model_hooks.rdoc +78 -27
- data/doc/object_model.rdoc +541 -0
- data/doc/opening_databases.rdoc +97 -50
- data/doc/prepared_statements.rdoc +40 -14
- data/doc/querying.rdoc +28 -4
- data/doc/reflection.rdoc +30 -4
- 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/release_notes/3.32.0.txt +202 -0
- data/doc/release_notes/3.33.0.txt +157 -0
- data/doc/release_notes/3.34.0.txt +671 -0
- data/doc/release_notes/3.35.0.txt +144 -0
- data/doc/release_notes/3.36.0.txt +245 -0
- data/doc/schema_modification.rdoc +572 -0
- data/doc/sharding.rdoc +95 -3
- data/doc/testing.rdoc +129 -0
- data/doc/thread_safety.rdoc +17 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/virtual_rows.rdoc +51 -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 +17 -11
- data/lib/sequel/adapters/do/postgres.rb +1 -57
- data/lib/sequel/adapters/do/sqlite.rb +0 -10
- data/lib/sequel/adapters/do.rb +18 -36
- data/lib/sequel/adapters/firebird.rb +27 -208
- data/lib/sequel/adapters/ibmdb.rb +453 -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 +47 -0
- data/lib/sequel/adapters/jdbc/derby.rb +287 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
- data/lib/sequel/adapters/jdbc/h2.rb +73 -31
- data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
- data/lib/sequel/adapters/jdbc/informix.rb +21 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
- data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
- data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +58 -48
- data/lib/sequel/adapters/jdbc/progress.rb +21 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
- data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +265 -94
- data/lib/sequel/adapters/mock.rb +361 -0
- data/lib/sequel/adapters/mysql.rb +118 -222
- data/lib/sequel/adapters/mysql2.rb +58 -57
- data/lib/sequel/adapters/odbc/mssql.rb +15 -7
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +3 -5
- data/lib/sequel/adapters/oracle.rb +349 -53
- data/lib/sequel/adapters/postgres.rb +356 -107
- data/lib/sequel/adapters/shared/access.rb +32 -9
- data/lib/sequel/adapters/shared/db2.rb +342 -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 +335 -118
- data/lib/sequel/adapters/shared/mysql.rb +340 -76
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
- data/lib/sequel/adapters/shared/oracle.rb +243 -45
- data/lib/sequel/adapters/shared/postgres.rb +524 -279
- data/lib/sequel/adapters/shared/progress.rb +5 -8
- data/lib/sequel/adapters/shared/sqlite.rb +265 -89
- data/lib/sequel/adapters/sqlite.rb +101 -39
- data/lib/sequel/adapters/swift/mysql.rb +9 -12
- data/lib/sequel/adapters/swift/postgres.rb +9 -74
- data/lib/sequel/adapters/swift/sqlite.rb +8 -6
- data/lib/sequel/adapters/swift.rb +8 -7
- data/lib/sequel/adapters/tinytds.rb +161 -21
- 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 +194 -0
- data/lib/sequel/connection_pool/sharded_single.rb +11 -1
- data/lib/sequel/connection_pool/sharded_threaded.rb +28 -2
- data/lib/sequel/connection_pool/single.rb +5 -0
- data/lib/sequel/connection_pool/threaded.rb +38 -14
- data/lib/sequel/connection_pool.rb +10 -2
- data/lib/sequel/core.rb +118 -11
- data/lib/sequel/database/connecting.rb +38 -28
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +63 -2
- data/lib/sequel/database/logging.rb +7 -2
- data/lib/sequel/database/misc.rb +150 -46
- data/lib/sequel/database/query.rb +215 -65
- data/lib/sequel/database/schema_generator.rb +64 -20
- data/lib/sequel/database/schema_methods.rb +212 -54
- data/lib/sequel/dataset/actions.rb +322 -85
- data/lib/sequel/dataset/features.rb +96 -8
- data/lib/sequel/dataset/graph.rb +38 -11
- data/lib/sequel/dataset/misc.rb +50 -23
- data/lib/sequel/dataset/mutation.rb +12 -7
- data/lib/sequel/dataset/prepared_statements.rb +68 -30
- data/lib/sequel/dataset/query.rb +237 -44
- data/lib/sequel/dataset/sql.rb +698 -278
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/_pretty_table.rb +79 -0
- data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +16 -34
- data/lib/sequel/extensions/migration.rb +97 -11
- data/lib/sequel/extensions/named_timezones.rb +5 -0
- data/lib/sequel/extensions/null_dataset.rb +90 -0
- data/lib/sequel/extensions/pg_array.rb +460 -0
- data/lib/sequel/extensions/pg_array_ops.rb +220 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +169 -0
- data/lib/sequel/extensions/pg_hstore.rb +296 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
- data/lib/sequel/extensions/pg_inet.rb +89 -0
- data/lib/sequel/extensions/pg_json.rb +178 -0
- data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
- data/lib/sequel/extensions/pretty_table.rb +5 -71
- data/lib/sequel/extensions/query.rb +1 -1
- data/lib/sequel/extensions/query_literals.rb +79 -0
- data/lib/sequel/extensions/schema_caching.rb +76 -0
- data/lib/sequel/extensions/schema_dumper.rb +251 -37
- data/lib/sequel/extensions/select_remove.rb +35 -0
- data/lib/sequel/extensions/server_block.rb +139 -0
- data/lib/sequel/extensions/sql_expr.rb +4 -110
- 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 +1187 -393
- data/lib/sequel/model/base.rb +578 -170
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model.rb +30 -11
- data/lib/sequel/no_core_ext.rb +2 -0
- data/lib/sequel/plugins/active_model.rb +13 -1
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/caching.rb +25 -18
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/composition.rb +44 -12
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/dirty.rb +214 -0
- data/lib/sequel/plugins/eager_each.rb +59 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/hook_class_methods.rb +1 -1
- data/lib/sequel/plugins/identity_map.rb +123 -8
- data/lib/sequel/plugins/instance_filters.rb +10 -0
- data/lib/sequel/plugins/instance_hooks.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +58 -6
- data/lib/sequel/plugins/list.rb +12 -2
- data/lib/sequel/plugins/many_through_many.rb +79 -28
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
- data/lib/sequel/plugins/nested_attributes.rb +139 -63
- data/lib/sequel/plugins/optimistic_locking.rb +8 -0
- data/lib/sequel/plugins/prepared_statements.rb +167 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +87 -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/schema.rb +7 -2
- data/lib/sequel/plugins/serialization.rb +98 -49
- data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
- data/lib/sequel/plugins/sharding.rb +21 -54
- data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
- data/lib/sequel/plugins/static_cache.rb +99 -0
- data/lib/sequel/plugins/subclasses.rb +10 -2
- data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
- data/lib/sequel/plugins/timestamps.rb +1 -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/validation_class_methods.rb +1 -1
- data/lib/sequel/plugins/xml_serializer.rb +15 -4
- data/lib/sequel/sql.rb +526 -59
- 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/firebird_spec.rb +1 -1
- data/spec/adapters/mssql_spec.rb +142 -40
- data/spec/adapters/mysql_spec.rb +380 -234
- data/spec/adapters/oracle_spec.rb +67 -77
- data/spec/adapters/postgres_spec.rb +1050 -212
- data/spec/adapters/spec_helper.rb +8 -5
- data/spec/adapters/sqlite_spec.rb +139 -27
- data/spec/core/connection_pool_spec.rb +196 -93
- data/spec/core/database_spec.rb +879 -510
- data/spec/core/dataset_spec.rb +1415 -1086
- data/spec/core/expression_filters_spec.rb +554 -109
- data/spec/core/mock_adapter_spec.rb +449 -0
- data/spec/core/object_graph_spec.rb +86 -114
- data/spec/core/schema_generator_spec.rb +4 -4
- data/spec/core/schema_spec.rb +482 -38
- data/spec/core/spec_helper.rb +6 -48
- data/spec/extensions/active_model_spec.rb +13 -0
- data/spec/extensions/arbitrary_servers_spec.rb +110 -0
- data/spec/extensions/association_autoreloading_spec.rb +18 -10
- data/spec/extensions/association_dependencies_spec.rb +15 -25
- data/spec/extensions/association_pks_spec.rb +66 -32
- data/spec/extensions/association_proxies_spec.rb +4 -4
- data/spec/extensions/caching_spec.rb +47 -51
- 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/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +119 -139
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/dirty_spec.rb +155 -0
- data/spec/extensions/eager_each_spec.rb +34 -0
- data/spec/extensions/force_encoding_spec.rb +4 -2
- data/spec/extensions/hook_class_methods_spec.rb +69 -92
- data/spec/extensions/identity_map_spec.rb +131 -22
- data/spec/extensions/inflector_spec.rb +0 -4
- data/spec/extensions/instance_filters_spec.rb +11 -21
- data/spec/extensions/instance_hooks_spec.rb +72 -0
- data/spec/extensions/json_serializer_spec.rb +49 -6
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +48 -39
- data/spec/extensions/many_through_many_spec.rb +283 -250
- data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
- data/spec/extensions/migration_spec.rb +140 -35
- data/spec/extensions/named_timezones_spec.rb +29 -11
- data/spec/extensions/nested_attributes_spec.rb +251 -89
- data/spec/extensions/null_dataset_spec.rb +85 -0
- data/spec/extensions/optimistic_locking_spec.rb +24 -21
- data/spec/extensions/pg_array_ops_spec.rb +105 -0
- data/spec/extensions/pg_array_spec.rb +196 -0
- data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
- data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
- data/spec/extensions/pg_hstore_spec.rb +195 -0
- data/spec/extensions/pg_inet_spec.rb +44 -0
- data/spec/extensions/pg_json_spec.rb +101 -0
- data/spec/extensions/pg_statement_cache_spec.rb +209 -0
- 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 +87 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
- data/spec/extensions/pretty_table_spec.rb +7 -6
- data/spec/extensions/query_literals_spec.rb +168 -0
- data/spec/extensions/query_spec.rb +6 -0
- data/spec/extensions/rcte_tree_spec.rb +50 -43
- data/spec/extensions/schema_caching_spec.rb +41 -0
- data/spec/extensions/schema_dumper_spec.rb +429 -22
- data/spec/extensions/schema_spec.rb +25 -21
- data/spec/extensions/select_remove_spec.rb +38 -0
- data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
- data/spec/extensions/serialization_spec.rb +85 -43
- data/spec/extensions/server_block_spec.rb +90 -0
- data/spec/extensions/sharding_spec.rb +69 -143
- data/spec/extensions/single_table_inheritance_spec.rb +23 -29
- data/spec/extensions/skip_create_refresh_spec.rb +1 -1
- data/spec/extensions/spec_helper.rb +22 -61
- data/spec/extensions/sql_expr_spec.rb +29 -60
- data/spec/extensions/static_cache_spec.rb +145 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +17 -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 +9 -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 +29 -4
- data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
- data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
- data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
- data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
- data/spec/integration/associations_test.rb +659 -21
- data/spec/integration/database_test.rb +9 -3
- data/spec/integration/dataset_test.rb +605 -91
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/migrator_test.rb +2 -3
- data/spec/integration/model_test.rb +81 -2
- data/spec/integration/plugin_test.rb +454 -143
- data/spec/integration/prepared_statement_test.rb +263 -128
- data/spec/integration/schema_test.rb +309 -43
- data/spec/integration/spec_helper.rb +40 -30
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +183 -8
- data/spec/integration/type_test.rb +18 -4
- data/spec/model/association_reflection_spec.rb +201 -7
- data/spec/model/associations_spec.rb +1067 -661
- data/spec/model/base_spec.rb +329 -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 +349 -67
- data/spec/model/model_spec.rb +240 -168
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +490 -232
- data/spec/model/spec_helper.rb +14 -71
- data/spec/model/validations_spec.rb +11 -0
- data/spec/rcov.opts +2 -0
- metadata +178 -80
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
module Sequel
|
|
2
|
-
Dataset::NON_SQL_OPTIONS << :disable_insert_returning
|
|
3
|
-
|
|
4
2
|
# Top level module for holding all PostgreSQL-related modules and classes
|
|
5
3
|
# for Sequel. There are a few module level accessors that are added via
|
|
6
4
|
# metaprogramming. These are:
|
|
@@ -33,10 +31,10 @@ module Sequel
|
|
|
33
31
|
# Array of exceptions that need to be converted. JDBC
|
|
34
32
|
# uses NativeExceptions, the native adapter uses PGError.
|
|
35
33
|
CONVERTED_EXCEPTIONS = []
|
|
36
|
-
|
|
34
|
+
|
|
37
35
|
@client_min_messages = :warning
|
|
38
36
|
@force_standard_strings = true
|
|
39
|
-
|
|
37
|
+
|
|
40
38
|
class << self
|
|
41
39
|
# By default, Sequel sets the minimum level of log messages sent to the client
|
|
42
40
|
# to WARNING, where PostgreSQL uses a default of NOTICE. This is to avoid a lot
|
|
@@ -45,25 +43,32 @@ module Sequel
|
|
|
45
43
|
attr_accessor :client_min_messages
|
|
46
44
|
|
|
47
45
|
# By default, Sequel forces the use of standard strings, so that
|
|
48
|
-
# '\\' is interpreted as \\ and not \. While PostgreSQL defaults
|
|
49
|
-
# to interpreting plain strings
|
|
50
|
-
#
|
|
51
|
-
#
|
|
46
|
+
# '\\' is interpreted as \\ and not \. While PostgreSQL <9.1 defaults
|
|
47
|
+
# to interpreting plain strings, newer versions use standard strings by
|
|
48
|
+
# default. Sequel assumes that SQL standard strings will be used. Setting
|
|
49
|
+
# this to false means Sequel will use the database's default.
|
|
52
50
|
attr_accessor :force_standard_strings
|
|
53
51
|
end
|
|
54
52
|
|
|
55
|
-
# Methods shared by
|
|
56
|
-
module
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
53
|
+
# Methods shared by Database instances that connect to PostgreSQL.
|
|
54
|
+
module DatabaseMethods
|
|
55
|
+
EXCLUDE_SCHEMAS = /pg_*|information_schema/i
|
|
56
|
+
PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
|
|
57
|
+
RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session|relation "(.*)" does not exist/.freeze
|
|
58
|
+
SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
|
|
59
|
+
FOREIGN_KEY_LIST_ON_DELETE_MAP = {'a'.freeze=>:no_action, 'r'.freeze=>:restrict, 'c'.freeze=>:cascade, 'n'.freeze=>:set_null, 'd'.freeze=>:set_default}.freeze
|
|
60
|
+
|
|
61
|
+
# SQL fragment for custom sequences (ones not created by serial primary key),
|
|
62
|
+
# Returning the schema and literal form of the sequence name, by parsing
|
|
63
|
+
# the column defaults table.
|
|
64
|
+
SELECT_CUSTOM_SEQUENCE_SQL = (<<-end_sql
|
|
65
|
+
SELECT name.nspname AS "schema",
|
|
66
|
+
CASE
|
|
67
|
+
WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
|
|
68
|
+
substr(split_part(def.adsrc, '''', 2),
|
|
69
|
+
strpos(split_part(def.adsrc, '''', 2), '.')+1)
|
|
70
|
+
ELSE split_part(def.adsrc, '''', 2)
|
|
71
|
+
END AS "sequence"
|
|
67
72
|
FROM pg_class t
|
|
68
73
|
JOIN pg_namespace name ON (t.relnamespace = name.oid)
|
|
69
74
|
JOIN pg_attribute attr ON (t.oid = attrelid)
|
|
@@ -71,24 +76,26 @@ module Sequel
|
|
|
71
76
|
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
|
72
77
|
WHERE cons.contype = 'p'
|
|
73
78
|
AND def.adsrc ~* 'nextval'
|
|
74
|
-
#{"AND name.nspname = '#{schema}'" if schema}
|
|
75
|
-
AND t.relname = '#{table}'
|
|
76
79
|
end_sql
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
).strip.gsub(/\s+/, ' ').freeze
|
|
81
|
+
|
|
82
|
+
# SQL fragment for determining primary key column for the given table. Only
|
|
83
|
+
# returns the first primary key if the table has a composite primary key.
|
|
84
|
+
SELECT_PK_SQL = (<<-end_sql
|
|
85
|
+
SELECT pg_attribute.attname AS pk
|
|
80
86
|
FROM pg_class, pg_attribute, pg_index, pg_namespace
|
|
81
87
|
WHERE pg_class.oid = pg_attribute.attrelid
|
|
82
88
|
AND pg_class.relnamespace = pg_namespace.oid
|
|
83
89
|
AND pg_class.oid = pg_index.indrelid
|
|
84
90
|
AND pg_index.indkey[0] = pg_attribute.attnum
|
|
85
91
|
AND pg_index.indisprimary = 't'
|
|
86
|
-
#{"AND pg_namespace.nspname = '#{schema}'" if schema}
|
|
87
|
-
AND pg_class.relname = '#{table}'
|
|
88
92
|
end_sql
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
93
|
+
).strip.gsub(/\s+/, ' ').freeze
|
|
94
|
+
|
|
95
|
+
# SQL fragment for getting sequence associated with table's
|
|
96
|
+
# primary key, assuming it was a serial primary key column.
|
|
97
|
+
SELECT_SERIAL_SEQUENCE_SQL = (<<-end_sql
|
|
98
|
+
SELECT name.nspname AS "schema", seq.relname AS "sequence"
|
|
92
99
|
FROM pg_class seq, pg_attribute attr, pg_depend dep,
|
|
93
100
|
pg_namespace name, pg_constraint cons
|
|
94
101
|
WHERE seq.oid = dep.objid
|
|
@@ -99,68 +106,8 @@ module Sequel
|
|
|
99
106
|
AND attr.attrelid = cons.conrelid
|
|
100
107
|
AND attr.attnum = cons.conkey[1]
|
|
101
108
|
AND cons.contype = 'p'
|
|
102
|
-
#{"AND name.nspname = '#{schema}'" if schema}
|
|
103
|
-
AND seq.relname = '#{table}'
|
|
104
109
|
end_sql
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
# Depth of the current transaction on this connection, used
|
|
108
|
-
# to implement multi-level transactions with savepoints.
|
|
109
|
-
attr_accessor :transaction_depth
|
|
110
|
-
|
|
111
|
-
# Apply connection settings for this connection. Currently, turns
|
|
112
|
-
# standard_conforming_strings ON if Postgres.force_standard_strings
|
|
113
|
-
# is true.
|
|
114
|
-
def apply_connection_settings
|
|
115
|
-
if Postgres.force_standard_strings
|
|
116
|
-
# This setting will only work on PostgreSQL 8.2 or greater
|
|
117
|
-
# and we don't know the server version at this point, so
|
|
118
|
-
# try it unconditionally and rescue any errors.
|
|
119
|
-
execute("SET standard_conforming_strings = ON") rescue nil
|
|
120
|
-
end
|
|
121
|
-
if cmm = Postgres.client_min_messages
|
|
122
|
-
execute("SET client_min_messages = '#{cmm.to_s.upcase}'")
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
# Get the last inserted value for the given sequence.
|
|
127
|
-
def last_insert_id(sequence)
|
|
128
|
-
sql = SELECT_CURRVAL % sequence
|
|
129
|
-
execute(sql) do |r|
|
|
130
|
-
val = single_value(r)
|
|
131
|
-
return val.to_i if val
|
|
132
|
-
end
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
# Get the primary key for the given table.
|
|
136
|
-
def primary_key(schema, table)
|
|
137
|
-
sql = SELECT_PK[schema, table]
|
|
138
|
-
execute(sql) do |r|
|
|
139
|
-
return single_value(r)
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
# Get the primary key and sequence for the given table.
|
|
144
|
-
def sequence(schema, table)
|
|
145
|
-
sql = SELECT_SERIAL_SEQUENCE[schema, table]
|
|
146
|
-
execute(sql) do |r|
|
|
147
|
-
seq = single_value(r)
|
|
148
|
-
return seq if seq
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
sql = SELECT_CUSTOM_SEQUENCE[schema, table]
|
|
152
|
-
execute(sql) do |r|
|
|
153
|
-
return single_value(r)
|
|
154
|
-
end
|
|
155
|
-
end
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
# Methods shared by Database instances that connect to PostgreSQL.
|
|
159
|
-
module DatabaseMethods
|
|
160
|
-
EXCLUDE_SCHEMAS = /pg_*|information_schema/i
|
|
161
|
-
PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
|
|
162
|
-
RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session|relation "(.*)" does not exist/.freeze
|
|
163
|
-
SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
|
|
110
|
+
).strip.gsub(/\s+/, ' ').freeze
|
|
164
111
|
|
|
165
112
|
# Commit an existing prepared transaction with the given transaction
|
|
166
113
|
# identifier string.
|
|
@@ -191,7 +138,7 @@ module Sequel
|
|
|
191
138
|
def create_function(name, definition, opts={})
|
|
192
139
|
self << create_function_sql(name, definition, opts)
|
|
193
140
|
end
|
|
194
|
-
|
|
141
|
+
|
|
195
142
|
# Create the procedural language in the database. Arguments:
|
|
196
143
|
# * name : Name of the procedural language (e.g. plpgsql)
|
|
197
144
|
# * opts : options hash:
|
|
@@ -202,7 +149,13 @@ module Sequel
|
|
|
202
149
|
def create_language(name, opts={})
|
|
203
150
|
self << create_language_sql(name, opts)
|
|
204
151
|
end
|
|
205
|
-
|
|
152
|
+
|
|
153
|
+
# Create a schema in the database. Arguments:
|
|
154
|
+
# * name : Name of the schema (e.g. admin)
|
|
155
|
+
def create_schema(name)
|
|
156
|
+
self << create_schema_sql(name)
|
|
157
|
+
end
|
|
158
|
+
|
|
206
159
|
# Create a trigger in the database. Arguments:
|
|
207
160
|
# * table : the table on which this trigger operates
|
|
208
161
|
# * name : the name of this trigger
|
|
@@ -216,7 +169,7 @@ module Sequel
|
|
|
216
169
|
def create_trigger(table, name, function, opts={})
|
|
217
170
|
self << create_trigger_sql(table, name, function, opts)
|
|
218
171
|
end
|
|
219
|
-
|
|
172
|
+
|
|
220
173
|
# PostgreSQL uses the :postgres database type.
|
|
221
174
|
def database_type
|
|
222
175
|
:postgres
|
|
@@ -231,7 +184,7 @@ module Sequel
|
|
|
231
184
|
def drop_function(name, opts={})
|
|
232
185
|
self << drop_function_sql(name, opts)
|
|
233
186
|
end
|
|
234
|
-
|
|
187
|
+
|
|
235
188
|
# Drops a procedural language from the database. Arguments:
|
|
236
189
|
# * name : name of the procedural language to drop
|
|
237
190
|
# * opts : options hash:
|
|
@@ -240,15 +193,14 @@ module Sequel
|
|
|
240
193
|
def drop_language(name, opts={})
|
|
241
194
|
self << drop_language_sql(name, opts)
|
|
242
195
|
end
|
|
243
|
-
|
|
244
|
-
#
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
super
|
|
196
|
+
|
|
197
|
+
# Drops a schema from the database. Arguments:
|
|
198
|
+
# * name : name of the schema to drop
|
|
199
|
+
# * opts : options hash:
|
|
200
|
+
# * :cascade : Drop all objects in this schema.
|
|
201
|
+
# * :if_exists : Don't raise an error if the schema doesn't exist.
|
|
202
|
+
def drop_schema(name, opts={})
|
|
203
|
+
self << drop_schema_sql(name, opts)
|
|
252
204
|
end
|
|
253
205
|
|
|
254
206
|
# Drops a trigger from the database. Arguments:
|
|
@@ -260,7 +212,65 @@ module Sequel
|
|
|
260
212
|
def drop_trigger(table, name, opts={})
|
|
261
213
|
self << drop_trigger_sql(table, name, opts)
|
|
262
214
|
end
|
|
263
|
-
|
|
215
|
+
|
|
216
|
+
# Return full foreign key information using the pg system tables, including
|
|
217
|
+
# :name, :on_delete, :on_update, and :deferrable entries in the hashes.
|
|
218
|
+
def foreign_key_list(table, opts={})
|
|
219
|
+
m = output_identifier_meth
|
|
220
|
+
im = input_identifier_meth
|
|
221
|
+
schema, table = schema_and_table(table)
|
|
222
|
+
range = 0...32
|
|
223
|
+
|
|
224
|
+
base_ds = metadata_dataset.
|
|
225
|
+
where(:cl__relkind=>'r', :co__contype=>'f', :cl__relname=>im.call(table)).
|
|
226
|
+
from(:pg_constraint___co).
|
|
227
|
+
join(:pg_class___cl, :oid=>:conrelid)
|
|
228
|
+
|
|
229
|
+
# We split the parsing into two separate queries, which are merged manually later.
|
|
230
|
+
# This is because PostgreSQL stores both the referencing and referenced columns in
|
|
231
|
+
# arrays, and I don't know a simple way to not create a cross product, as PostgreSQL
|
|
232
|
+
# doesn't appear to have a function that takes an array and element and gives you
|
|
233
|
+
# the index of that element in the array.
|
|
234
|
+
|
|
235
|
+
ds = base_ds.
|
|
236
|
+
join(:pg_attribute___att, :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, :co__conkey)).
|
|
237
|
+
order(:co__conname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:co__conkey, [x]), x]}, 32, :att__attnum)).
|
|
238
|
+
select(:co__conname___name, :att__attname___column, :co__confupdtype___on_update, :co__confdeltype___on_delete,
|
|
239
|
+
SQL::BooleanExpression.new(:AND, :co__condeferrable, :co__condeferred).as(:deferrable))
|
|
240
|
+
|
|
241
|
+
ref_ds = base_ds.
|
|
242
|
+
join(:pg_class___cl2, :oid=>:co__confrelid).
|
|
243
|
+
join(:pg_attribute___att2, :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, :co__confkey)).
|
|
244
|
+
order(:co__conname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:co__conkey, [x]), x]}, 32, :att2__attnum)).
|
|
245
|
+
select(:co__conname___name, :cl2__relname___table, :att2__attname___refcolumn)
|
|
246
|
+
|
|
247
|
+
# If a schema is given, we only search in that schema, and the returned :table
|
|
248
|
+
# entry is schema qualified as well.
|
|
249
|
+
if schema
|
|
250
|
+
ds = ds.join(:pg_namespace___nsp, :oid=>:cl__relnamespace).
|
|
251
|
+
where(:nsp__nspname=>im.call(schema))
|
|
252
|
+
ref_ds = ref_ds.join(:pg_namespace___nsp2, :oid=>:cl2__relnamespace).
|
|
253
|
+
select_more(:nsp2__nspname___schema)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
h = {}
|
|
257
|
+
fklod_map = FOREIGN_KEY_LIST_ON_DELETE_MAP
|
|
258
|
+
ds.each do |row|
|
|
259
|
+
if r = h[row[:name]]
|
|
260
|
+
r[:columns] << m.call(row[:column])
|
|
261
|
+
else
|
|
262
|
+
h[row[:name]] = {:name=>m.call(row[:name]), :columns=>[m.call(row[:column])], :on_update=>fklod_map[row[:on_update]], :on_delete=>fklod_map[row[:on_delete]], :deferrable=>row[:deferrable]}
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
ref_ds.each do |row|
|
|
266
|
+
r = h[row[:name]]
|
|
267
|
+
r[:table] ||= schema ? SQL::QualifiedIdentifier.new(m.call(row[:schema]), m.call(row[:table])) : m.call(row[:table])
|
|
268
|
+
r[:key] ||= []
|
|
269
|
+
r[:key] << m.call(row[:refcolumn])
|
|
270
|
+
end
|
|
271
|
+
h.values
|
|
272
|
+
end
|
|
273
|
+
|
|
264
274
|
# Use the pg_* system tables to determine indexes on a table
|
|
265
275
|
def indexes(table, opts={})
|
|
266
276
|
m = output_identifier_meth
|
|
@@ -273,14 +283,13 @@ module Sequel
|
|
|
273
283
|
join(:pg_index___ind, :indrelid=>:oid, im.call(table)=>:relname).
|
|
274
284
|
join(:pg_class___indc, :oid=>:indexrelid).
|
|
275
285
|
join(:pg_attribute___att, :attrelid=>:tab__oid, :attnum=>attnums).
|
|
276
|
-
filter(:indc__relkind=>'i', :ind__indisprimary=>false, :indexprs=>nil, :indpred=>nil).
|
|
277
|
-
order(:indc__relname, range.map{|x| [SQL::Subscript.new(:ind__indkey, [x]), x]}
|
|
286
|
+
filter(:indc__relkind=>'i', :ind__indisprimary=>false, :indexprs=>nil, :indpred=>nil, :indisvalid=>true).
|
|
287
|
+
order(:indc__relname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:ind__indkey, [x]), x]}, 32, :att__attnum)).
|
|
278
288
|
select(:indc__relname___name, :ind__indisunique___unique, :att__attname___column)
|
|
279
|
-
|
|
289
|
+
|
|
280
290
|
ds.join!(:pg_namespace___nsp, :oid=>:tab__relnamespace, :nspname=>schema.to_s) if schema
|
|
281
|
-
ds.filter!(:indisvalid=>true) if server_version >= 80200
|
|
282
291
|
ds.filter!(:indisready=>true, :indcheckxmin=>false) if server_version >= 80300
|
|
283
|
-
|
|
292
|
+
|
|
284
293
|
indexes = {}
|
|
285
294
|
ds.each do |r|
|
|
286
295
|
i = indexes[m.call(r[:name])] ||= {:columns=>[], :unique=>r[:unique]}
|
|
@@ -289,40 +298,59 @@ module Sequel
|
|
|
289
298
|
indexes
|
|
290
299
|
end
|
|
291
300
|
|
|
292
|
-
# Dataset containing all current database locks
|
|
301
|
+
# Dataset containing all current database locks
|
|
293
302
|
def locks
|
|
294
303
|
dataset.from(:pg_class).join(:pg_locks, :relation=>:relfilenode).select(:pg_class__relname, Sequel::SQL::ColumnAll.new(:pg_locks))
|
|
295
304
|
end
|
|
296
|
-
|
|
305
|
+
|
|
306
|
+
# Notifies the given channel. See the PostgreSQL NOTIFY documentation. Options:
|
|
307
|
+
#
|
|
308
|
+
# :payload :: The payload string to use for the NOTIFY statement. Only supported
|
|
309
|
+
# in PostgreSQL 9.0+.
|
|
310
|
+
# :server :: The server to which to send the NOTIFY statement, if the sharding support
|
|
311
|
+
# is being used.
|
|
312
|
+
def notify(channel, opts={})
|
|
313
|
+
execute_ddl("NOTIFY #{channel}#{", #{literal(opts[:payload].to_s)}" if opts[:payload]}", opts)
|
|
314
|
+
end
|
|
315
|
+
|
|
297
316
|
# Return primary key for the given table.
|
|
298
317
|
def primary_key(table, opts={})
|
|
299
318
|
quoted_table = quote_schema_table(table)
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
319
|
+
@primary_keys.fetch(quoted_table) do
|
|
320
|
+
schema, table = schema_and_table(table)
|
|
321
|
+
sql = "#{SELECT_PK_SQL} AND pg_class.relname = #{literal(table)}"
|
|
322
|
+
sql << "AND pg_namespace.nspname = #{literal(schema)}" if schema
|
|
323
|
+
@primary_keys[quoted_table] = fetch(sql).single_value
|
|
305
324
|
end
|
|
306
325
|
end
|
|
307
|
-
|
|
326
|
+
|
|
308
327
|
# Return the sequence providing the default for the primary key for the given table.
|
|
309
328
|
def primary_key_sequence(table, opts={})
|
|
310
329
|
quoted_table = quote_schema_table(table)
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
330
|
+
@primary_key_sequences.fetch(quoted_table) do
|
|
331
|
+
schema, table = schema_and_table(table)
|
|
332
|
+
table = literal(table)
|
|
333
|
+
sql = "#{SELECT_SERIAL_SEQUENCE_SQL} AND seq.relname = #{table}"
|
|
334
|
+
sql << " AND name.nspname = #{literal(schema)}" if schema
|
|
335
|
+
unless pks = fetch(sql).single_record
|
|
336
|
+
sql = "#{SELECT_CUSTOM_SEQUENCE_SQL} AND t.relname = #{table}"
|
|
337
|
+
sql << " AND name.nspname = #{literal(schema)}" if schema
|
|
338
|
+
pks = fetch(sql).single_record
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
@primary_key_sequences[quoted_table] = if pks
|
|
342
|
+
literal(SQL::QualifiedIdentifier.new(pks[:schema], LiteralString.new(pks[:sequence])))
|
|
343
|
+
end
|
|
316
344
|
end
|
|
317
345
|
end
|
|
318
|
-
|
|
346
|
+
|
|
319
347
|
# Reset the primary key sequence for the given table, baseing it on the
|
|
320
348
|
# maximum current value of the table's primary key.
|
|
321
349
|
def reset_primary_key_sequence(table)
|
|
322
|
-
pk = SQL::Identifier.new(primary_key(table))
|
|
323
350
|
return unless seq = primary_key_sequence(table)
|
|
351
|
+
pk = SQL::Identifier.new(primary_key(table))
|
|
324
352
|
db = self
|
|
325
|
-
seq_ds = db.from(seq
|
|
353
|
+
seq_ds = db.from(LiteralString.new(seq))
|
|
326
354
|
get{setval(seq, db[table].select{coalesce(max(pk)+seq_ds.select{:increment_by}, seq_ds.select(:min_value))}, false)}
|
|
327
355
|
end
|
|
328
356
|
|
|
@@ -337,7 +365,7 @@ module Sequel
|
|
|
337
365
|
def serial_primary_key_options
|
|
338
366
|
{:primary_key => true, :serial => true, :type=>Integer}
|
|
339
367
|
end
|
|
340
|
-
|
|
368
|
+
|
|
341
369
|
# The version of the PostgreSQL server, used for determining capability.
|
|
342
370
|
def server_version(server=nil)
|
|
343
371
|
return @server_version if @server_version
|
|
@@ -345,12 +373,26 @@ module Sequel
|
|
|
345
373
|
(conn.server_version rescue nil) if conn.respond_to?(:server_version)
|
|
346
374
|
end
|
|
347
375
|
unless @server_version
|
|
348
|
-
m = /PostgreSQL (\d+)\.(\d+)(?:(?:rc\d+)|\.(\d+))?/.match(fetch('SELECT version()').single_value)
|
|
349
|
-
|
|
376
|
+
@server_version = if m = /PostgreSQL (\d+)\.(\d+)(?:(?:rc\d+)|\.(\d+))?/.match(fetch('SELECT version()').single_value)
|
|
377
|
+
(m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
|
|
378
|
+
else
|
|
379
|
+
0
|
|
380
|
+
end
|
|
350
381
|
end
|
|
382
|
+
warn 'Sequel no longer supports PostgreSQL <8.2, some things may not work' if @server_version < 80200
|
|
351
383
|
@server_version
|
|
352
384
|
end
|
|
353
|
-
|
|
385
|
+
|
|
386
|
+
# PostgreSQL supports CREATE TABLE IF NOT EXISTS on 9.1+
|
|
387
|
+
def supports_create_table_if_not_exists?
|
|
388
|
+
server_version >= 90100
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
# PostgreSQL supports DROP TABLE IF EXISTS
|
|
392
|
+
def supports_drop_table_if_exists?
|
|
393
|
+
true
|
|
394
|
+
end
|
|
395
|
+
|
|
354
396
|
# PostgreSQL supports prepared transactions (two-phase commit) if
|
|
355
397
|
# max_prepared_transactions is greater than 0.
|
|
356
398
|
def supports_prepared_transactions?
|
|
@@ -368,44 +410,95 @@ module Sequel
|
|
|
368
410
|
true
|
|
369
411
|
end
|
|
370
412
|
|
|
371
|
-
#
|
|
413
|
+
# PostgreSQL supports transaction DDL statements.
|
|
414
|
+
def supports_transactional_ddl?
|
|
415
|
+
true
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
# Array of symbols specifying table names in the current database.
|
|
419
|
+
# The dataset used is yielded to the block if one is provided,
|
|
420
|
+
# otherwise, an array of symbols of table names is returned.
|
|
372
421
|
#
|
|
373
422
|
# Options:
|
|
374
423
|
# * :schema - The schema to search (default_schema by default)
|
|
375
424
|
# * :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?}
|
|
425
|
+
def tables(opts={}, &block)
|
|
426
|
+
pg_class_relname('r', opts, &block)
|
|
381
427
|
end
|
|
382
|
-
|
|
383
|
-
#
|
|
384
|
-
#
|
|
385
|
-
|
|
428
|
+
|
|
429
|
+
# Check whether the given type name string/symbol (e.g. :hstore) is supported by
|
|
430
|
+
# the database.
|
|
431
|
+
def type_supported?(type)
|
|
432
|
+
@supported_types ||= {}
|
|
433
|
+
@supported_types.fetch(type){@supported_types[type] = (from(:pg_type).filter(:typtype=>'b', :typname=>type.to_s).count > 0)}
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
# Array of symbols specifying view names in the current database.
|
|
386
437
|
#
|
|
387
438
|
# Options:
|
|
388
439
|
# * :schema - The schema to search (default_schema by default)
|
|
389
440
|
# * :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])}
|
|
441
|
+
def views(opts={})
|
|
442
|
+
pg_class_relname('v', opts)
|
|
395
443
|
end
|
|
396
444
|
|
|
397
445
|
private
|
|
398
446
|
|
|
447
|
+
# Handle :using option for set_column_type op.
|
|
448
|
+
def alter_table_sql(table, op)
|
|
449
|
+
case op[:op]
|
|
450
|
+
when :set_column_type
|
|
451
|
+
s = super
|
|
452
|
+
if using = op[:using]
|
|
453
|
+
using = Sequel::LiteralString.new(using) if using.is_a?(String)
|
|
454
|
+
s << ' USING '
|
|
455
|
+
s << literal(using)
|
|
456
|
+
end
|
|
457
|
+
s
|
|
458
|
+
else
|
|
459
|
+
super
|
|
460
|
+
end
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
# If the :synchronous option is given and non-nil, set synchronous_commit
|
|
464
|
+
# appropriately. Valid values for the :synchronous option are true,
|
|
465
|
+
# :on, false, :off, :local, and :remote_write.
|
|
466
|
+
def begin_new_transaction(conn, opts)
|
|
467
|
+
super
|
|
468
|
+
if opts.has_key?(:synchronous)
|
|
469
|
+
case sync = opts[:synchronous]
|
|
470
|
+
when true
|
|
471
|
+
sync = :on
|
|
472
|
+
when false
|
|
473
|
+
sync = :off
|
|
474
|
+
when nil
|
|
475
|
+
return
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
log_connection_execute(conn, "SET LOCAL synchronous_commit = #{sync}")
|
|
479
|
+
end
|
|
480
|
+
end
|
|
481
|
+
|
|
399
482
|
# If the :prepare option is given and we aren't in a savepoint,
|
|
400
483
|
# prepare the transaction for a two-phase commit.
|
|
401
484
|
def commit_transaction(conn, opts={})
|
|
402
|
-
if opts[:prepare] &&
|
|
403
|
-
log_connection_execute(conn, "PREPARE TRANSACTION #{literal(
|
|
485
|
+
if (s = opts[:prepare]) && _trans(conn)[:savepoint_level] <= 1
|
|
486
|
+
log_connection_execute(conn, "PREPARE TRANSACTION #{literal(s)}")
|
|
404
487
|
else
|
|
405
488
|
super
|
|
406
489
|
end
|
|
407
490
|
end
|
|
408
491
|
|
|
492
|
+
# The SQL queries to execute when starting a new connection.
|
|
493
|
+
def connection_configuration_sqls
|
|
494
|
+
sqls = []
|
|
495
|
+
sqls << "SET standard_conforming_strings = ON" if Postgres.force_standard_strings
|
|
496
|
+
if cmm = Postgres.client_min_messages
|
|
497
|
+
sqls << "SET client_min_messages = '#{cmm.to_s.upcase}'"
|
|
498
|
+
end
|
|
499
|
+
sqls
|
|
500
|
+
end
|
|
501
|
+
|
|
409
502
|
# SQL statement to create database function.
|
|
410
503
|
def create_function_sql(name, definition, opts={})
|
|
411
504
|
args = opts[:args]
|
|
@@ -426,39 +519,49 @@ module Sequel
|
|
|
426
519
|
AS #{literal(definition.to_s)}#{", #{literal(opts[:link_symbol].to_s)}" if opts[:link_symbol]}
|
|
427
520
|
END
|
|
428
521
|
end
|
|
429
|
-
|
|
522
|
+
|
|
430
523
|
# SQL for creating a procedural language.
|
|
431
524
|
def create_language_sql(name, opts={})
|
|
432
525
|
"CREATE#{' OR REPLACE' if opts[:replace] && server_version >= 90000}#{' TRUSTED' if opts[:trusted]} LANGUAGE #{name}#{" HANDLER #{opts[:handler]}" if opts[:handler]}#{" VALIDATOR #{opts[:validator]}" if opts[:validator]}"
|
|
433
526
|
end
|
|
434
|
-
|
|
435
|
-
# SQL for creating a
|
|
527
|
+
|
|
528
|
+
# SQL for creating a schema.
|
|
529
|
+
def create_schema_sql(name)
|
|
530
|
+
"CREATE SCHEMA #{quote_identifier(name)}"
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
# SQL for creating a database trigger.
|
|
436
534
|
def create_trigger_sql(table, name, function, opts={})
|
|
437
535
|
events = opts[:events] ? Array(opts[:events]) : [:insert, :update, :delete]
|
|
438
536
|
whence = opts[:after] ? 'AFTER' : 'BEFORE'
|
|
439
537
|
"CREATE TRIGGER #{name} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} ON #{quote_schema_table(table)}#{' FOR EACH ROW' if opts[:each_row]} EXECUTE PROCEDURE #{function}(#{Array(opts[:args]).map{|a| literal(a)}.join(', ')})"
|
|
440
538
|
end
|
|
441
|
-
|
|
539
|
+
|
|
442
540
|
# The errors that the main adapters can raise, depends on the adapter being used
|
|
443
541
|
def database_error_classes
|
|
444
542
|
CONVERTED_EXCEPTIONS
|
|
445
543
|
end
|
|
446
|
-
|
|
447
|
-
# SQL for dropping a function from the database.
|
|
544
|
+
|
|
545
|
+
# SQL for dropping a function from the database.
|
|
448
546
|
def drop_function_sql(name, opts={})
|
|
449
547
|
"DROP FUNCTION#{' IF EXISTS' if opts[:if_exists]} #{name}#{sql_function_args(opts[:args])}#{' CASCADE' if opts[:cascade]}"
|
|
450
548
|
end
|
|
451
549
|
|
|
550
|
+
# Support :if_exists, :cascade, and :concurrently options.
|
|
551
|
+
def drop_index_sql(table, op)
|
|
552
|
+
"DROP INDEX#{' CONCURRENTLY' if op[:concurrently]}#{' IF EXISTS' if op[:if_exists]} #{quote_identifier(op[:name] || default_index_name(table, op[:columns]))}#{' CASCADE' if op[:cascade]}"
|
|
553
|
+
end
|
|
554
|
+
|
|
452
555
|
# SQL for dropping a procedural language from the database.
|
|
453
556
|
def drop_language_sql(name, opts={})
|
|
454
557
|
"DROP LANGUAGE#{' IF EXISTS' if opts[:if_exists]} #{name}#{' CASCADE' if opts[:cascade]}"
|
|
455
558
|
end
|
|
456
559
|
|
|
457
|
-
#
|
|
458
|
-
def
|
|
459
|
-
"DROP
|
|
560
|
+
# SQL for dropping a schema from the database.
|
|
561
|
+
def drop_schema_sql(name, opts={})
|
|
562
|
+
"DROP SCHEMA#{' IF EXISTS' if opts[:if_exists]} #{quote_identifier(name)}#{' CASCADE' if opts[:cascade]}"
|
|
460
563
|
end
|
|
461
|
-
|
|
564
|
+
|
|
462
565
|
# SQL for dropping a trigger from the database.
|
|
463
566
|
def drop_trigger_sql(table, name, opts={})
|
|
464
567
|
"DROP TRIGGER#{' IF EXISTS' if opts[:if_exists]} #{name} ON #{quote_schema_table(table)}#{' CASCADE' if opts[:cascade]}"
|
|
@@ -473,12 +576,12 @@ module Sequel
|
|
|
473
576
|
ds.exclude(:pg_namespace__nspname=>EXCLUDE_SCHEMAS)
|
|
474
577
|
end
|
|
475
578
|
end
|
|
476
|
-
|
|
579
|
+
|
|
477
580
|
# PostgreSQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
|
|
478
581
|
def identifier_input_method_default
|
|
479
582
|
nil
|
|
480
583
|
end
|
|
481
|
-
|
|
584
|
+
|
|
482
585
|
# PostgreSQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on output.
|
|
483
586
|
def identifier_output_method_default
|
|
484
587
|
nil
|
|
@@ -488,7 +591,7 @@ module Sequel
|
|
|
488
591
|
def index_definition_sql(table_name, index)
|
|
489
592
|
cols = index[:columns]
|
|
490
593
|
index_name = index[:name] || default_index_name(table_name, cols)
|
|
491
|
-
expr = if o = index[:opclass]
|
|
594
|
+
expr = if o = index[:opclass]
|
|
492
595
|
"(#{Array(cols).map{|c| "#{literal(c)} #{o}"}.join(', ')})"
|
|
493
596
|
else
|
|
494
597
|
literal(Array(cols))
|
|
@@ -499,58 +602,42 @@ module Sequel
|
|
|
499
602
|
filter = " WHERE #{filter_expr(filter)}" if filter
|
|
500
603
|
case index_type
|
|
501
604
|
when :full_text
|
|
502
|
-
expr = "(to_tsvector(#{literal(index[:language] || 'simple')}, #{dataset.send(:full_text_string_join, cols)}))"
|
|
605
|
+
expr = "(to_tsvector(#{literal(index[:language] || 'simple')}::regconfig, #{literal(dataset.send(:full_text_string_join, cols))}))"
|
|
503
606
|
index_type = :gin
|
|
504
607
|
when :spatial
|
|
505
608
|
index_type = :gist
|
|
506
609
|
end
|
|
507
|
-
"CREATE #{unique}INDEX #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{filter}"
|
|
508
|
-
end
|
|
509
|
-
|
|
510
|
-
# The result of the insert for the given table and values. If values
|
|
511
|
-
# is an array, assume the first column is the primary key and return
|
|
512
|
-
# that. If values is a hash, lookup the primary key for the table. If
|
|
513
|
-
# the primary key is present in the hash, return its value. Otherwise,
|
|
514
|
-
# look up the sequence for the table's primary key. If one exists,
|
|
515
|
-
# return the last value the of the sequence for the connection.
|
|
516
|
-
def insert_result(conn, table, values)
|
|
517
|
-
case values
|
|
518
|
-
when Hash
|
|
519
|
-
return nil unless pk = primary_key(table, :conn=>conn)
|
|
520
|
-
if pk and pkv = values[pk.to_sym]
|
|
521
|
-
pkv
|
|
522
|
-
else
|
|
523
|
-
begin
|
|
524
|
-
if seq = primary_key_sequence(table, :conn=>conn)
|
|
525
|
-
conn.last_insert_id(seq)
|
|
526
|
-
end
|
|
527
|
-
rescue Exception => e
|
|
528
|
-
raise_error(e, :classes=>CONVERTED_EXCEPTIONS) unless RE_CURRVAL_ERROR.match(e.message)
|
|
529
|
-
end
|
|
530
|
-
end
|
|
531
|
-
when Array
|
|
532
|
-
values.first
|
|
533
|
-
else
|
|
534
|
-
nil
|
|
535
|
-
end
|
|
610
|
+
"CREATE #{unique}INDEX#{' CONCURRENTLY' if index[:concurrently]} #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{filter}"
|
|
536
611
|
end
|
|
537
612
|
|
|
538
|
-
#
|
|
539
|
-
def
|
|
540
|
-
|
|
613
|
+
# Backbone of the tables and views support.
|
|
614
|
+
def pg_class_relname(type, opts)
|
|
615
|
+
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)
|
|
616
|
+
ds = filter_schema(ds, opts)
|
|
617
|
+
m = output_identifier_meth
|
|
618
|
+
block_given? ? yield(ds) : ds.map{|r| m.call(r[:relname])}
|
|
541
619
|
end
|
|
542
|
-
|
|
620
|
+
|
|
543
621
|
# Use a dollar sign instead of question mark for the argument
|
|
544
622
|
# placeholder.
|
|
545
623
|
def prepared_arg_placeholder
|
|
546
624
|
PREPARED_ARG_PLACEHOLDER
|
|
547
625
|
end
|
|
548
|
-
|
|
626
|
+
|
|
627
|
+
# Remove the cached entries for primary keys and sequences when a table is
|
|
628
|
+
# changed.
|
|
629
|
+
def remove_cached_schema(table)
|
|
630
|
+
tab = quote_schema_table(table)
|
|
631
|
+
@primary_keys.delete(tab)
|
|
632
|
+
@primary_key_sequences.delete(tab)
|
|
633
|
+
super
|
|
634
|
+
end
|
|
635
|
+
|
|
549
636
|
# SQL DDL statement for renaming a table. PostgreSQL doesn't allow you to change a table's schema in
|
|
550
637
|
# a rename table operation, so speciying a new schema in new_name will not have an effect.
|
|
551
638
|
def rename_table_sql(name, new_name)
|
|
552
639
|
"ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_identifier(schema_and_table(new_name).last)}"
|
|
553
|
-
end
|
|
640
|
+
end
|
|
554
641
|
|
|
555
642
|
# PostgreSQL's autoincrementing primary keys are of type integer or bigint
|
|
556
643
|
# using a nextval function call as a default.
|
|
@@ -560,13 +647,14 @@ module Sequel
|
|
|
560
647
|
|
|
561
648
|
# The dataset used for parsing table schemas, using the pg_* system catalogs.
|
|
562
649
|
def schema_parse_table(table_name, opts)
|
|
563
|
-
m = output_identifier_meth
|
|
564
|
-
m2 = input_identifier_meth
|
|
650
|
+
m = output_identifier_meth(opts[:dataset])
|
|
651
|
+
m2 = input_identifier_meth(opts[:dataset])
|
|
565
652
|
ds = metadata_dataset.select(:pg_attribute__attname___name,
|
|
566
653
|
SQL::Function.new(:format_type, :pg_type__oid, :pg_attribute__atttypmod).as(:db_type),
|
|
567
654
|
SQL::Function.new(:pg_get_expr, :pg_attrdef__adbin, :pg_class__oid).as(:default),
|
|
568
655
|
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)
|
|
656
|
+
SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(:pg_attribute__attnum => SQL::Function.new(:ANY, :pg_index__indkey)), false).as(:primary_key),
|
|
657
|
+
:pg_namespace__nspname).
|
|
570
658
|
from(:pg_class).
|
|
571
659
|
join(:pg_attribute, :attrelid=>:oid).
|
|
572
660
|
join(:pg_type, :oid=>:atttypid).
|
|
@@ -578,18 +666,41 @@ module Sequel
|
|
|
578
666
|
filter(:pg_class__relname=>m2.call(table_name)).
|
|
579
667
|
order(:pg_attribute__attnum)
|
|
580
668
|
ds = filter_schema(ds, opts)
|
|
669
|
+
current_schema = nil
|
|
581
670
|
ds.map do |row|
|
|
671
|
+
sch = row.delete(:nspname)
|
|
672
|
+
if current_schema
|
|
673
|
+
if sch != current_schema
|
|
674
|
+
raise Error, "columns from tables in two separate schema were returned (please specify a schema): #{current_schema.inspect}, #{sch.inspect}"
|
|
675
|
+
end
|
|
676
|
+
else
|
|
677
|
+
current_schema = sch
|
|
678
|
+
end
|
|
582
679
|
row[:default] = nil if blank_object?(row[:default])
|
|
583
680
|
row[:type] = schema_column_type(row[:db_type])
|
|
584
681
|
[m.call(row.delete(:name)), row]
|
|
585
682
|
end
|
|
586
683
|
end
|
|
587
684
|
|
|
685
|
+
# Set the transaction isolation level on the given connection
|
|
686
|
+
def set_transaction_isolation(conn, opts)
|
|
687
|
+
level = opts.fetch(:isolation, transaction_isolation_level)
|
|
688
|
+
read_only = opts[:read_only]
|
|
689
|
+
deferrable = opts[:deferrable]
|
|
690
|
+
if level || !read_only.nil? || !deferrable.nil?
|
|
691
|
+
sql = "SET TRANSACTION"
|
|
692
|
+
sql << " ISOLATION LEVEL #{Sequel::Database::TRANSACTION_ISOLATION_LEVELS[level]}" if level
|
|
693
|
+
sql << " READ #{read_only ? 'ONLY' : 'WRITE'}" unless read_only.nil?
|
|
694
|
+
sql << " #{'NOT ' unless deferrable}DEFERRABLE" unless deferrable.nil?
|
|
695
|
+
log_connection_execute(conn, sql)
|
|
696
|
+
end
|
|
697
|
+
end
|
|
698
|
+
|
|
588
699
|
# Turns an array of argument specifiers into an SQL fragment used for function arguments. See create_function_sql.
|
|
589
700
|
def sql_function_args(args)
|
|
590
701
|
"(#{Array(args).map{|a| Array(a).reverse.join(' ')}.join(', ')})"
|
|
591
702
|
end
|
|
592
|
-
|
|
703
|
+
|
|
593
704
|
# Handle bigserial type if :serial option is present
|
|
594
705
|
def type_literal_generic_bignum(column)
|
|
595
706
|
column[:serial] ? :bigserial : super
|
|
@@ -619,7 +730,7 @@ module Sequel
|
|
|
619
730
|
end
|
|
620
731
|
end
|
|
621
732
|
end
|
|
622
|
-
|
|
733
|
+
|
|
623
734
|
# Instance methods for datasets that connect to a PostgreSQL database.
|
|
624
735
|
module DatasetMethods
|
|
625
736
|
ACCESS_SHARE = 'ACCESS SHARE'.freeze
|
|
@@ -627,107 +738,123 @@ module Sequel
|
|
|
627
738
|
BOOL_FALSE = 'false'.freeze
|
|
628
739
|
BOOL_TRUE = 'true'.freeze
|
|
629
740
|
COMMA_SEPARATOR = ', '.freeze
|
|
630
|
-
DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'from using where')
|
|
741
|
+
DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'delete from using where returning')
|
|
742
|
+
DELETE_CLAUSE_METHODS_91 = Dataset.clause_methods(:delete, %w'with delete from using where returning')
|
|
631
743
|
EXCLUSIVE = 'EXCLUSIVE'.freeze
|
|
632
744
|
EXPLAIN = 'EXPLAIN '.freeze
|
|
633
745
|
EXPLAIN_ANALYZE = 'EXPLAIN ANALYZE '.freeze
|
|
634
746
|
FOR_SHARE = ' FOR SHARE'.freeze
|
|
747
|
+
INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'insert into columns values returning')
|
|
748
|
+
INSERT_CLAUSE_METHODS_91 = Dataset.clause_methods(:insert, %w'with insert into columns values returning')
|
|
635
749
|
LOCK = 'LOCK TABLE %s IN %s MODE'.freeze
|
|
636
750
|
NULL = LiteralString.new('NULL').freeze
|
|
637
751
|
PG_TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S".freeze
|
|
638
752
|
QUERY_PLAN = 'QUERY PLAN'.to_sym
|
|
639
753
|
ROW_EXCLUSIVE = 'ROW EXCLUSIVE'.freeze
|
|
640
754
|
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')
|
|
755
|
+
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select distinct columns from join where group having compounds order limit lock')
|
|
756
|
+
SELECT_CLAUSE_METHODS_84 = Dataset.clause_methods(:select, %w'with select distinct columns from join where group having window compounds order limit lock')
|
|
643
757
|
SHARE = 'SHARE'.freeze
|
|
644
758
|
SHARE_ROW_EXCLUSIVE = 'SHARE ROW EXCLUSIVE'.freeze
|
|
645
759
|
SHARE_UPDATE_EXCLUSIVE = 'SHARE UPDATE EXCLUSIVE'.freeze
|
|
646
760
|
SQL_WITH_RECURSIVE = "WITH RECURSIVE ".freeze
|
|
647
|
-
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'table set from where')
|
|
648
|
-
|
|
761
|
+
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'update table set from where returning')
|
|
762
|
+
UPDATE_CLAUSE_METHODS_91 = Dataset.clause_methods(:update, %w'with update table set from where returning')
|
|
763
|
+
SPACE = Dataset::SPACE
|
|
764
|
+
FROM = Dataset::FROM
|
|
765
|
+
APOS = Dataset::APOS
|
|
766
|
+
APOS_RE = Dataset::APOS_RE
|
|
767
|
+
DOUBLE_APOS = Dataset::DOUBLE_APOS
|
|
768
|
+
PAREN_OPEN = Dataset::PAREN_OPEN
|
|
769
|
+
PAREN_CLOSE = Dataset::PAREN_CLOSE
|
|
770
|
+
COMMA = Dataset::COMMA
|
|
771
|
+
AS = Dataset::AS
|
|
772
|
+
XOR_OP = ' # '.freeze
|
|
773
|
+
CRLF = "\r\n".freeze
|
|
774
|
+
BLOB_RE = /[\000-\037\047\134\177-\377]/n.freeze
|
|
775
|
+
WINDOW = " WINDOW ".freeze
|
|
776
|
+
EMPTY_STRING = ''.freeze
|
|
777
|
+
|
|
649
778
|
# Shared methods for prepared statements when used with PostgreSQL databases.
|
|
650
779
|
module PreparedStatementMethods
|
|
651
780
|
# Override insert action to use RETURNING if the server supports it.
|
|
781
|
+
def run
|
|
782
|
+
if @prepared_type == :insert
|
|
783
|
+
fetch_rows(prepared_sql){|r| return r.values.first}
|
|
784
|
+
else
|
|
785
|
+
super
|
|
786
|
+
end
|
|
787
|
+
end
|
|
788
|
+
|
|
652
789
|
def prepared_sql
|
|
653
790
|
return @prepared_sql if @prepared_sql
|
|
791
|
+
@opts[:returning] = insert_pk if @prepared_type == :insert
|
|
654
792
|
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
793
|
@prepared_sql
|
|
660
794
|
end
|
|
661
795
|
end
|
|
662
796
|
|
|
663
|
-
#
|
|
664
|
-
def self.extended(obj)
|
|
665
|
-
obj.def_mutation_method(:disable_insert_returning)
|
|
666
|
-
end
|
|
667
|
-
|
|
668
|
-
# Add the disable_insert_returning! mutation method
|
|
669
|
-
def self.included(mod)
|
|
670
|
-
mod.def_mutation_method(:disable_insert_returning)
|
|
671
|
-
end
|
|
672
|
-
|
|
673
|
-
# Return the results of an ANALYZE query as a string
|
|
797
|
+
# Return the results of an EXPLAIN ANALYZE query as a string
|
|
674
798
|
def analyze
|
|
675
799
|
explain(:analyze=>true)
|
|
676
800
|
end
|
|
677
|
-
|
|
801
|
+
|
|
678
802
|
# Handle converting the ruby xor operator (^) into the
|
|
679
803
|
# PostgreSQL xor operator (#).
|
|
680
|
-
def
|
|
804
|
+
def complex_expression_sql_append(sql, op, args)
|
|
681
805
|
case op
|
|
682
806
|
when :^
|
|
683
|
-
|
|
807
|
+
j = XOR_OP
|
|
808
|
+
c = false
|
|
809
|
+
args.each do |a|
|
|
810
|
+
sql << j if c
|
|
811
|
+
literal_append(sql, a)
|
|
812
|
+
c ||= true
|
|
813
|
+
end
|
|
684
814
|
else
|
|
685
815
|
super
|
|
686
816
|
end
|
|
687
817
|
end
|
|
688
818
|
|
|
689
|
-
# Disable the use of INSERT RETURNING, even if the server supports it
|
|
690
|
-
def disable_insert_returning
|
|
691
|
-
clone(:disable_insert_returning=>true)
|
|
692
|
-
end
|
|
693
|
-
|
|
694
819
|
# Return the results of an EXPLAIN query as a string
|
|
695
820
|
def explain(opts={})
|
|
696
|
-
with_sql((opts[:analyze] ? EXPLAIN_ANALYZE : EXPLAIN) + select_sql).map(QUERY_PLAN).join(
|
|
821
|
+
with_sql((opts[:analyze] ? EXPLAIN_ANALYZE : EXPLAIN) + select_sql).map(QUERY_PLAN).join(CRLF)
|
|
697
822
|
end
|
|
698
|
-
|
|
823
|
+
|
|
699
824
|
# Return a cloned dataset which will use FOR SHARE to lock returned rows.
|
|
700
825
|
def for_share
|
|
701
826
|
lock_style(:share)
|
|
702
827
|
end
|
|
703
|
-
|
|
828
|
+
|
|
704
829
|
# PostgreSQL specific full text search syntax, using tsearch2 (included
|
|
705
830
|
# in 8.3 by default, and available for earlier versions as an add-on).
|
|
706
831
|
def full_text_search(cols, terms, opts = {})
|
|
707
832
|
lang = opts[:language] || 'simple'
|
|
708
|
-
|
|
833
|
+
terms = terms.join(' | ') if terms.is_a?(Array)
|
|
834
|
+
filter("to_tsvector(?::regconfig, ?) @@ to_tsquery(?::regconfig, ?)", lang, full_text_string_join(cols), lang, terms)
|
|
709
835
|
end
|
|
710
|
-
|
|
836
|
+
|
|
711
837
|
# Insert given values into the database.
|
|
712
838
|
def insert(*values)
|
|
713
|
-
if @opts[:
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
839
|
+
if @opts[:returning]
|
|
840
|
+
# already know which columns to return, let the standard code
|
|
841
|
+
# handle it
|
|
842
|
+
super
|
|
843
|
+
elsif @opts[:sql]
|
|
844
|
+
# raw SQL used, so don't know which table is being inserted
|
|
845
|
+
# into, and therefore can't determine primary key. Run the
|
|
846
|
+
# insert statement and return nil.
|
|
847
|
+
super
|
|
848
|
+
nil
|
|
717
849
|
else
|
|
718
|
-
|
|
850
|
+
# Force the use of RETURNING with the primary key value.
|
|
851
|
+
returning(insert_pk).insert(*values){|r| return r.values.first}
|
|
719
852
|
end
|
|
720
853
|
end
|
|
721
854
|
|
|
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
855
|
# Insert a record returning the record inserted
|
|
728
856
|
def insert_select(*values)
|
|
729
|
-
return
|
|
730
|
-
naked.clone(default_server_opts(:sql=>insert_returning_sql(nil, *values))).single_record
|
|
857
|
+
returning.insert(*values){|r| return r}
|
|
731
858
|
end
|
|
732
859
|
|
|
733
860
|
# Locks all tables in the dataset's FROM clause (but not in JOINs) with
|
|
@@ -743,50 +870,119 @@ module Sequel
|
|
|
743
870
|
end
|
|
744
871
|
nil
|
|
745
872
|
end
|
|
746
|
-
|
|
747
|
-
#
|
|
873
|
+
|
|
874
|
+
# PostgreSQL allows inserting multiple rows at once.
|
|
748
875
|
def multi_insert_sql(columns, values)
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
[insert_sql(columns, LiteralString.new('VALUES ' + values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)))]
|
|
876
|
+
sql = LiteralString.new('VALUES ')
|
|
877
|
+
expression_list_append(sql, values.map{|r| Array(r)})
|
|
878
|
+
[insert_sql(columns, sql)]
|
|
753
879
|
end
|
|
754
|
-
|
|
880
|
+
|
|
881
|
+
# PostgreSQL supports using the WITH clause in subqueries if it
|
|
882
|
+
# supports using WITH at all (i.e. on PostgreSQL 8.4+).
|
|
883
|
+
def supports_cte_in_subqueries?
|
|
884
|
+
supports_cte?
|
|
885
|
+
end
|
|
886
|
+
|
|
755
887
|
# DISTINCT ON is a PostgreSQL extension
|
|
756
888
|
def supports_distinct_on?
|
|
757
889
|
true
|
|
758
890
|
end
|
|
759
|
-
|
|
891
|
+
|
|
760
892
|
# PostgreSQL supports modifying joined datasets
|
|
761
893
|
def supports_modifying_joins?
|
|
762
894
|
true
|
|
763
895
|
end
|
|
764
896
|
|
|
897
|
+
# Returning is always supported.
|
|
898
|
+
def supports_returning?(type)
|
|
899
|
+
true
|
|
900
|
+
end
|
|
901
|
+
|
|
765
902
|
# PostgreSQL supports timezones in literal timestamps
|
|
766
903
|
def supports_timestamp_timezones?
|
|
767
904
|
true
|
|
768
905
|
end
|
|
769
|
-
|
|
906
|
+
|
|
770
907
|
# PostgreSQL 8.4+ supports window functions
|
|
771
908
|
def supports_window_functions?
|
|
772
909
|
server_version >= 80400
|
|
773
910
|
end
|
|
774
911
|
|
|
912
|
+
# Truncates the dataset. Returns nil.
|
|
913
|
+
#
|
|
914
|
+
# Options:
|
|
915
|
+
# :cascade :: whether to use the CASCADE option, useful when truncating
|
|
916
|
+
# tables with Foreign Keys.
|
|
917
|
+
# :only :: truncate using ONLY, so child tables are unaffected
|
|
918
|
+
# :restart :: use RESTART IDENTITY to restart any related sequences
|
|
919
|
+
#
|
|
920
|
+
# :only and :restart only work correctly on PostgreSQL 8.4+.
|
|
921
|
+
#
|
|
922
|
+
# Usage:
|
|
923
|
+
# DB[:table].truncate # TRUNCATE TABLE "table"
|
|
924
|
+
# # => nil
|
|
925
|
+
# DB[:table].truncate(:cascade => true, :only=>true, :restart=>true) # TRUNCATE TABLE ONLY "table" RESTART IDENTITY CASCADE
|
|
926
|
+
# # => nil
|
|
927
|
+
def truncate(opts = {})
|
|
928
|
+
if opts.empty?
|
|
929
|
+
super()
|
|
930
|
+
else
|
|
931
|
+
clone(:truncate_opts=>opts).truncate
|
|
932
|
+
end
|
|
933
|
+
end
|
|
934
|
+
|
|
775
935
|
# Return a clone of the dataset with an addition named window that can be referenced in window functions.
|
|
776
936
|
def window(name, opts)
|
|
777
937
|
clone(:window=>(@opts[:window]||[]) + [[name, SQL::Window.new(opts)]])
|
|
778
938
|
end
|
|
779
|
-
|
|
939
|
+
|
|
940
|
+
protected
|
|
941
|
+
|
|
942
|
+
# If returned primary keys are requested, use RETURNING unless already set on the
|
|
943
|
+
# dataset. If RETURNING is already set, use existing returning values. If RETURNING
|
|
944
|
+
# is only set to return a single columns, return an array of just that column.
|
|
945
|
+
# Otherwise, return an array of hashes.
|
|
946
|
+
def _import(columns, values, opts={})
|
|
947
|
+
if @opts[:returning]
|
|
948
|
+
statements = multi_insert_sql(columns, values)
|
|
949
|
+
@db.transaction(opts.merge(:server=>@opts[:server])) do
|
|
950
|
+
statements.map{|st| returning_fetch_rows(st)}
|
|
951
|
+
end.first.map{|v| v.length == 1 ? v.values.first : v}
|
|
952
|
+
elsif opts[:return] == :primary_key
|
|
953
|
+
returning(insert_pk)._import(columns, values, opts)
|
|
954
|
+
else
|
|
955
|
+
super
|
|
956
|
+
end
|
|
957
|
+
end
|
|
958
|
+
|
|
780
959
|
private
|
|
781
|
-
|
|
960
|
+
|
|
961
|
+
# Format TRUNCATE statement with PostgreSQL specific options.
|
|
962
|
+
def _truncate_sql(table)
|
|
963
|
+
to = @opts[:truncate_opts] || {}
|
|
964
|
+
"TRUNCATE TABLE#{' ONLY' if to[:only]} #{table}#{' RESTART IDENTITY' if to[:restart]}#{' CASCADE' if to[:cascade]}"
|
|
965
|
+
end
|
|
966
|
+
|
|
967
|
+
# Allow truncation of multiple source tables.
|
|
968
|
+
def check_truncation_allowed!
|
|
969
|
+
raise(InvalidOperation, "Grouped datasets cannot be truncated") if opts[:group]
|
|
970
|
+
raise(InvalidOperation, "Joined datasets cannot be truncated") if opts[:join]
|
|
971
|
+
end
|
|
972
|
+
|
|
782
973
|
# PostgreSQL allows deleting from joined datasets
|
|
783
974
|
def delete_clause_methods
|
|
784
|
-
|
|
785
|
-
|
|
975
|
+
if server_version >= 90100
|
|
976
|
+
DELETE_CLAUSE_METHODS_91
|
|
977
|
+
else
|
|
978
|
+
DELETE_CLAUSE_METHODS
|
|
979
|
+
end
|
|
980
|
+
end
|
|
786
981
|
|
|
787
982
|
# Only include the primary table in the main delete clause
|
|
788
983
|
def delete_from_sql(sql)
|
|
789
|
-
sql <<
|
|
984
|
+
sql << FROM
|
|
985
|
+
source_list_append(sql, @opts[:from][0..0])
|
|
790
986
|
end
|
|
791
987
|
|
|
792
988
|
# Use USING to specify additional tables in a delete query
|
|
@@ -794,36 +990,60 @@ module Sequel
|
|
|
794
990
|
join_from_sql(:USING, sql)
|
|
795
991
|
end
|
|
796
992
|
|
|
797
|
-
#
|
|
798
|
-
def
|
|
799
|
-
|
|
800
|
-
|
|
993
|
+
# PostgreSQL allows a RETURNING clause.
|
|
994
|
+
def insert_clause_methods
|
|
995
|
+
if server_version >= 90100
|
|
996
|
+
INSERT_CLAUSE_METHODS_91
|
|
997
|
+
else
|
|
998
|
+
INSERT_CLAUSE_METHODS
|
|
999
|
+
end
|
|
801
1000
|
end
|
|
802
|
-
|
|
1001
|
+
|
|
1002
|
+
# Return the primary key to use for RETURNING in an INSERT statement
|
|
1003
|
+
def insert_pk
|
|
1004
|
+
if (f = opts[:from]) && !f.empty? && (pk = db.primary_key(f.first))
|
|
1005
|
+
Sequel::SQL::Identifier.new(pk)
|
|
1006
|
+
end
|
|
1007
|
+
end
|
|
1008
|
+
|
|
803
1009
|
# For multiple table support, PostgreSQL requires at least
|
|
804
1010
|
# two from tables, with joins allowed.
|
|
805
1011
|
def join_from_sql(type, sql)
|
|
806
1012
|
if(from = @opts[:from][1..-1]).empty?
|
|
807
1013
|
raise(Error, 'Need multiple FROM tables if updating/deleting a dataset with JOINs') if @opts[:join]
|
|
808
1014
|
else
|
|
809
|
-
sql <<
|
|
1015
|
+
sql << SPACE << type.to_s << SPACE
|
|
1016
|
+
source_list_append(sql, from)
|
|
810
1017
|
select_join_sql(sql)
|
|
811
1018
|
end
|
|
812
1019
|
end
|
|
813
1020
|
|
|
814
1021
|
# Use a generic blob quoting method, hopefully overridden in one of the subadapter methods
|
|
815
|
-
def
|
|
816
|
-
|
|
1022
|
+
def literal_blob_append(sql, v)
|
|
1023
|
+
sql << APOS << v.gsub(BLOB_RE){|b| "\\#{("%o" % b[0..1].unpack("C")[0]).rjust(3, '0')}"} << APOS
|
|
817
1024
|
end
|
|
818
1025
|
|
|
819
1026
|
# PostgreSQL uses FALSE for false values
|
|
820
1027
|
def literal_false
|
|
821
1028
|
BOOL_FALSE
|
|
822
1029
|
end
|
|
1030
|
+
|
|
1031
|
+
# PostgreSQL quotes NaN and Infinity.
|
|
1032
|
+
def literal_float(value)
|
|
1033
|
+
if value.finite?
|
|
1034
|
+
super
|
|
1035
|
+
elsif value.nan?
|
|
1036
|
+
"'NaN'"
|
|
1037
|
+
elsif value.infinite? == 1
|
|
1038
|
+
"'Infinity'"
|
|
1039
|
+
else
|
|
1040
|
+
"'-Infinity'"
|
|
1041
|
+
end
|
|
1042
|
+
end
|
|
823
1043
|
|
|
824
1044
|
# Assume that SQL standard quoting is on, per Sequel's defaults
|
|
825
|
-
def
|
|
826
|
-
|
|
1045
|
+
def literal_string_append(sql, v)
|
|
1046
|
+
sql << APOS << v.gsub(APOS_RE, DOUBLE_APOS) << APOS
|
|
827
1047
|
end
|
|
828
1048
|
|
|
829
1049
|
# PostgreSQL uses FALSE for false values
|
|
@@ -835,7 +1055,15 @@ module Sequel
|
|
|
835
1055
|
def select_clause_methods
|
|
836
1056
|
server_version >= 80400 ? SELECT_CLAUSE_METHODS_84 : SELECT_CLAUSE_METHODS
|
|
837
1057
|
end
|
|
838
|
-
|
|
1058
|
+
|
|
1059
|
+
# PostgreSQL requires parentheses around compound datasets if they use
|
|
1060
|
+
# CTEs, and using them in other places doesn't hurt.
|
|
1061
|
+
def compound_dataset_sql_append(sql, ds)
|
|
1062
|
+
sql << PAREN_OPEN
|
|
1063
|
+
super
|
|
1064
|
+
sql << PAREN_CLOSE
|
|
1065
|
+
end
|
|
1066
|
+
|
|
839
1067
|
# Support FOR SHARE locking when using the :share lock style.
|
|
840
1068
|
def select_lock_sql(sql)
|
|
841
1069
|
@opts[:lock] == :share ? (sql << FOR_SHARE) : super
|
|
@@ -843,14 +1071,26 @@ module Sequel
|
|
|
843
1071
|
|
|
844
1072
|
# SQL fragment for named window specifications
|
|
845
1073
|
def select_window_sql(sql)
|
|
846
|
-
|
|
1074
|
+
if ws = @opts[:window]
|
|
1075
|
+
sql << WINDOW
|
|
1076
|
+
c = false
|
|
1077
|
+
co = COMMA
|
|
1078
|
+
as = AS
|
|
1079
|
+
ws.map do |name, window|
|
|
1080
|
+
sql << co if c
|
|
1081
|
+
literal_append(sql, name)
|
|
1082
|
+
sql << as
|
|
1083
|
+
literal_append(sql, window)
|
|
1084
|
+
c ||= true
|
|
1085
|
+
end
|
|
1086
|
+
end
|
|
847
1087
|
end
|
|
848
|
-
|
|
1088
|
+
|
|
849
1089
|
# Use WITH RECURSIVE instead of WITH if any of the CTEs is recursive
|
|
850
1090
|
def select_with_sql_base
|
|
851
1091
|
opts[:with].any?{|w| w[:recursive]} ? SQL_WITH_RECURSIVE : super
|
|
852
1092
|
end
|
|
853
|
-
|
|
1093
|
+
|
|
854
1094
|
# The version of the database server
|
|
855
1095
|
def server_version
|
|
856
1096
|
db.server_version(@opts[:server])
|
|
@@ -858,15 +1098,19 @@ module Sequel
|
|
|
858
1098
|
|
|
859
1099
|
# Concatenate the expressions with a space in between
|
|
860
1100
|
def full_text_string_join(cols)
|
|
861
|
-
cols = Array(cols).map{|x| SQL::Function.new(:COALESCE, x,
|
|
862
|
-
cols = cols.zip([
|
|
1101
|
+
cols = Array(cols).map{|x| SQL::Function.new(:COALESCE, x, EMPTY_STRING)}
|
|
1102
|
+
cols = cols.zip([SPACE] * cols.length).flatten
|
|
863
1103
|
cols.pop
|
|
864
|
-
|
|
1104
|
+
SQL::StringExpression.new(:'||', *cols)
|
|
865
1105
|
end
|
|
866
1106
|
|
|
867
1107
|
# PostgreSQL splits the main table from the joined tables
|
|
868
1108
|
def update_clause_methods
|
|
869
|
-
|
|
1109
|
+
if server_version >= 90100
|
|
1110
|
+
UPDATE_CLAUSE_METHODS_91
|
|
1111
|
+
else
|
|
1112
|
+
UPDATE_CLAUSE_METHODS
|
|
1113
|
+
end
|
|
870
1114
|
end
|
|
871
1115
|
|
|
872
1116
|
# Use FROM to specify additional tables in an update query
|
|
@@ -876,7 +1120,8 @@ module Sequel
|
|
|
876
1120
|
|
|
877
1121
|
# Only include the primary table in the main update clause
|
|
878
1122
|
def update_table_sql(sql)
|
|
879
|
-
sql <<
|
|
1123
|
+
sql << SPACE
|
|
1124
|
+
source_list_append(sql, @opts[:from][0..0])
|
|
880
1125
|
end
|
|
881
1126
|
end
|
|
882
1127
|
end
|