sequel 3.21.0 → 3.39.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 +1061 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +103 -63
- data/Rakefile +59 -27
- data/bin/sequel +50 -26
- data/doc/active_record.rdoc +67 -51
- data/doc/advanced_associations.rdoc +189 -75
- data/doc/association_basics.rdoc +327 -79
- data/doc/cheat_sheet.rdoc +21 -21
- data/doc/core_extensions.rdoc +374 -0
- data/doc/dataset_basics.rdoc +10 -10
- data/doc/dataset_filtering.rdoc +54 -42
- data/doc/mass_assignment.rdoc +56 -0
- data/doc/migration.rdoc +89 -557
- data/doc/model_hooks.rdoc +80 -29
- data/doc/object_model.rdoc +547 -0
- data/doc/opening_databases.rdoc +114 -53
- data/doc/prepared_statements.rdoc +41 -15
- data/doc/querying.rdoc +137 -56
- data/doc/reflection.rdoc +36 -10
- 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/release_notes/3.37.0.txt +338 -0
- data/doc/release_notes/3.38.0.txt +234 -0
- data/doc/release_notes/3.39.0.txt +237 -0
- data/doc/schema_modification.rdoc +585 -0
- data/doc/sharding.rdoc +99 -8
- data/doc/sql.rdoc +154 -112
- data/doc/testing.rdoc +169 -0
- data/doc/thread_safety.rdoc +17 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/validations.rdoc +1 -1
- data/doc/virtual_rows.rdoc +78 -43
- 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 +2 -61
- 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 +56 -0
- data/lib/sequel/adapters/jdbc/derby.rb +325 -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 +40 -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 +77 -52
- 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 +279 -97
- data/lib/sequel/adapters/mock.rb +372 -0
- data/lib/sequel/adapters/mysql.rb +125 -222
- data/lib/sequel/adapters/mysql2.rb +65 -56
- data/lib/sequel/adapters/odbc/mssql.rb +17 -9
- 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 +368 -138
- data/lib/sequel/adapters/shared/access.rb +32 -9
- data/lib/sequel/adapters/shared/db2.rb +343 -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 +401 -116
- data/lib/sequel/adapters/shared/mysql.rb +424 -89
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
- data/lib/sequel/adapters/shared/oracle.rb +257 -45
- data/lib/sequel/adapters/shared/postgres.rb +675 -279
- data/lib/sequel/adapters/shared/progress.rb +5 -8
- data/lib/sequel/adapters/shared/sqlite.rb +285 -96
- data/lib/sequel/adapters/sqlite.rb +101 -39
- data/lib/sequel/adapters/swift/mysql.rb +10 -12
- data/lib/sequel/adapters/swift/postgres.rb +13 -79
- data/lib/sequel/adapters/swift/sqlite.rb +9 -6
- data/lib/sequel/adapters/swift.rb +28 -19
- data/lib/sequel/adapters/tinytds.rb +174 -22
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
- data/lib/sequel/adapters/utils/pg_types.rb +81 -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 +33 -2
- data/lib/sequel/connection_pool/single.rb +5 -0
- data/lib/sequel/connection_pool/threaded.rb +43 -14
- data/lib/sequel/connection_pool.rb +10 -2
- data/lib/sequel/core.rb +143 -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 +190 -46
- data/lib/sequel/database/query.rb +272 -110
- data/lib/sequel/database/schema_generator.rb +103 -36
- data/lib/sequel/database/schema_methods.rb +300 -67
- data/lib/sequel/dataset/actions.rb +344 -90
- data/lib/sequel/dataset/features.rb +101 -8
- data/lib/sequel/dataset/graph.rb +42 -15
- data/lib/sequel/dataset/misc.rb +52 -25
- data/lib/sequel/dataset/mutation.rb +12 -7
- data/lib/sequel/dataset/prepared_statements.rb +68 -30
- data/lib/sequel/dataset/query.rb +305 -54
- data/lib/sequel/dataset/sql.rb +720 -292
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/_pretty_table.rb +83 -0
- data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
- data/lib/sequel/extensions/blank.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +72 -0
- data/lib/sequel/extensions/constraint_validations.rb +451 -0
- data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
- data/lib/sequel/extensions/eval_inspect.rb +173 -0
- data/lib/sequel/extensions/inflector.rb +4 -0
- data/lib/sequel/extensions/looser_typecasting.rb +5 -4
- data/lib/sequel/extensions/migration.rb +100 -11
- data/lib/sequel/extensions/named_timezones.rb +9 -0
- data/lib/sequel/extensions/null_dataset.rb +94 -0
- data/lib/sequel/extensions/pagination.rb +4 -0
- data/lib/sequel/extensions/pg_array.rb +537 -0
- data/lib/sequel/extensions/pg_array_ops.rb +263 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
- data/lib/sequel/extensions/pg_hstore.rb +313 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
- data/lib/sequel/extensions/pg_inet.rb +113 -0
- data/lib/sequel/extensions/pg_interval.rb +191 -0
- data/lib/sequel/extensions/pg_json.rb +208 -0
- data/lib/sequel/extensions/pg_range.rb +512 -0
- data/lib/sequel/extensions/pg_range_ops.rb +150 -0
- data/lib/sequel/extensions/pg_row.rb +572 -0
- data/lib/sequel/extensions/pg_row_ops.rb +182 -0
- data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
- data/lib/sequel/extensions/pretty_table.rb +17 -72
- data/lib/sequel/extensions/query.rb +8 -4
- 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 +282 -76
- data/lib/sequel/extensions/select_remove.rb +39 -0
- data/lib/sequel/extensions/server_block.rb +140 -0
- data/lib/sequel/extensions/split_array_nil.rb +65 -0
- data/lib/sequel/extensions/sql_expr.rb +8 -110
- data/lib/sequel/extensions/string_date_time.rb +4 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
- data/lib/sequel/extensions/to_dot.rb +99 -83
- data/lib/sequel/model/associations.rb +1263 -409
- data/lib/sequel/model/base.rb +624 -171
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +24 -2
- 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/constraint_validations.rb +198 -0
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +72 -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 +134 -15
- 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 +13 -2
- data/lib/sequel/plugins/many_through_many.rb +103 -51
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
- data/lib/sequel/plugins/nested_attributes.rb +150 -66
- data/lib/sequel/plugins/optimistic_locking.rb +8 -0
- data/lib/sequel/plugins/pg_row.rb +121 -0
- data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -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 +31 -17
- data/lib/sequel/plugins/schema.rb +8 -3
- 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 +29 -3
- data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/tree.rb +3 -3
- 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/validation_helpers.rb +55 -4
- data/lib/sequel/plugins/xml_serializer.rb +15 -4
- data/lib/sequel/sql.rb +649 -122
- data/lib/sequel/timezones.rb +67 -40
- 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 +194 -66
- data/spec/adapters/mysql_spec.rb +475 -306
- data/spec/adapters/oracle_spec.rb +92 -117
- data/spec/adapters/postgres_spec.rb +1982 -325
- data/spec/adapters/spec_helper.rb +9 -6
- data/spec/adapters/sqlite_spec.rb +203 -75
- data/spec/core/connection_pool_spec.rb +218 -93
- data/spec/core/database_spec.rb +956 -522
- data/spec/core/dataset_spec.rb +1631 -1242
- data/spec/core/expression_filters_spec.rb +690 -334
- data/spec/core/mock_adapter_spec.rb +453 -0
- data/spec/core/object_graph_spec.rb +88 -129
- data/spec/core/schema_generator_spec.rb +4 -4
- data/spec/core/schema_spec.rb +556 -53
- data/spec/core/spec_helper.rb +7 -48
- data/spec/core_extensions_spec.rb +626 -0
- 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/boolean_readers_spec.rb +25 -25
- 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/extensions/constraint_validations_plugin_spec.rb +196 -0
- data/spec/extensions/constraint_validations_spec.rb +316 -0
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +88 -0
- data/spec/extensions/dirty_spec.rb +155 -0
- data/spec/extensions/eager_each_spec.rb +34 -0
- data/spec/extensions/eval_inspect_spec.rb +67 -0
- data/spec/extensions/force_encoding_spec.rb +4 -2
- data/spec/extensions/hook_class_methods_spec.rb +97 -128
- data/spec/extensions/identity_map_spec.rb +142 -24
- 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 -12
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +49 -40
- data/spec/extensions/looser_typecasting_spec.rb +7 -7
- data/spec/extensions/many_through_many_spec.rb +368 -254
- 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 +268 -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 +112 -0
- data/spec/extensions/pg_array_spec.rb +320 -0
- data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
- data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
- data/spec/extensions/pg_hstore_spec.rb +194 -0
- data/spec/extensions/pg_inet_spec.rb +47 -0
- data/spec/extensions/pg_interval_spec.rb +72 -0
- data/spec/extensions/pg_json_spec.rb +99 -0
- data/spec/extensions/pg_range_ops_spec.rb +56 -0
- data/spec/extensions/pg_range_spec.rb +395 -0
- data/spec/extensions/pg_row_ops_spec.rb +58 -0
- data/spec/extensions/pg_row_plugin_spec.rb +49 -0
- data/spec/extensions/pg_row_spec.rb +323 -0
- data/spec/extensions/pg_statement_cache_spec.rb +208 -0
- data/spec/extensions/pg_typecast_on_load_spec.rb +58 -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 +167 -0
- data/spec/extensions/query_spec.rb +9 -3
- 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 +434 -49
- data/spec/extensions/schema_spec.rb +30 -49
- 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 +86 -48
- 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 +34 -67
- data/spec/extensions/split_array_nil_spec.rb +24 -0
- data/spec/extensions/sql_expr_spec.rb +29 -60
- data/spec/extensions/static_cache_spec.rb +145 -0
- data/spec/extensions/subclasses_spec.rb +14 -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 +8 -11
- 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 +58 -67
- data/spec/extensions/validation_helpers_spec.rb +31 -37
- 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 +11 -5
- data/spec/integration/dataset_test.rb +717 -129
- data/spec/integration/eager_loader_test.rb +38 -54
- data/spec/integration/migrator_test.rb +2 -3
- data/spec/integration/model_test.rb +81 -2
- data/spec/integration/plugin_test.rb +604 -147
- data/spec/integration/prepared_statement_test.rb +272 -128
- data/spec/integration/schema_test.rb +367 -57
- data/spec/integration/spec_helper.rb +42 -32
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +183 -8
- data/spec/integration/type_test.rb +31 -8
- data/spec/model/association_reflection_spec.rb +207 -8
- data/spec/model/associations_spec.rb +1206 -693
- data/spec/model/base_spec.rb +334 -100
- data/spec/model/dataset_methods_spec.rb +45 -27
- data/spec/model/eager_loading_spec.rb +828 -692
- data/spec/model/hooks_spec.rb +355 -67
- data/spec/model/model_spec.rb +251 -179
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +506 -232
- data/spec/model/spec_helper.rb +16 -77
- data/spec/model/validations_spec.rb +38 -2
- data/spec/rcov.opts +2 -0
- metadata +209 -80
- data/spec/core/core_sql_spec.rb +0 -451
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
Dataset::NON_SQL_OPTIONS << :disable_insert_returning
|
|
1
|
+
Sequel.require 'adapters/utils/pg_types'
|
|
3
2
|
|
|
3
|
+
module Sequel
|
|
4
4
|
# Top level module for holding all PostgreSQL-related modules and classes
|
|
5
5
|
# for Sequel. There are a few module level accessors that are added via
|
|
6
6
|
# metaprogramming. These are:
|
|
@@ -33,10 +33,10 @@ module Sequel
|
|
|
33
33
|
# Array of exceptions that need to be converted. JDBC
|
|
34
34
|
# uses NativeExceptions, the native adapter uses PGError.
|
|
35
35
|
CONVERTED_EXCEPTIONS = []
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
@client_min_messages = :warning
|
|
38
38
|
@force_standard_strings = true
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
class << self
|
|
41
41
|
# By default, Sequel sets the minimum level of log messages sent to the client
|
|
42
42
|
# to WARNING, where PostgreSQL uses a default of NOTICE. This is to avoid a lot
|
|
@@ -45,25 +45,70 @@ module Sequel
|
|
|
45
45
|
attr_accessor :client_min_messages
|
|
46
46
|
|
|
47
47
|
# 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
|
-
#
|
|
48
|
+
# '\\' is interpreted as \\ and not \. While PostgreSQL <9.1 defaults
|
|
49
|
+
# to interpreting plain strings, newer versions use standard strings by
|
|
50
|
+
# default. Sequel assumes that SQL standard strings will be used. Setting
|
|
51
|
+
# this to false means Sequel will use the database's default.
|
|
52
52
|
attr_accessor :force_standard_strings
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
55
|
+
class CreateTableGenerator < Sequel::Schema::Generator
|
|
56
|
+
# Add an exclusion constraint when creating the table. elements should be
|
|
57
|
+
# an array of 2 element arrays, with the first element being the column or
|
|
58
|
+
# expression the exclusion constraint is applied to, and the second element
|
|
59
|
+
# being the operator to use for the column/expression to check for exclusion.
|
|
60
|
+
#
|
|
61
|
+
# Example:
|
|
62
|
+
#
|
|
63
|
+
# exclusion_constraint([[:col1, '&&'], [:col2, '=']])
|
|
64
|
+
# # EXCLUDE USING gist (col1 WITH &&, col2 WITH =)
|
|
65
|
+
#
|
|
66
|
+
# Options supported:
|
|
67
|
+
#
|
|
68
|
+
# :name :: Name the constraint with the given name (useful if you may
|
|
69
|
+
# need to drop the constraint later)
|
|
70
|
+
# :using :: Override the index_method for the exclusion constraint (defaults to gist).
|
|
71
|
+
# :where :: Create a partial exclusion constraint, which only affects
|
|
72
|
+
# a subset of table rows, value should be a filter expression.
|
|
73
|
+
def exclude(elements, opts={})
|
|
74
|
+
constraints << {:type => :exclude, :elements => elements}.merge(opts)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
class AlterTableGenerator < Sequel::Schema::AlterTableGenerator
|
|
79
|
+
# Adds an exclusion constraint to an existing table, see
|
|
80
|
+
# CreateTableGenerator#exclude.
|
|
81
|
+
def add_exclusion_constraint(elements, opts={})
|
|
82
|
+
@operations << {:op => :add_constraint, :type => :exclude, :elements => elements}.merge(opts)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Validate the constraint with the given name, which should have
|
|
86
|
+
# been added previously with NOT VALID.
|
|
87
|
+
def validate_constraint(name)
|
|
88
|
+
@operations << {:op => :validate_constraint, :name => name}
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Methods shared by Database instances that connect to PostgreSQL.
|
|
93
|
+
module DatabaseMethods
|
|
94
|
+
EXCLUDE_SCHEMAS = /pg_*|information_schema/i
|
|
95
|
+
PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
|
|
96
|
+
RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session|relation "(.*)" does not exist/.freeze
|
|
97
|
+
SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
|
|
98
|
+
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
|
|
99
|
+
POSTGRES_DEFAULT_RE = /\A(?:B?('.*')::[^']+|\((-?\d+(?:\.\d+)?)\))\z/
|
|
100
|
+
|
|
101
|
+
# SQL fragment for custom sequences (ones not created by serial primary key),
|
|
102
|
+
# Returning the schema and literal form of the sequence name, by parsing
|
|
103
|
+
# the column defaults table.
|
|
104
|
+
SELECT_CUSTOM_SEQUENCE_SQL = (<<-end_sql
|
|
105
|
+
SELECT name.nspname AS "schema",
|
|
106
|
+
CASE
|
|
107
|
+
WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
|
|
108
|
+
substr(split_part(def.adsrc, '''', 2),
|
|
109
|
+
strpos(split_part(def.adsrc, '''', 2), '.')+1)
|
|
110
|
+
ELSE split_part(def.adsrc, '''', 2)
|
|
111
|
+
END AS "sequence"
|
|
67
112
|
FROM pg_class t
|
|
68
113
|
JOIN pg_namespace name ON (t.relnamespace = name.oid)
|
|
69
114
|
JOIN pg_attribute attr ON (t.oid = attrelid)
|
|
@@ -71,26 +116,28 @@ module Sequel
|
|
|
71
116
|
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
|
72
117
|
WHERE cons.contype = 'p'
|
|
73
118
|
AND def.adsrc ~* 'nextval'
|
|
74
|
-
#{"AND name.nspname = '#{schema}'" if schema}
|
|
75
|
-
AND t.relname = '#{table}'
|
|
76
119
|
end_sql
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
120
|
+
).strip.gsub(/\s+/, ' ').freeze
|
|
121
|
+
|
|
122
|
+
# SQL fragment for determining primary key column for the given table. Only
|
|
123
|
+
# returns the first primary key if the table has a composite primary key.
|
|
124
|
+
SELECT_PK_SQL = (<<-end_sql
|
|
125
|
+
SELECT pg_attribute.attname AS pk
|
|
80
126
|
FROM pg_class, pg_attribute, pg_index, pg_namespace
|
|
81
127
|
WHERE pg_class.oid = pg_attribute.attrelid
|
|
82
128
|
AND pg_class.relnamespace = pg_namespace.oid
|
|
83
129
|
AND pg_class.oid = pg_index.indrelid
|
|
84
130
|
AND pg_index.indkey[0] = pg_attribute.attnum
|
|
85
131
|
AND pg_index.indisprimary = 't'
|
|
86
|
-
#{"AND pg_namespace.nspname = '#{schema}'" if schema}
|
|
87
|
-
AND pg_class.relname = '#{table}'
|
|
88
132
|
end_sql
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
133
|
+
).strip.gsub(/\s+/, ' ').freeze
|
|
134
|
+
|
|
135
|
+
# SQL fragment for getting sequence associated with table's
|
|
136
|
+
# primary key, assuming it was a serial primary key column.
|
|
137
|
+
SELECT_SERIAL_SEQUENCE_SQL = (<<-end_sql
|
|
138
|
+
SELECT name.nspname AS "schema", seq.relname AS "sequence"
|
|
92
139
|
FROM pg_class seq, pg_attribute attr, pg_depend dep,
|
|
93
|
-
pg_namespace name, pg_constraint cons
|
|
140
|
+
pg_namespace name, pg_constraint cons, pg_class t
|
|
94
141
|
WHERE seq.oid = dep.objid
|
|
95
142
|
AND seq.relnamespace = name.oid
|
|
96
143
|
AND seq.relkind = 'S'
|
|
@@ -98,69 +145,17 @@ module Sequel
|
|
|
98
145
|
AND attr.attnum = dep.refobjsubid
|
|
99
146
|
AND attr.attrelid = cons.conrelid
|
|
100
147
|
AND attr.attnum = cons.conkey[1]
|
|
148
|
+
AND attr.attrelid = t.oid
|
|
101
149
|
AND cons.contype = 'p'
|
|
102
|
-
#{"AND name.nspname = '#{schema}'" if schema}
|
|
103
|
-
AND seq.relname = '#{table}'
|
|
104
150
|
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
|
|
151
|
+
).strip.gsub(/\s+/, ' ').freeze
|
|
125
152
|
|
|
126
|
-
#
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
|
153
|
+
# The Sequel extensions that require reseting of the conversion procs.
|
|
154
|
+
RESET_PROCS_EXTENSIONS = [:pg_array, :pg_hstore, :pg_inet, :pg_interval, :pg_json, :pg_range].freeze
|
|
155
|
+
|
|
156
|
+
# A hash of conversion procs, keyed by type integer (oid) and
|
|
157
|
+
# having callable values for the conversion proc for that type.
|
|
158
|
+
attr_reader :conversion_procs
|
|
164
159
|
|
|
165
160
|
# Commit an existing prepared transaction with the given transaction
|
|
166
161
|
# identifier string.
|
|
@@ -191,7 +186,7 @@ module Sequel
|
|
|
191
186
|
def create_function(name, definition, opts={})
|
|
192
187
|
self << create_function_sql(name, definition, opts)
|
|
193
188
|
end
|
|
194
|
-
|
|
189
|
+
|
|
195
190
|
# Create the procedural language in the database. Arguments:
|
|
196
191
|
# * name : Name of the procedural language (e.g. plpgsql)
|
|
197
192
|
# * opts : options hash:
|
|
@@ -202,7 +197,13 @@ module Sequel
|
|
|
202
197
|
def create_language(name, opts={})
|
|
203
198
|
self << create_language_sql(name, opts)
|
|
204
199
|
end
|
|
205
|
-
|
|
200
|
+
|
|
201
|
+
# Create a schema in the database. Arguments:
|
|
202
|
+
# * name : Name of the schema (e.g. admin)
|
|
203
|
+
def create_schema(name)
|
|
204
|
+
self << create_schema_sql(name)
|
|
205
|
+
end
|
|
206
|
+
|
|
206
207
|
# Create a trigger in the database. Arguments:
|
|
207
208
|
# * table : the table on which this trigger operates
|
|
208
209
|
# * name : the name of this trigger
|
|
@@ -216,7 +217,7 @@ module Sequel
|
|
|
216
217
|
def create_trigger(table, name, function, opts={})
|
|
217
218
|
self << create_trigger_sql(table, name, function, opts)
|
|
218
219
|
end
|
|
219
|
-
|
|
220
|
+
|
|
220
221
|
# PostgreSQL uses the :postgres database type.
|
|
221
222
|
def database_type
|
|
222
223
|
:postgres
|
|
@@ -231,7 +232,7 @@ module Sequel
|
|
|
231
232
|
def drop_function(name, opts={})
|
|
232
233
|
self << drop_function_sql(name, opts)
|
|
233
234
|
end
|
|
234
|
-
|
|
235
|
+
|
|
235
236
|
# Drops a procedural language from the database. Arguments:
|
|
236
237
|
# * name : name of the procedural language to drop
|
|
237
238
|
# * opts : options hash:
|
|
@@ -240,15 +241,14 @@ module Sequel
|
|
|
240
241
|
def drop_language(name, opts={})
|
|
241
242
|
self << drop_language_sql(name, opts)
|
|
242
243
|
end
|
|
243
|
-
|
|
244
|
-
#
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
super
|
|
244
|
+
|
|
245
|
+
# Drops a schema from the database. Arguments:
|
|
246
|
+
# * name : name of the schema to drop
|
|
247
|
+
# * opts : options hash:
|
|
248
|
+
# * :cascade : Drop all objects in this schema.
|
|
249
|
+
# * :if_exists : Don't raise an error if the schema doesn't exist.
|
|
250
|
+
def drop_schema(name, opts={})
|
|
251
|
+
self << drop_schema_sql(name, opts)
|
|
252
252
|
end
|
|
253
253
|
|
|
254
254
|
# Drops a trigger from the database. Arguments:
|
|
@@ -260,7 +260,76 @@ module Sequel
|
|
|
260
260
|
def drop_trigger(table, name, opts={})
|
|
261
261
|
self << drop_trigger_sql(table, name, opts)
|
|
262
262
|
end
|
|
263
|
-
|
|
263
|
+
|
|
264
|
+
# If any of the extensions that require reseting the conversion procs
|
|
265
|
+
# is loaded, reset them. This is done here so that if you load
|
|
266
|
+
# multiple pg_* extensions in the same call, the conversion procs are
|
|
267
|
+
# only reset once instead of once for every extension.
|
|
268
|
+
def extension(*exts)
|
|
269
|
+
super
|
|
270
|
+
unless (RESET_PROCS_EXTENSIONS & exts).empty?
|
|
271
|
+
reset_conversion_procs
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# Return full foreign key information using the pg system tables, including
|
|
276
|
+
# :name, :on_delete, :on_update, and :deferrable entries in the hashes.
|
|
277
|
+
def foreign_key_list(table, opts={})
|
|
278
|
+
m = output_identifier_meth
|
|
279
|
+
im = input_identifier_meth
|
|
280
|
+
schema, table = schema_and_table(table)
|
|
281
|
+
range = 0...32
|
|
282
|
+
|
|
283
|
+
base_ds = metadata_dataset.
|
|
284
|
+
where(:cl__relkind=>'r', :co__contype=>'f', :cl__relname=>im.call(table)).
|
|
285
|
+
from(:pg_constraint___co).
|
|
286
|
+
join(:pg_class___cl, :oid=>:conrelid)
|
|
287
|
+
|
|
288
|
+
# We split the parsing into two separate queries, which are merged manually later.
|
|
289
|
+
# This is because PostgreSQL stores both the referencing and referenced columns in
|
|
290
|
+
# arrays, and I don't know a simple way to not create a cross product, as PostgreSQL
|
|
291
|
+
# doesn't appear to have a function that takes an array and element and gives you
|
|
292
|
+
# the index of that element in the array.
|
|
293
|
+
|
|
294
|
+
ds = base_ds.
|
|
295
|
+
join(:pg_attribute___att, :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, :co__conkey)).
|
|
296
|
+
order(:co__conname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:co__conkey, [x]), x]}, 32, :att__attnum)).
|
|
297
|
+
select(:co__conname___name, :att__attname___column, :co__confupdtype___on_update, :co__confdeltype___on_delete,
|
|
298
|
+
SQL::BooleanExpression.new(:AND, :co__condeferrable, :co__condeferred).as(:deferrable))
|
|
299
|
+
|
|
300
|
+
ref_ds = base_ds.
|
|
301
|
+
join(:pg_class___cl2, :oid=>:co__confrelid).
|
|
302
|
+
join(:pg_attribute___att2, :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, :co__confkey)).
|
|
303
|
+
order(:co__conname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:co__conkey, [x]), x]}, 32, :att2__attnum)).
|
|
304
|
+
select(:co__conname___name, :cl2__relname___table, :att2__attname___refcolumn)
|
|
305
|
+
|
|
306
|
+
# If a schema is given, we only search in that schema, and the returned :table
|
|
307
|
+
# entry is schema qualified as well.
|
|
308
|
+
if schema
|
|
309
|
+
ds = ds.join(:pg_namespace___nsp, :oid=>:cl__relnamespace).
|
|
310
|
+
where(:nsp__nspname=>im.call(schema))
|
|
311
|
+
ref_ds = ref_ds.join(:pg_namespace___nsp2, :oid=>:cl2__relnamespace).
|
|
312
|
+
select_more(:nsp2__nspname___schema)
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
h = {}
|
|
316
|
+
fklod_map = FOREIGN_KEY_LIST_ON_DELETE_MAP
|
|
317
|
+
ds.each do |row|
|
|
318
|
+
if r = h[row[:name]]
|
|
319
|
+
r[:columns] << m.call(row[:column])
|
|
320
|
+
else
|
|
321
|
+
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]}
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
ref_ds.each do |row|
|
|
325
|
+
r = h[row[:name]]
|
|
326
|
+
r[:table] ||= schema ? SQL::QualifiedIdentifier.new(m.call(row[:schema]), m.call(row[:table])) : m.call(row[:table])
|
|
327
|
+
r[:key] ||= []
|
|
328
|
+
r[:key] << m.call(row[:refcolumn])
|
|
329
|
+
end
|
|
330
|
+
h.values
|
|
331
|
+
end
|
|
332
|
+
|
|
264
333
|
# Use the pg_* system tables to determine indexes on a table
|
|
265
334
|
def indexes(table, opts={})
|
|
266
335
|
m = output_identifier_meth
|
|
@@ -273,14 +342,13 @@ module Sequel
|
|
|
273
342
|
join(:pg_index___ind, :indrelid=>:oid, im.call(table)=>:relname).
|
|
274
343
|
join(:pg_class___indc, :oid=>:indexrelid).
|
|
275
344
|
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]}
|
|
345
|
+
filter(:indc__relkind=>'i', :ind__indisprimary=>false, :indexprs=>nil, :indpred=>nil, :indisvalid=>true).
|
|
346
|
+
order(:indc__relname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:ind__indkey, [x]), x]}, 32, :att__attnum)).
|
|
278
347
|
select(:indc__relname___name, :ind__indisunique___unique, :att__attname___column)
|
|
279
|
-
|
|
348
|
+
|
|
280
349
|
ds.join!(:pg_namespace___nsp, :oid=>:tab__relnamespace, :nspname=>schema.to_s) if schema
|
|
281
|
-
ds.filter!(:indisvalid=>true) if server_version >= 80200
|
|
282
350
|
ds.filter!(:indisready=>true, :indcheckxmin=>false) if server_version >= 80300
|
|
283
|
-
|
|
351
|
+
|
|
284
352
|
indexes = {}
|
|
285
353
|
ds.each do |r|
|
|
286
354
|
i = indexes[m.call(r[:name])] ||= {:columns=>[], :unique=>r[:unique]}
|
|
@@ -289,40 +357,66 @@ module Sequel
|
|
|
289
357
|
indexes
|
|
290
358
|
end
|
|
291
359
|
|
|
292
|
-
# Dataset containing all current database locks
|
|
360
|
+
# Dataset containing all current database locks
|
|
293
361
|
def locks
|
|
294
362
|
dataset.from(:pg_class).join(:pg_locks, :relation=>:relfilenode).select(:pg_class__relname, Sequel::SQL::ColumnAll.new(:pg_locks))
|
|
295
363
|
end
|
|
296
|
-
|
|
364
|
+
|
|
365
|
+
# Notifies the given channel. See the PostgreSQL NOTIFY documentation. Options:
|
|
366
|
+
#
|
|
367
|
+
# :payload :: The payload string to use for the NOTIFY statement. Only supported
|
|
368
|
+
# in PostgreSQL 9.0+.
|
|
369
|
+
# :server :: The server to which to send the NOTIFY statement, if the sharding support
|
|
370
|
+
# is being used.
|
|
371
|
+
def notify(channel, opts={})
|
|
372
|
+
execute_ddl("NOTIFY #{channel}#{", #{literal(opts[:payload].to_s)}" if opts[:payload]}", opts)
|
|
373
|
+
end
|
|
374
|
+
|
|
297
375
|
# Return primary key for the given table.
|
|
298
376
|
def primary_key(table, opts={})
|
|
299
377
|
quoted_table = quote_schema_table(table)
|
|
300
|
-
return @primary_keys[quoted_table] if @primary_keys.
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
378
|
+
Sequel.synchronize{return @primary_keys[quoted_table] if @primary_keys.has_key?(quoted_table)}
|
|
379
|
+
schema, table = schema_and_table(table)
|
|
380
|
+
sql = "#{SELECT_PK_SQL} AND pg_class.relname = #{literal(table)}"
|
|
381
|
+
sql << " AND pg_namespace.nspname = #{literal(schema)}" if schema
|
|
382
|
+
value = fetch(sql).single_value
|
|
383
|
+
Sequel.synchronize{@primary_keys[quoted_table] = value}
|
|
306
384
|
end
|
|
307
|
-
|
|
385
|
+
|
|
308
386
|
# Return the sequence providing the default for the primary key for the given table.
|
|
309
387
|
def primary_key_sequence(table, opts={})
|
|
310
388
|
quoted_table = quote_schema_table(table)
|
|
311
|
-
return @primary_key_sequences[quoted_table] if @primary_key_sequences.
|
|
312
|
-
|
|
313
|
-
|
|
389
|
+
Sequel.synchronize{return @primary_key_sequences[quoted_table] if @primary_key_sequences.has_key?(quoted_table)}
|
|
390
|
+
schema, table = schema_and_table(table)
|
|
391
|
+
table = literal(table)
|
|
392
|
+
sql = "#{SELECT_SERIAL_SEQUENCE_SQL} AND t.relname = #{table}"
|
|
393
|
+
sql << " AND name.nspname = #{literal(schema)}" if schema
|
|
394
|
+
if pks = fetch(sql).single_record
|
|
395
|
+
value = literal(SQL::QualifiedIdentifier.new(pks[:schema], pks[:sequence]))
|
|
396
|
+
Sequel.synchronize{@primary_key_sequences[quoted_table] = value}
|
|
314
397
|
else
|
|
315
|
-
|
|
398
|
+
sql = "#{SELECT_CUSTOM_SEQUENCE_SQL} AND t.relname = #{table}"
|
|
399
|
+
sql << " AND name.nspname = #{literal(schema)}" if schema
|
|
400
|
+
if pks = fetch(sql).single_record
|
|
401
|
+
value = literal(SQL::QualifiedIdentifier.new(pks[:schema], LiteralString.new(pks[:sequence])))
|
|
402
|
+
Sequel.synchronize{@primary_key_sequences[quoted_table] = value}
|
|
403
|
+
end
|
|
316
404
|
end
|
|
317
405
|
end
|
|
318
406
|
|
|
319
|
-
# Reset the
|
|
407
|
+
# Reset the database's conversion procs, requires a server query if there
|
|
408
|
+
# any named types.
|
|
409
|
+
def reset_conversion_procs
|
|
410
|
+
@conversion_procs = get_conversion_procs
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
# Reset the primary key sequence for the given table, basing it on the
|
|
320
414
|
# maximum current value of the table's primary key.
|
|
321
415
|
def reset_primary_key_sequence(table)
|
|
322
|
-
pk = SQL::Identifier.new(primary_key(table))
|
|
323
416
|
return unless seq = primary_key_sequence(table)
|
|
417
|
+
pk = SQL::Identifier.new(primary_key(table))
|
|
324
418
|
db = self
|
|
325
|
-
seq_ds = db.from(seq
|
|
419
|
+
seq_ds = db.from(LiteralString.new(seq))
|
|
326
420
|
get{setval(seq, db[table].select{coalesce(max(pk)+seq_ds.select{:increment_by}, seq_ds.select(:min_value))}, false)}
|
|
327
421
|
end
|
|
328
422
|
|
|
@@ -337,7 +431,7 @@ module Sequel
|
|
|
337
431
|
def serial_primary_key_options
|
|
338
432
|
{:primary_key => true, :serial => true, :type=>Integer}
|
|
339
433
|
end
|
|
340
|
-
|
|
434
|
+
|
|
341
435
|
# The version of the PostgreSQL server, used for determining capability.
|
|
342
436
|
def server_version(server=nil)
|
|
343
437
|
return @server_version if @server_version
|
|
@@ -345,12 +439,26 @@ module Sequel
|
|
|
345
439
|
(conn.server_version rescue nil) if conn.respond_to?(:server_version)
|
|
346
440
|
end
|
|
347
441
|
unless @server_version
|
|
348
|
-
m = /PostgreSQL (\d+)\.(\d+)(?:(?:rc\d+)|\.(\d+))?/.match(fetch('SELECT version()').single_value)
|
|
349
|
-
|
|
442
|
+
@server_version = if m = /PostgreSQL (\d+)\.(\d+)(?:(?:rc\d+)|\.(\d+))?/.match(fetch('SELECT version()').single_value)
|
|
443
|
+
(m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
|
|
444
|
+
else
|
|
445
|
+
0
|
|
446
|
+
end
|
|
350
447
|
end
|
|
448
|
+
warn 'Sequel no longer supports PostgreSQL <8.2, some things may not work' if @server_version < 80200
|
|
351
449
|
@server_version
|
|
352
450
|
end
|
|
353
|
-
|
|
451
|
+
|
|
452
|
+
# PostgreSQL supports CREATE TABLE IF NOT EXISTS on 9.1+
|
|
453
|
+
def supports_create_table_if_not_exists?
|
|
454
|
+
server_version >= 90100
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
# PostgreSQL supports DROP TABLE IF EXISTS
|
|
458
|
+
def supports_drop_table_if_exists?
|
|
459
|
+
true
|
|
460
|
+
end
|
|
461
|
+
|
|
354
462
|
# PostgreSQL supports prepared transactions (two-phase commit) if
|
|
355
463
|
# max_prepared_transactions is greater than 0.
|
|
356
464
|
def supports_prepared_transactions?
|
|
@@ -368,39 +476,128 @@ module Sequel
|
|
|
368
476
|
true
|
|
369
477
|
end
|
|
370
478
|
|
|
371
|
-
#
|
|
479
|
+
# PostgreSQL supports transaction DDL statements.
|
|
480
|
+
def supports_transactional_ddl?
|
|
481
|
+
true
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
# Array of symbols specifying table names in the current database.
|
|
485
|
+
# The dataset used is yielded to the block if one is provided,
|
|
486
|
+
# otherwise, an array of symbols of table names is returned.
|
|
372
487
|
#
|
|
373
488
|
# Options:
|
|
374
489
|
# * :schema - The schema to search (default_schema by default)
|
|
375
490
|
# * :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?}
|
|
491
|
+
def tables(opts={}, &block)
|
|
492
|
+
pg_class_relname('r', opts, &block)
|
|
381
493
|
end
|
|
382
|
-
|
|
383
|
-
#
|
|
384
|
-
#
|
|
385
|
-
|
|
494
|
+
|
|
495
|
+
# Check whether the given type name string/symbol (e.g. :hstore) is supported by
|
|
496
|
+
# the database.
|
|
497
|
+
def type_supported?(type)
|
|
498
|
+
@supported_types ||= {}
|
|
499
|
+
@supported_types.fetch(type){@supported_types[type] = (from(:pg_type).filter(:typtype=>'b', :typname=>type.to_s).count > 0)}
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
# Array of symbols specifying view names in the current database.
|
|
386
503
|
#
|
|
387
504
|
# Options:
|
|
388
505
|
# * :schema - The schema to search (default_schema by default)
|
|
389
506
|
# * :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])}
|
|
507
|
+
def views(opts={})
|
|
508
|
+
pg_class_relname('v', opts)
|
|
395
509
|
end
|
|
396
510
|
|
|
397
511
|
private
|
|
398
512
|
|
|
513
|
+
# Use a PostgreSQL-specific alter table generator
|
|
514
|
+
def alter_table_generator_class
|
|
515
|
+
Postgres::AlterTableGenerator
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
# Handle :using option for set_column_type op, and the :validate_constraint op.
|
|
519
|
+
def alter_table_op_sql(table, op)
|
|
520
|
+
case op[:op]
|
|
521
|
+
when :set_column_type
|
|
522
|
+
s = super
|
|
523
|
+
if using = op[:using]
|
|
524
|
+
using = Sequel::LiteralString.new(using) if using.is_a?(String)
|
|
525
|
+
s << ' USING '
|
|
526
|
+
s << literal(using)
|
|
527
|
+
end
|
|
528
|
+
s
|
|
529
|
+
when :validate_constraint
|
|
530
|
+
"VALIDATE CONSTRAINT #{quote_identifier(op[:name])}"
|
|
531
|
+
else
|
|
532
|
+
super
|
|
533
|
+
end
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
# If the :synchronous option is given and non-nil, set synchronous_commit
|
|
537
|
+
# appropriately. Valid values for the :synchronous option are true,
|
|
538
|
+
# :on, false, :off, :local, and :remote_write.
|
|
539
|
+
def begin_new_transaction(conn, opts)
|
|
540
|
+
super
|
|
541
|
+
if opts.has_key?(:synchronous)
|
|
542
|
+
case sync = opts[:synchronous]
|
|
543
|
+
when true
|
|
544
|
+
sync = :on
|
|
545
|
+
when false
|
|
546
|
+
sync = :off
|
|
547
|
+
when nil
|
|
548
|
+
return
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
log_connection_execute(conn, "SET LOCAL synchronous_commit = #{sync}")
|
|
552
|
+
end
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
# Handle PostgreSQL specific default format.
|
|
556
|
+
def column_schema_normalize_default(default, type)
|
|
557
|
+
if m = POSTGRES_DEFAULT_RE.match(default)
|
|
558
|
+
default = m[1] || m[2]
|
|
559
|
+
end
|
|
560
|
+
super(default, type)
|
|
561
|
+
end
|
|
562
|
+
|
|
399
563
|
# If the :prepare option is given and we aren't in a savepoint,
|
|
400
564
|
# prepare the transaction for a two-phase commit.
|
|
401
565
|
def commit_transaction(conn, opts={})
|
|
402
|
-
if opts[:prepare] &&
|
|
403
|
-
log_connection_execute(conn, "PREPARE TRANSACTION #{literal(
|
|
566
|
+
if (s = opts[:prepare]) && _trans(conn)[:savepoint_level] <= 1
|
|
567
|
+
log_connection_execute(conn, "PREPARE TRANSACTION #{literal(s)}")
|
|
568
|
+
else
|
|
569
|
+
super
|
|
570
|
+
end
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
# PostgreSQL can't combine rename_column operations, and it can combine
|
|
574
|
+
# the custom validate_constraint operation.
|
|
575
|
+
def combinable_alter_table_op?(op)
|
|
576
|
+
(super || op[:op] == :validate_constraint) && op[:op] != :rename_column
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
# The SQL queries to execute when starting a new connection.
|
|
580
|
+
def connection_configuration_sqls
|
|
581
|
+
sqls = []
|
|
582
|
+
sqls << "SET standard_conforming_strings = ON" if Postgres.force_standard_strings
|
|
583
|
+
if cmm = Postgres.client_min_messages
|
|
584
|
+
sqls << "SET client_min_messages = '#{cmm.to_s.upcase}'"
|
|
585
|
+
end
|
|
586
|
+
sqls
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
# Handle exclusion constraints.
|
|
590
|
+
def constraint_definition_sql(constraint)
|
|
591
|
+
case constraint[:type]
|
|
592
|
+
when :exclude
|
|
593
|
+
elements = constraint[:elements].map{|c, op| "#{literal(c)} WITH #{op}"}.join(', ')
|
|
594
|
+
"#{"CONSTRAINT #{quote_identifier(constraint[:name])} " if constraint[:name]}EXCLUDE USING #{constraint[:using]||'gist'} (#{elements})#{" WHERE #{filter_expr(constraint[:where])}" if constraint[:where]}"
|
|
595
|
+
when :foreign_key
|
|
596
|
+
sql = super
|
|
597
|
+
if constraint[:not_valid]
|
|
598
|
+
sql << " NOT VALID"
|
|
599
|
+
end
|
|
600
|
+
sql
|
|
404
601
|
else
|
|
405
602
|
super
|
|
406
603
|
end
|
|
@@ -426,39 +623,54 @@ module Sequel
|
|
|
426
623
|
AS #{literal(definition.to_s)}#{", #{literal(opts[:link_symbol].to_s)}" if opts[:link_symbol]}
|
|
427
624
|
END
|
|
428
625
|
end
|
|
429
|
-
|
|
626
|
+
|
|
430
627
|
# SQL for creating a procedural language.
|
|
431
628
|
def create_language_sql(name, opts={})
|
|
432
629
|
"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
630
|
end
|
|
434
|
-
|
|
435
|
-
# SQL for creating a
|
|
631
|
+
|
|
632
|
+
# SQL for creating a schema.
|
|
633
|
+
def create_schema_sql(name)
|
|
634
|
+
"CREATE SCHEMA #{quote_identifier(name)}"
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
# Use a PostgreSQL-specific create table generator
|
|
638
|
+
def create_table_generator_class
|
|
639
|
+
Postgres::CreateTableGenerator
|
|
640
|
+
end
|
|
641
|
+
|
|
642
|
+
# SQL for creating a database trigger.
|
|
436
643
|
def create_trigger_sql(table, name, function, opts={})
|
|
437
644
|
events = opts[:events] ? Array(opts[:events]) : [:insert, :update, :delete]
|
|
438
645
|
whence = opts[:after] ? 'AFTER' : 'BEFORE'
|
|
439
646
|
"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
647
|
end
|
|
441
|
-
|
|
648
|
+
|
|
442
649
|
# The errors that the main adapters can raise, depends on the adapter being used
|
|
443
650
|
def database_error_classes
|
|
444
651
|
CONVERTED_EXCEPTIONS
|
|
445
652
|
end
|
|
446
|
-
|
|
447
|
-
# SQL for dropping a function from the database.
|
|
653
|
+
|
|
654
|
+
# SQL for dropping a function from the database.
|
|
448
655
|
def drop_function_sql(name, opts={})
|
|
449
656
|
"DROP FUNCTION#{' IF EXISTS' if opts[:if_exists]} #{name}#{sql_function_args(opts[:args])}#{' CASCADE' if opts[:cascade]}"
|
|
450
657
|
end
|
|
451
658
|
|
|
659
|
+
# Support :if_exists, :cascade, and :concurrently options.
|
|
660
|
+
def drop_index_sql(table, op)
|
|
661
|
+
"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]}"
|
|
662
|
+
end
|
|
663
|
+
|
|
452
664
|
# SQL for dropping a procedural language from the database.
|
|
453
665
|
def drop_language_sql(name, opts={})
|
|
454
666
|
"DROP LANGUAGE#{' IF EXISTS' if opts[:if_exists]} #{name}#{' CASCADE' if opts[:cascade]}"
|
|
455
667
|
end
|
|
456
668
|
|
|
457
|
-
#
|
|
458
|
-
def
|
|
459
|
-
"DROP
|
|
669
|
+
# SQL for dropping a schema from the database.
|
|
670
|
+
def drop_schema_sql(name, opts={})
|
|
671
|
+
"DROP SCHEMA#{' IF EXISTS' if opts[:if_exists]} #{quote_identifier(name)}#{' CASCADE' if opts[:cascade]}"
|
|
460
672
|
end
|
|
461
|
-
|
|
673
|
+
|
|
462
674
|
# SQL for dropping a trigger from the database.
|
|
463
675
|
def drop_trigger_sql(table, name, opts={})
|
|
464
676
|
"DROP TRIGGER#{' IF EXISTS' if opts[:if_exists]} #{name} ON #{quote_schema_table(table)}#{' CASCADE' if opts[:cascade]}"
|
|
@@ -473,12 +685,24 @@ module Sequel
|
|
|
473
685
|
ds.exclude(:pg_namespace__nspname=>EXCLUDE_SCHEMAS)
|
|
474
686
|
end
|
|
475
687
|
end
|
|
476
|
-
|
|
688
|
+
|
|
689
|
+
# Return a hash with oid keys and callable values, used for converting types.
|
|
690
|
+
def get_conversion_procs
|
|
691
|
+
procs = PG_TYPES.dup
|
|
692
|
+
procs[1184] = procs[1114] = method(:to_application_timestamp)
|
|
693
|
+
unless (pgnt = PG_NAMED_TYPES).empty?
|
|
694
|
+
from(:pg_type).where(:typtype=>'b', :typname=>pgnt.keys.map{|t| t.to_s}).select_map([:oid, :typname]).each do |oid, name|
|
|
695
|
+
procs[oid.to_i] ||= pgnt[name.untaint.to_sym]
|
|
696
|
+
end
|
|
697
|
+
end
|
|
698
|
+
procs
|
|
699
|
+
end
|
|
700
|
+
|
|
477
701
|
# PostgreSQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
|
|
478
702
|
def identifier_input_method_default
|
|
479
703
|
nil
|
|
480
704
|
end
|
|
481
|
-
|
|
705
|
+
|
|
482
706
|
# PostgreSQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on output.
|
|
483
707
|
def identifier_output_method_default
|
|
484
708
|
nil
|
|
@@ -488,7 +712,7 @@ module Sequel
|
|
|
488
712
|
def index_definition_sql(table_name, index)
|
|
489
713
|
cols = index[:columns]
|
|
490
714
|
index_name = index[:name] || default_index_name(table_name, cols)
|
|
491
|
-
expr = if o = index[:opclass]
|
|
715
|
+
expr = if o = index[:opclass]
|
|
492
716
|
"(#{Array(cols).map{|c| "#{literal(c)} #{o}"}.join(', ')})"
|
|
493
717
|
else
|
|
494
718
|
literal(Array(cols))
|
|
@@ -499,74 +723,79 @@ module Sequel
|
|
|
499
723
|
filter = " WHERE #{filter_expr(filter)}" if filter
|
|
500
724
|
case index_type
|
|
501
725
|
when :full_text
|
|
502
|
-
expr = "(to_tsvector(#{literal(index[:language] || 'simple')}, #{dataset.send(:full_text_string_join, cols)}))"
|
|
726
|
+
expr = "(to_tsvector(#{literal(index[:language] || 'simple')}::regconfig, #{literal(dataset.send(:full_text_string_join, cols))}))"
|
|
503
727
|
index_type = :gin
|
|
504
728
|
when :spatial
|
|
505
729
|
index_type = :gist
|
|
506
730
|
end
|
|
507
|
-
"CREATE #{unique}INDEX #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{filter}"
|
|
731
|
+
"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}"
|
|
508
732
|
end
|
|
509
|
-
|
|
510
|
-
#
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
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
|
|
733
|
+
|
|
734
|
+
# Setup datastructures shared by all postgres adapters.
|
|
735
|
+
def initialize_postgres_adapter
|
|
736
|
+
@primary_keys = {}
|
|
737
|
+
@primary_key_sequences = {}
|
|
738
|
+
reset_conversion_procs
|
|
536
739
|
end
|
|
537
740
|
|
|
538
|
-
#
|
|
539
|
-
def
|
|
540
|
-
|
|
741
|
+
# Backbone of the tables and views support.
|
|
742
|
+
def pg_class_relname(type, opts)
|
|
743
|
+
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)
|
|
744
|
+
ds = filter_schema(ds, opts)
|
|
745
|
+
m = output_identifier_meth
|
|
746
|
+
block_given? ? yield(ds) : ds.map{|r| m.call(r[:relname])}
|
|
541
747
|
end
|
|
542
|
-
|
|
748
|
+
|
|
543
749
|
# Use a dollar sign instead of question mark for the argument
|
|
544
750
|
# placeholder.
|
|
545
751
|
def prepared_arg_placeholder
|
|
546
752
|
PREPARED_ARG_PLACEHOLDER
|
|
547
753
|
end
|
|
548
|
-
|
|
754
|
+
|
|
755
|
+
# Remove the cached entries for primary keys and sequences when a table is
|
|
756
|
+
# changed.
|
|
757
|
+
def remove_cached_schema(table)
|
|
758
|
+
tab = quote_schema_table(table)
|
|
759
|
+
Sequel.synchronize do
|
|
760
|
+
@primary_keys.delete(tab)
|
|
761
|
+
@primary_key_sequences.delete(tab)
|
|
762
|
+
end
|
|
763
|
+
super
|
|
764
|
+
end
|
|
765
|
+
|
|
549
766
|
# SQL DDL statement for renaming a table. PostgreSQL doesn't allow you to change a table's schema in
|
|
550
767
|
# a rename table operation, so speciying a new schema in new_name will not have an effect.
|
|
551
768
|
def rename_table_sql(name, new_name)
|
|
552
769
|
"ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_identifier(schema_and_table(new_name).last)}"
|
|
553
|
-
end
|
|
770
|
+
end
|
|
554
771
|
|
|
555
772
|
# PostgreSQL's autoincrementing primary keys are of type integer or bigint
|
|
556
773
|
# using a nextval function call as a default.
|
|
557
774
|
def schema_autoincrementing_primary_key?(schema)
|
|
558
|
-
super
|
|
775
|
+
super && schema[:default] =~ /\Anextval/io
|
|
776
|
+
end
|
|
777
|
+
|
|
778
|
+
# Recognize PostgreSQL interval type.
|
|
779
|
+
def schema_column_type(db_type)
|
|
780
|
+
case db_type
|
|
781
|
+
when /\Ainterval\z/io
|
|
782
|
+
:interval
|
|
783
|
+
else
|
|
784
|
+
super
|
|
785
|
+
end
|
|
559
786
|
end
|
|
560
787
|
|
|
561
788
|
# The dataset used for parsing table schemas, using the pg_* system catalogs.
|
|
562
789
|
def schema_parse_table(table_name, opts)
|
|
563
|
-
m = output_identifier_meth
|
|
564
|
-
m2 = input_identifier_meth
|
|
790
|
+
m = output_identifier_meth(opts[:dataset])
|
|
791
|
+
m2 = input_identifier_meth(opts[:dataset])
|
|
565
792
|
ds = metadata_dataset.select(:pg_attribute__attname___name,
|
|
793
|
+
SQL::Cast.new(:pg_attribute__atttypid, :integer).as(:oid),
|
|
566
794
|
SQL::Function.new(:format_type, :pg_type__oid, :pg_attribute__atttypmod).as(:db_type),
|
|
567
795
|
SQL::Function.new(:pg_get_expr, :pg_attrdef__adbin, :pg_class__oid).as(:default),
|
|
568
796
|
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)
|
|
797
|
+
SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(:pg_attribute__attnum => SQL::Function.new(:ANY, :pg_index__indkey)), false).as(:primary_key),
|
|
798
|
+
:pg_namespace__nspname).
|
|
570
799
|
from(:pg_class).
|
|
571
800
|
join(:pg_attribute, :attrelid=>:oid).
|
|
572
801
|
join(:pg_type, :oid=>:atttypid).
|
|
@@ -578,18 +807,46 @@ module Sequel
|
|
|
578
807
|
filter(:pg_class__relname=>m2.call(table_name)).
|
|
579
808
|
order(:pg_attribute__attnum)
|
|
580
809
|
ds = filter_schema(ds, opts)
|
|
810
|
+
current_schema = nil
|
|
581
811
|
ds.map do |row|
|
|
812
|
+
sch = row.delete(:nspname)
|
|
813
|
+
if current_schema
|
|
814
|
+
if sch != current_schema
|
|
815
|
+
raise Error, "columns from tables in two separate schema were returned (please specify a schema): #{current_schema.inspect}, #{sch.inspect}"
|
|
816
|
+
end
|
|
817
|
+
else
|
|
818
|
+
current_schema = sch
|
|
819
|
+
end
|
|
582
820
|
row[:default] = nil if blank_object?(row[:default])
|
|
583
821
|
row[:type] = schema_column_type(row[:db_type])
|
|
584
822
|
[m.call(row.delete(:name)), row]
|
|
585
823
|
end
|
|
586
824
|
end
|
|
587
825
|
|
|
826
|
+
# Set the transaction isolation level on the given connection
|
|
827
|
+
def set_transaction_isolation(conn, opts)
|
|
828
|
+
level = opts.fetch(:isolation, transaction_isolation_level)
|
|
829
|
+
read_only = opts[:read_only]
|
|
830
|
+
deferrable = opts[:deferrable]
|
|
831
|
+
if level || !read_only.nil? || !deferrable.nil?
|
|
832
|
+
sql = "SET TRANSACTION"
|
|
833
|
+
sql << " ISOLATION LEVEL #{Sequel::Database::TRANSACTION_ISOLATION_LEVELS[level]}" if level
|
|
834
|
+
sql << " READ #{read_only ? 'ONLY' : 'WRITE'}" unless read_only.nil?
|
|
835
|
+
sql << " #{'NOT ' unless deferrable}DEFERRABLE" unless deferrable.nil?
|
|
836
|
+
log_connection_execute(conn, sql)
|
|
837
|
+
end
|
|
838
|
+
end
|
|
839
|
+
|
|
588
840
|
# Turns an array of argument specifiers into an SQL fragment used for function arguments. See create_function_sql.
|
|
589
841
|
def sql_function_args(args)
|
|
590
842
|
"(#{Array(args).map{|a| Array(a).reverse.join(' ')}.join(', ')})"
|
|
591
843
|
end
|
|
592
|
-
|
|
844
|
+
|
|
845
|
+
# PostgreSQL can combine multiple alter table ops into a single query.
|
|
846
|
+
def supports_combining_alter_table_ops?
|
|
847
|
+
true
|
|
848
|
+
end
|
|
849
|
+
|
|
593
850
|
# Handle bigserial type if :serial option is present
|
|
594
851
|
def type_literal_generic_bignum(column)
|
|
595
852
|
column[:serial] ? :bigserial : super
|
|
@@ -619,7 +876,7 @@ module Sequel
|
|
|
619
876
|
end
|
|
620
877
|
end
|
|
621
878
|
end
|
|
622
|
-
|
|
879
|
+
|
|
623
880
|
# Instance methods for datasets that connect to a PostgreSQL database.
|
|
624
881
|
module DatasetMethods
|
|
625
882
|
ACCESS_SHARE = 'ACCESS SHARE'.freeze
|
|
@@ -627,107 +884,123 @@ module Sequel
|
|
|
627
884
|
BOOL_FALSE = 'false'.freeze
|
|
628
885
|
BOOL_TRUE = 'true'.freeze
|
|
629
886
|
COMMA_SEPARATOR = ', '.freeze
|
|
630
|
-
DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'from using where')
|
|
887
|
+
DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'delete from using where returning')
|
|
888
|
+
DELETE_CLAUSE_METHODS_91 = Dataset.clause_methods(:delete, %w'with delete from using where returning')
|
|
631
889
|
EXCLUSIVE = 'EXCLUSIVE'.freeze
|
|
632
890
|
EXPLAIN = 'EXPLAIN '.freeze
|
|
633
891
|
EXPLAIN_ANALYZE = 'EXPLAIN ANALYZE '.freeze
|
|
634
892
|
FOR_SHARE = ' FOR SHARE'.freeze
|
|
893
|
+
INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'insert into columns values returning')
|
|
894
|
+
INSERT_CLAUSE_METHODS_91 = Dataset.clause_methods(:insert, %w'with insert into columns values returning')
|
|
635
895
|
LOCK = 'LOCK TABLE %s IN %s MODE'.freeze
|
|
636
896
|
NULL = LiteralString.new('NULL').freeze
|
|
637
897
|
PG_TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S".freeze
|
|
638
898
|
QUERY_PLAN = 'QUERY PLAN'.to_sym
|
|
639
899
|
ROW_EXCLUSIVE = 'ROW EXCLUSIVE'.freeze
|
|
640
900
|
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')
|
|
901
|
+
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select distinct columns from join where group having compounds order limit lock')
|
|
902
|
+
SELECT_CLAUSE_METHODS_84 = Dataset.clause_methods(:select, %w'with select distinct columns from join where group having window compounds order limit lock')
|
|
643
903
|
SHARE = 'SHARE'.freeze
|
|
644
904
|
SHARE_ROW_EXCLUSIVE = 'SHARE ROW EXCLUSIVE'.freeze
|
|
645
905
|
SHARE_UPDATE_EXCLUSIVE = 'SHARE UPDATE EXCLUSIVE'.freeze
|
|
646
906
|
SQL_WITH_RECURSIVE = "WITH RECURSIVE ".freeze
|
|
647
|
-
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'table set from where')
|
|
648
|
-
|
|
907
|
+
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'update table set from where returning')
|
|
908
|
+
UPDATE_CLAUSE_METHODS_91 = Dataset.clause_methods(:update, %w'with update table set from where returning')
|
|
909
|
+
SPACE = Dataset::SPACE
|
|
910
|
+
FROM = Dataset::FROM
|
|
911
|
+
APOS = Dataset::APOS
|
|
912
|
+
APOS_RE = Dataset::APOS_RE
|
|
913
|
+
DOUBLE_APOS = Dataset::DOUBLE_APOS
|
|
914
|
+
PAREN_OPEN = Dataset::PAREN_OPEN
|
|
915
|
+
PAREN_CLOSE = Dataset::PAREN_CLOSE
|
|
916
|
+
COMMA = Dataset::COMMA
|
|
917
|
+
AS = Dataset::AS
|
|
918
|
+
XOR_OP = ' # '.freeze
|
|
919
|
+
CRLF = "\r\n".freeze
|
|
920
|
+
BLOB_RE = /[\000-\037\047\134\177-\377]/n.freeze
|
|
921
|
+
WINDOW = " WINDOW ".freeze
|
|
922
|
+
EMPTY_STRING = ''.freeze
|
|
923
|
+
|
|
649
924
|
# Shared methods for prepared statements when used with PostgreSQL databases.
|
|
650
925
|
module PreparedStatementMethods
|
|
651
926
|
# Override insert action to use RETURNING if the server supports it.
|
|
927
|
+
def run
|
|
928
|
+
if @prepared_type == :insert
|
|
929
|
+
fetch_rows(prepared_sql){|r| return r.values.first}
|
|
930
|
+
else
|
|
931
|
+
super
|
|
932
|
+
end
|
|
933
|
+
end
|
|
934
|
+
|
|
652
935
|
def prepared_sql
|
|
653
936
|
return @prepared_sql if @prepared_sql
|
|
937
|
+
@opts[:returning] = insert_pk if @prepared_type == :insert
|
|
654
938
|
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
939
|
@prepared_sql
|
|
660
940
|
end
|
|
661
941
|
end
|
|
662
942
|
|
|
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
|
|
943
|
+
# Return the results of an EXPLAIN ANALYZE query as a string
|
|
674
944
|
def analyze
|
|
675
945
|
explain(:analyze=>true)
|
|
676
946
|
end
|
|
677
|
-
|
|
947
|
+
|
|
678
948
|
# Handle converting the ruby xor operator (^) into the
|
|
679
949
|
# PostgreSQL xor operator (#).
|
|
680
|
-
def
|
|
950
|
+
def complex_expression_sql_append(sql, op, args)
|
|
681
951
|
case op
|
|
682
952
|
when :^
|
|
683
|
-
|
|
953
|
+
j = XOR_OP
|
|
954
|
+
c = false
|
|
955
|
+
args.each do |a|
|
|
956
|
+
sql << j if c
|
|
957
|
+
literal_append(sql, a)
|
|
958
|
+
c ||= true
|
|
959
|
+
end
|
|
684
960
|
else
|
|
685
961
|
super
|
|
686
962
|
end
|
|
687
963
|
end
|
|
688
964
|
|
|
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
965
|
# Return the results of an EXPLAIN query as a string
|
|
695
966
|
def explain(opts={})
|
|
696
|
-
with_sql((opts[:analyze] ? EXPLAIN_ANALYZE : EXPLAIN) + select_sql).map(QUERY_PLAN).join(
|
|
967
|
+
with_sql((opts[:analyze] ? EXPLAIN_ANALYZE : EXPLAIN) + select_sql).map(QUERY_PLAN).join(CRLF)
|
|
697
968
|
end
|
|
698
|
-
|
|
969
|
+
|
|
699
970
|
# Return a cloned dataset which will use FOR SHARE to lock returned rows.
|
|
700
971
|
def for_share
|
|
701
972
|
lock_style(:share)
|
|
702
973
|
end
|
|
703
|
-
|
|
974
|
+
|
|
704
975
|
# PostgreSQL specific full text search syntax, using tsearch2 (included
|
|
705
976
|
# in 8.3 by default, and available for earlier versions as an add-on).
|
|
706
977
|
def full_text_search(cols, terms, opts = {})
|
|
707
978
|
lang = opts[:language] || 'simple'
|
|
708
|
-
|
|
979
|
+
terms = terms.join(' | ') if terms.is_a?(Array)
|
|
980
|
+
filter("to_tsvector(?::regconfig, ?) @@ to_tsquery(?::regconfig, ?)", lang, full_text_string_join(cols), lang, terms)
|
|
709
981
|
end
|
|
710
|
-
|
|
982
|
+
|
|
711
983
|
# Insert given values into the database.
|
|
712
984
|
def insert(*values)
|
|
713
|
-
if @opts[:
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
985
|
+
if @opts[:returning]
|
|
986
|
+
# already know which columns to return, let the standard code
|
|
987
|
+
# handle it
|
|
988
|
+
super
|
|
989
|
+
elsif @opts[:sql]
|
|
990
|
+
# raw SQL used, so don't know which table is being inserted
|
|
991
|
+
# into, and therefore can't determine primary key. Run the
|
|
992
|
+
# insert statement and return nil.
|
|
993
|
+
super
|
|
994
|
+
nil
|
|
717
995
|
else
|
|
718
|
-
|
|
996
|
+
# Force the use of RETURNING with the primary key value.
|
|
997
|
+
returning(insert_pk).insert(*values){|r| return r.values.first}
|
|
719
998
|
end
|
|
720
999
|
end
|
|
721
1000
|
|
|
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
1001
|
# Insert a record returning the record inserted
|
|
728
1002
|
def insert_select(*values)
|
|
729
|
-
return
|
|
730
|
-
naked.clone(default_server_opts(:sql=>insert_returning_sql(nil, *values))).single_record
|
|
1003
|
+
returning.insert(*values){|r| return r}
|
|
731
1004
|
end
|
|
732
1005
|
|
|
733
1006
|
# Locks all tables in the dataset's FROM clause (but not in JOINs) with
|
|
@@ -743,50 +1016,124 @@ module Sequel
|
|
|
743
1016
|
end
|
|
744
1017
|
nil
|
|
745
1018
|
end
|
|
746
|
-
|
|
747
|
-
#
|
|
1019
|
+
|
|
1020
|
+
# PostgreSQL allows inserting multiple rows at once.
|
|
748
1021
|
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)))]
|
|
1022
|
+
sql = LiteralString.new('VALUES ')
|
|
1023
|
+
expression_list_append(sql, values.map{|r| Array(r)})
|
|
1024
|
+
[insert_sql(columns, sql)]
|
|
753
1025
|
end
|
|
754
|
-
|
|
1026
|
+
|
|
1027
|
+
# PostgreSQL supports using the WITH clause in subqueries if it
|
|
1028
|
+
# supports using WITH at all (i.e. on PostgreSQL 8.4+).
|
|
1029
|
+
def supports_cte_in_subqueries?
|
|
1030
|
+
supports_cte?
|
|
1031
|
+
end
|
|
1032
|
+
|
|
755
1033
|
# DISTINCT ON is a PostgreSQL extension
|
|
756
1034
|
def supports_distinct_on?
|
|
757
1035
|
true
|
|
758
1036
|
end
|
|
759
|
-
|
|
1037
|
+
|
|
760
1038
|
# PostgreSQL supports modifying joined datasets
|
|
761
1039
|
def supports_modifying_joins?
|
|
762
1040
|
true
|
|
763
1041
|
end
|
|
764
1042
|
|
|
1043
|
+
# Returning is always supported.
|
|
1044
|
+
def supports_returning?(type)
|
|
1045
|
+
true
|
|
1046
|
+
end
|
|
1047
|
+
|
|
1048
|
+
# PostgreSQL supports pattern matching via regular expressions
|
|
1049
|
+
def supports_regexp?
|
|
1050
|
+
true
|
|
1051
|
+
end
|
|
1052
|
+
|
|
765
1053
|
# PostgreSQL supports timezones in literal timestamps
|
|
766
1054
|
def supports_timestamp_timezones?
|
|
767
1055
|
true
|
|
768
1056
|
end
|
|
769
|
-
|
|
1057
|
+
|
|
770
1058
|
# PostgreSQL 8.4+ supports window functions
|
|
771
1059
|
def supports_window_functions?
|
|
772
1060
|
server_version >= 80400
|
|
773
1061
|
end
|
|
774
1062
|
|
|
1063
|
+
# Truncates the dataset. Returns nil.
|
|
1064
|
+
#
|
|
1065
|
+
# Options:
|
|
1066
|
+
# :cascade :: whether to use the CASCADE option, useful when truncating
|
|
1067
|
+
# tables with Foreign Keys.
|
|
1068
|
+
# :only :: truncate using ONLY, so child tables are unaffected
|
|
1069
|
+
# :restart :: use RESTART IDENTITY to restart any related sequences
|
|
1070
|
+
#
|
|
1071
|
+
# :only and :restart only work correctly on PostgreSQL 8.4+.
|
|
1072
|
+
#
|
|
1073
|
+
# Usage:
|
|
1074
|
+
# DB[:table].truncate # TRUNCATE TABLE "table"
|
|
1075
|
+
# # => nil
|
|
1076
|
+
# DB[:table].truncate(:cascade => true, :only=>true, :restart=>true) # TRUNCATE TABLE ONLY "table" RESTART IDENTITY CASCADE
|
|
1077
|
+
# # => nil
|
|
1078
|
+
def truncate(opts = {})
|
|
1079
|
+
if opts.empty?
|
|
1080
|
+
super()
|
|
1081
|
+
else
|
|
1082
|
+
clone(:truncate_opts=>opts).truncate
|
|
1083
|
+
end
|
|
1084
|
+
end
|
|
1085
|
+
|
|
775
1086
|
# Return a clone of the dataset with an addition named window that can be referenced in window functions.
|
|
776
1087
|
def window(name, opts)
|
|
777
1088
|
clone(:window=>(@opts[:window]||[]) + [[name, SQL::Window.new(opts)]])
|
|
778
1089
|
end
|
|
779
|
-
|
|
1090
|
+
|
|
1091
|
+
protected
|
|
1092
|
+
|
|
1093
|
+
# If returned primary keys are requested, use RETURNING unless already set on the
|
|
1094
|
+
# dataset. If RETURNING is already set, use existing returning values. If RETURNING
|
|
1095
|
+
# is only set to return a single columns, return an array of just that column.
|
|
1096
|
+
# Otherwise, return an array of hashes.
|
|
1097
|
+
def _import(columns, values, opts={})
|
|
1098
|
+
if @opts[:returning]
|
|
1099
|
+
statements = multi_insert_sql(columns, values)
|
|
1100
|
+
@db.transaction(opts.merge(:server=>@opts[:server])) do
|
|
1101
|
+
statements.map{|st| returning_fetch_rows(st)}
|
|
1102
|
+
end.first.map{|v| v.length == 1 ? v.values.first : v}
|
|
1103
|
+
elsif opts[:return] == :primary_key
|
|
1104
|
+
returning(insert_pk)._import(columns, values, opts)
|
|
1105
|
+
else
|
|
1106
|
+
super
|
|
1107
|
+
end
|
|
1108
|
+
end
|
|
1109
|
+
|
|
780
1110
|
private
|
|
781
|
-
|
|
1111
|
+
|
|
1112
|
+
# Format TRUNCATE statement with PostgreSQL specific options.
|
|
1113
|
+
def _truncate_sql(table)
|
|
1114
|
+
to = @opts[:truncate_opts] || {}
|
|
1115
|
+
"TRUNCATE TABLE#{' ONLY' if to[:only]} #{table}#{' RESTART IDENTITY' if to[:restart]}#{' CASCADE' if to[:cascade]}"
|
|
1116
|
+
end
|
|
1117
|
+
|
|
1118
|
+
# Allow truncation of multiple source tables.
|
|
1119
|
+
def check_truncation_allowed!
|
|
1120
|
+
raise(InvalidOperation, "Grouped datasets cannot be truncated") if opts[:group]
|
|
1121
|
+
raise(InvalidOperation, "Joined datasets cannot be truncated") if opts[:join]
|
|
1122
|
+
end
|
|
1123
|
+
|
|
782
1124
|
# PostgreSQL allows deleting from joined datasets
|
|
783
1125
|
def delete_clause_methods
|
|
784
|
-
|
|
785
|
-
|
|
1126
|
+
if server_version >= 90100
|
|
1127
|
+
DELETE_CLAUSE_METHODS_91
|
|
1128
|
+
else
|
|
1129
|
+
DELETE_CLAUSE_METHODS
|
|
1130
|
+
end
|
|
1131
|
+
end
|
|
786
1132
|
|
|
787
1133
|
# Only include the primary table in the main delete clause
|
|
788
1134
|
def delete_from_sql(sql)
|
|
789
|
-
sql <<
|
|
1135
|
+
sql << FROM
|
|
1136
|
+
source_list_append(sql, @opts[:from][0..0])
|
|
790
1137
|
end
|
|
791
1138
|
|
|
792
1139
|
# Use USING to specify additional tables in a delete query
|
|
@@ -794,36 +1141,60 @@ module Sequel
|
|
|
794
1141
|
join_from_sql(:USING, sql)
|
|
795
1142
|
end
|
|
796
1143
|
|
|
797
|
-
#
|
|
798
|
-
def
|
|
799
|
-
|
|
800
|
-
|
|
1144
|
+
# PostgreSQL allows a RETURNING clause.
|
|
1145
|
+
def insert_clause_methods
|
|
1146
|
+
if server_version >= 90100
|
|
1147
|
+
INSERT_CLAUSE_METHODS_91
|
|
1148
|
+
else
|
|
1149
|
+
INSERT_CLAUSE_METHODS
|
|
1150
|
+
end
|
|
801
1151
|
end
|
|
802
|
-
|
|
1152
|
+
|
|
1153
|
+
# Return the primary key to use for RETURNING in an INSERT statement
|
|
1154
|
+
def insert_pk
|
|
1155
|
+
if (f = opts[:from]) && !f.empty? && (pk = db.primary_key(f.first))
|
|
1156
|
+
Sequel::SQL::Identifier.new(pk)
|
|
1157
|
+
end
|
|
1158
|
+
end
|
|
1159
|
+
|
|
803
1160
|
# For multiple table support, PostgreSQL requires at least
|
|
804
1161
|
# two from tables, with joins allowed.
|
|
805
1162
|
def join_from_sql(type, sql)
|
|
806
1163
|
if(from = @opts[:from][1..-1]).empty?
|
|
807
1164
|
raise(Error, 'Need multiple FROM tables if updating/deleting a dataset with JOINs') if @opts[:join]
|
|
808
1165
|
else
|
|
809
|
-
sql <<
|
|
1166
|
+
sql << SPACE << type.to_s << SPACE
|
|
1167
|
+
source_list_append(sql, from)
|
|
810
1168
|
select_join_sql(sql)
|
|
811
1169
|
end
|
|
812
1170
|
end
|
|
813
1171
|
|
|
814
1172
|
# Use a generic blob quoting method, hopefully overridden in one of the subadapter methods
|
|
815
|
-
def
|
|
816
|
-
|
|
1173
|
+
def literal_blob_append(sql, v)
|
|
1174
|
+
sql << APOS << v.gsub(BLOB_RE){|b| "\\#{("%o" % b[0..1].unpack("C")[0]).rjust(3, '0')}"} << APOS
|
|
817
1175
|
end
|
|
818
1176
|
|
|
819
1177
|
# PostgreSQL uses FALSE for false values
|
|
820
1178
|
def literal_false
|
|
821
1179
|
BOOL_FALSE
|
|
822
1180
|
end
|
|
1181
|
+
|
|
1182
|
+
# PostgreSQL quotes NaN and Infinity.
|
|
1183
|
+
def literal_float(value)
|
|
1184
|
+
if value.finite?
|
|
1185
|
+
super
|
|
1186
|
+
elsif value.nan?
|
|
1187
|
+
"'NaN'"
|
|
1188
|
+
elsif value.infinite? == 1
|
|
1189
|
+
"'Infinity'"
|
|
1190
|
+
else
|
|
1191
|
+
"'-Infinity'"
|
|
1192
|
+
end
|
|
1193
|
+
end
|
|
823
1194
|
|
|
824
1195
|
# Assume that SQL standard quoting is on, per Sequel's defaults
|
|
825
|
-
def
|
|
826
|
-
|
|
1196
|
+
def literal_string_append(sql, v)
|
|
1197
|
+
sql << APOS << v.gsub(APOS_RE, DOUBLE_APOS) << APOS
|
|
827
1198
|
end
|
|
828
1199
|
|
|
829
1200
|
# PostgreSQL uses FALSE for false values
|
|
@@ -835,7 +1206,15 @@ module Sequel
|
|
|
835
1206
|
def select_clause_methods
|
|
836
1207
|
server_version >= 80400 ? SELECT_CLAUSE_METHODS_84 : SELECT_CLAUSE_METHODS
|
|
837
1208
|
end
|
|
838
|
-
|
|
1209
|
+
|
|
1210
|
+
# PostgreSQL requires parentheses around compound datasets if they use
|
|
1211
|
+
# CTEs, and using them in other places doesn't hurt.
|
|
1212
|
+
def compound_dataset_sql_append(sql, ds)
|
|
1213
|
+
sql << PAREN_OPEN
|
|
1214
|
+
super
|
|
1215
|
+
sql << PAREN_CLOSE
|
|
1216
|
+
end
|
|
1217
|
+
|
|
839
1218
|
# Support FOR SHARE locking when using the :share lock style.
|
|
840
1219
|
def select_lock_sql(sql)
|
|
841
1220
|
@opts[:lock] == :share ? (sql << FOR_SHARE) : super
|
|
@@ -843,14 +1222,26 @@ module Sequel
|
|
|
843
1222
|
|
|
844
1223
|
# SQL fragment for named window specifications
|
|
845
1224
|
def select_window_sql(sql)
|
|
846
|
-
|
|
1225
|
+
if ws = @opts[:window]
|
|
1226
|
+
sql << WINDOW
|
|
1227
|
+
c = false
|
|
1228
|
+
co = COMMA
|
|
1229
|
+
as = AS
|
|
1230
|
+
ws.map do |name, window|
|
|
1231
|
+
sql << co if c
|
|
1232
|
+
literal_append(sql, name)
|
|
1233
|
+
sql << as
|
|
1234
|
+
literal_append(sql, window)
|
|
1235
|
+
c ||= true
|
|
1236
|
+
end
|
|
1237
|
+
end
|
|
847
1238
|
end
|
|
848
|
-
|
|
1239
|
+
|
|
849
1240
|
# Use WITH RECURSIVE instead of WITH if any of the CTEs is recursive
|
|
850
1241
|
def select_with_sql_base
|
|
851
1242
|
opts[:with].any?{|w| w[:recursive]} ? SQL_WITH_RECURSIVE : super
|
|
852
1243
|
end
|
|
853
|
-
|
|
1244
|
+
|
|
854
1245
|
# The version of the database server
|
|
855
1246
|
def server_version
|
|
856
1247
|
db.server_version(@opts[:server])
|
|
@@ -858,15 +1249,19 @@ module Sequel
|
|
|
858
1249
|
|
|
859
1250
|
# Concatenate the expressions with a space in between
|
|
860
1251
|
def full_text_string_join(cols)
|
|
861
|
-
cols = Array(cols).map{|x| SQL::Function.new(:COALESCE, x,
|
|
862
|
-
cols = cols.zip([
|
|
1252
|
+
cols = Array(cols).map{|x| SQL::Function.new(:COALESCE, x, EMPTY_STRING)}
|
|
1253
|
+
cols = cols.zip([SPACE] * cols.length).flatten
|
|
863
1254
|
cols.pop
|
|
864
|
-
|
|
1255
|
+
SQL::StringExpression.new(:'||', *cols)
|
|
865
1256
|
end
|
|
866
1257
|
|
|
867
1258
|
# PostgreSQL splits the main table from the joined tables
|
|
868
1259
|
def update_clause_methods
|
|
869
|
-
|
|
1260
|
+
if server_version >= 90100
|
|
1261
|
+
UPDATE_CLAUSE_METHODS_91
|
|
1262
|
+
else
|
|
1263
|
+
UPDATE_CLAUSE_METHODS
|
|
1264
|
+
end
|
|
870
1265
|
end
|
|
871
1266
|
|
|
872
1267
|
# Use FROM to specify additional tables in an update query
|
|
@@ -876,7 +1271,8 @@ module Sequel
|
|
|
876
1271
|
|
|
877
1272
|
# Only include the primary table in the main update clause
|
|
878
1273
|
def update_table_sql(sql)
|
|
879
|
-
sql <<
|
|
1274
|
+
sql << SPACE
|
|
1275
|
+
source_list_append(sql, @opts[:from][0..0])
|
|
880
1276
|
end
|
|
881
1277
|
end
|
|
882
1278
|
end
|