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