activerecord 4.2.11.1 → 5.2.4.5
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +594 -1620
- data/MIT-LICENSE +2 -2
- data/README.rdoc +10 -11
- data/examples/performance.rb +32 -31
- data/examples/simple.rb +5 -4
- data/lib/active_record/aggregations.rb +263 -249
- data/lib/active_record/association_relation.rb +11 -6
- data/lib/active_record/associations/alias_tracker.rb +29 -35
- data/lib/active_record/associations/association.rb +77 -43
- data/lib/active_record/associations/association_scope.rb +106 -133
- data/lib/active_record/associations/belongs_to_association.rb +52 -41
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
- data/lib/active_record/associations/builder/association.rb +29 -38
- data/lib/active_record/associations/builder/belongs_to.rb +77 -30
- data/lib/active_record/associations/builder/collection_association.rb +9 -22
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -35
- data/lib/active_record/associations/builder/has_many.rb +6 -4
- data/lib/active_record/associations/builder/has_one.rb +13 -6
- data/lib/active_record/associations/builder/singular_association.rb +15 -11
- data/lib/active_record/associations/collection_association.rb +139 -280
- data/lib/active_record/associations/collection_proxy.rb +231 -133
- data/lib/active_record/associations/foreign_association.rb +3 -1
- data/lib/active_record/associations/has_many_association.rb +34 -89
- data/lib/active_record/associations/has_many_through_association.rb +49 -76
- data/lib/active_record/associations/has_one_association.rb +38 -24
- data/lib/active_record/associations/has_one_through_association.rb +18 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +40 -87
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
- data/lib/active_record/associations/join_dependency.rb +133 -159
- data/lib/active_record/associations/preloader/association.rb +85 -120
- data/lib/active_record/associations/preloader/through_association.rb +85 -74
- data/lib/active_record/associations/preloader.rb +81 -91
- data/lib/active_record/associations/singular_association.rb +27 -34
- data/lib/active_record/associations/through_association.rb +38 -18
- data/lib/active_record/associations.rb +1732 -1597
- data/lib/active_record/attribute_assignment.rb +58 -182
- data/lib/active_record/attribute_decorators.rb +39 -15
- data/lib/active_record/attribute_methods/before_type_cast.rb +10 -8
- data/lib/active_record/attribute_methods/dirty.rb +94 -135
- data/lib/active_record/attribute_methods/primary_key.rb +86 -71
- data/lib/active_record/attribute_methods/query.rb +4 -2
- data/lib/active_record/attribute_methods/read.rb +45 -63
- data/lib/active_record/attribute_methods/serialization.rb +40 -20
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
- data/lib/active_record/attribute_methods/write.rb +30 -45
- data/lib/active_record/attribute_methods.rb +166 -109
- data/lib/active_record/attributes.rb +201 -82
- data/lib/active_record/autosave_association.rb +94 -36
- data/lib/active_record/base.rb +57 -44
- data/lib/active_record/callbacks.rb +97 -57
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +24 -12
- data/lib/active_record/collection_cache_key.rb +53 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -290
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +237 -90
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +71 -21
- data/lib/active_record/connection_adapters/abstract/quoting.rb +118 -52
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +318 -217
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +570 -228
- data/lib/active_record/connection_adapters/abstract/transaction.rb +138 -70
- data/lib/active_record/connection_adapters/abstract_adapter.rb +325 -202
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +542 -601
- data/lib/active_record/connection_adapters/column.rb +50 -41
- data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +41 -180
- data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +45 -114
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +55 -53
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +107 -47
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -284
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +432 -323
- data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +269 -308
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +40 -27
- data/lib/active_record/core.rb +178 -198
- data/lib/active_record/counter_cache.rb +79 -36
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -105
- data/lib/active_record/enum.rb +135 -88
- data/lib/active_record/errors.rb +179 -52
- data/lib/active_record/explain.rb +23 -11
- data/lib/active_record/explain_registry.rb +4 -2
- data/lib/active_record/explain_subscriber.rb +10 -5
- data/lib/active_record/fixture_set/file.rb +35 -9
- data/lib/active_record/fixtures.rb +188 -132
- data/lib/active_record/gem_version.rb +5 -3
- data/lib/active_record/inheritance.rb +148 -112
- data/lib/active_record/integration.rb +70 -28
- data/lib/active_record/internal_metadata.rb +45 -0
- data/lib/active_record/legacy_yaml_adapter.rb +21 -3
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +88 -96
- data/lib/active_record/locking/pessimistic.rb +15 -3
- data/lib/active_record/log_subscriber.rb +95 -33
- data/lib/active_record/migration/command_recorder.rb +133 -90
- data/lib/active_record/migration/compatibility.rb +217 -0
- data/lib/active_record/migration/join_table.rb +8 -6
- data/lib/active_record/migration.rb +581 -282
- data/lib/active_record/model_schema.rb +290 -111
- data/lib/active_record/nested_attributes.rb +264 -222
- data/lib/active_record/no_touching.rb +7 -1
- data/lib/active_record/null_relation.rb +24 -37
- data/lib/active_record/persistence.rb +347 -119
- data/lib/active_record/query_cache.rb +13 -24
- data/lib/active_record/querying.rb +19 -17
- data/lib/active_record/railtie.rb +94 -32
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +9 -3
- data/lib/active_record/railties/databases.rake +149 -156
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +414 -267
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +204 -55
- data/lib/active_record/relation/calculations.rb +256 -248
- data/lib/active_record/relation/delegation.rb +67 -60
- data/lib/active_record/relation/finder_methods.rb +288 -239
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +86 -86
- data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -24
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/predicate_builder.rb +116 -119
- data/lib/active_record/relation/query_attribute.rb +45 -0
- data/lib/active_record/relation/query_methods.rb +448 -393
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +11 -13
- data/lib/active_record/relation/where_clause.rb +186 -0
- data/lib/active_record/relation/where_clause_factory.rb +34 -0
- data/lib/active_record/relation.rb +287 -340
- data/lib/active_record/result.rb +54 -36
- data/lib/active_record/runtime_registry.rb +6 -4
- data/lib/active_record/sanitization.rb +155 -124
- data/lib/active_record/schema.rb +30 -24
- data/lib/active_record/schema_dumper.rb +91 -87
- data/lib/active_record/schema_migration.rb +19 -16
- data/lib/active_record/scoping/default.rb +102 -85
- data/lib/active_record/scoping/named.rb +81 -32
- data/lib/active_record/scoping.rb +45 -26
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +5 -5
- data/lib/active_record/statement_cache.rb +45 -35
- data/lib/active_record/store.rb +42 -36
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +82 -0
- data/lib/active_record/tasks/database_tasks.rb +134 -96
- data/lib/active_record/tasks/mysql_database_tasks.rb +56 -100
- data/lib/active_record/tasks/postgresql_database_tasks.rb +83 -41
- data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
- data/lib/active_record/timestamp.rb +70 -38
- data/lib/active_record/touch_later.rb +64 -0
- data/lib/active_record/transactions.rb +199 -124
- data/lib/active_record/translation.rb +2 -0
- data/lib/active_record/type/adapter_specific_registry.rb +136 -0
- data/lib/active_record/type/date.rb +4 -45
- data/lib/active_record/type/date_time.rb +4 -49
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +24 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +11 -16
- data/lib/active_record/type/type_map.rb +15 -17
- data/lib/active_record/type/unsigned_integer.rb +9 -7
- data/lib/active_record/type.rb +79 -23
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +13 -4
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +14 -13
- data/lib/active_record/validations/uniqueness.rb +40 -41
- data/lib/active_record/validations.rb +38 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +34 -22
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -35
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -3
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -1
- data/lib/rails/generators/active_record/migration.rb +18 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +72 -49
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute.rb +0 -163
- data/lib/active_record/attribute_set/builder.rb +0 -106
- data/lib/active_record/attribute_set.rb +0 -81
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -31
- data/lib/active_record/type/decimal.rb +0 -64
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -59
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -40
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -110
@@ -1,31 +1,34 @@
|
|
1
|
-
|
2
|
-
require 'active_record/connection_adapters/statement_pool'
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
require
|
7
|
-
require 'active_record/connection_adapters/postgresql/quoting'
|
8
|
-
require 'active_record/connection_adapters/postgresql/referential_integrity'
|
9
|
-
require 'active_record/connection_adapters/postgresql/schema_definitions'
|
10
|
-
require 'active_record/connection_adapters/postgresql/schema_statements'
|
11
|
-
require 'active_record/connection_adapters/postgresql/database_statements'
|
3
|
+
# Make sure we're using pg high enough for type casts and Ruby 2.2+ compatibility
|
4
|
+
gem "pg", ">= 0.18", "< 2.0"
|
5
|
+
require "pg"
|
12
6
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
7
|
+
# Use async_exec instead of exec_params on pg versions before 1.1
|
8
|
+
class ::PG::Connection
|
9
|
+
unless self.public_method_defined?(:async_exec_params)
|
10
|
+
remove_method :exec_params
|
11
|
+
alias exec_params async_exec
|
12
|
+
end
|
13
|
+
end
|
18
14
|
|
19
|
-
require
|
15
|
+
require "active_record/connection_adapters/abstract_adapter"
|
16
|
+
require "active_record/connection_adapters/statement_pool"
|
17
|
+
require "active_record/connection_adapters/postgresql/column"
|
18
|
+
require "active_record/connection_adapters/postgresql/database_statements"
|
19
|
+
require "active_record/connection_adapters/postgresql/explain_pretty_printer"
|
20
|
+
require "active_record/connection_adapters/postgresql/oid"
|
21
|
+
require "active_record/connection_adapters/postgresql/quoting"
|
22
|
+
require "active_record/connection_adapters/postgresql/referential_integrity"
|
23
|
+
require "active_record/connection_adapters/postgresql/schema_creation"
|
24
|
+
require "active_record/connection_adapters/postgresql/schema_definitions"
|
25
|
+
require "active_record/connection_adapters/postgresql/schema_dumper"
|
26
|
+
require "active_record/connection_adapters/postgresql/schema_statements"
|
27
|
+
require "active_record/connection_adapters/postgresql/type_metadata"
|
28
|
+
require "active_record/connection_adapters/postgresql/utils"
|
20
29
|
|
21
30
|
module ActiveRecord
|
22
31
|
module ConnectionHandling # :nodoc:
|
23
|
-
VALID_CONN_PARAMS = [:host, :hostaddr, :port, :dbname, :user, :password, :connect_timeout,
|
24
|
-
:client_encoding, :options, :application_name, :fallback_application_name,
|
25
|
-
:keepalives, :keepalives_idle, :keepalives_interval, :keepalives_count,
|
26
|
-
:tty, :sslmode, :requiressl, :sslcompression, :sslcert, :sslkey,
|
27
|
-
:sslrootcert, :sslcrl, :requirepeer, :krbsrvname, :gsslib, :service]
|
28
|
-
|
29
32
|
# Establishes a connection to the database that's used by all Active Record objects
|
30
33
|
def postgresql_connection(config)
|
31
34
|
conn_params = config.symbolize_keys
|
@@ -36,10 +39,11 @@ module ActiveRecord
|
|
36
39
|
conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
|
37
40
|
conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
|
38
41
|
|
39
|
-
# Forward only valid config params to
|
40
|
-
|
42
|
+
# Forward only valid config params to PG::Connection.connect.
|
43
|
+
valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
|
44
|
+
conn_params.slice!(*valid_conn_param_keys)
|
41
45
|
|
42
|
-
# The postgres drivers don't allow the creation of an unconnected
|
46
|
+
# The postgres drivers don't allow the creation of an unconnected PG::Connection object,
|
43
47
|
# so just pass a nil connection object for the time being.
|
44
48
|
ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, conn_params, config)
|
45
49
|
end
|
@@ -68,20 +72,19 @@ module ActiveRecord
|
|
68
72
|
# defaults to true.
|
69
73
|
#
|
70
74
|
# Any further options are used as connection parameters to libpq. See
|
71
|
-
#
|
75
|
+
# https://www.postgresql.org/docs/current/static/libpq-connect.html for the
|
72
76
|
# list of parameters.
|
73
77
|
#
|
74
78
|
# In addition, default connection parameters of libpq can be set per environment variables.
|
75
|
-
# See
|
79
|
+
# See https://www.postgresql.org/docs/current/static/libpq-envars.html .
|
76
80
|
class PostgreSQLAdapter < AbstractAdapter
|
77
|
-
ADAPTER_NAME =
|
81
|
+
ADAPTER_NAME = "PostgreSQL".freeze
|
78
82
|
|
79
83
|
NATIVE_DATABASE_TYPES = {
|
80
|
-
primary_key: "
|
81
|
-
bigserial: "bigserial",
|
84
|
+
primary_key: "bigserial primary key",
|
82
85
|
string: { name: "character varying" },
|
83
86
|
text: { name: "text" },
|
84
|
-
integer: { name: "integer" },
|
87
|
+
integer: { name: "integer", limit: 4 },
|
85
88
|
float: { name: "float" },
|
86
89
|
decimal: { name: "decimal" },
|
87
90
|
datetime: { name: "timestamp" },
|
@@ -95,7 +98,6 @@ module ActiveRecord
|
|
95
98
|
int8range: { name: "int8range" },
|
96
99
|
binary: { name: "bytea" },
|
97
100
|
boolean: { name: "boolean" },
|
98
|
-
bigint: { name: "bigint" },
|
99
101
|
xml: { name: "xml" },
|
100
102
|
tsvector: { name: "tsvector" },
|
101
103
|
hstore: { name: "hstore" },
|
@@ -108,9 +110,17 @@ module ActiveRecord
|
|
108
110
|
ltree: { name: "ltree" },
|
109
111
|
citext: { name: "citext" },
|
110
112
|
point: { name: "point" },
|
113
|
+
line: { name: "line" },
|
114
|
+
lseg: { name: "lseg" },
|
115
|
+
box: { name: "box" },
|
116
|
+
path: { name: "path" },
|
117
|
+
polygon: { name: "polygon" },
|
118
|
+
circle: { name: "circle" },
|
111
119
|
bit: { name: "bit" },
|
112
120
|
bit_varying: { name: "bit varying" },
|
113
121
|
money: { name: "money" },
|
122
|
+
interval: { name: "interval" },
|
123
|
+
oid: { name: "oid" },
|
114
124
|
}
|
115
125
|
|
116
126
|
OID = PostgreSQL::OID #:nodoc:
|
@@ -119,143 +129,119 @@ module ActiveRecord
|
|
119
129
|
include PostgreSQL::ReferentialIntegrity
|
120
130
|
include PostgreSQL::SchemaStatements
|
121
131
|
include PostgreSQL::DatabaseStatements
|
122
|
-
include Savepoints
|
123
132
|
|
124
|
-
def
|
125
|
-
|
133
|
+
def supports_bulk_alter?
|
134
|
+
true
|
126
135
|
end
|
127
136
|
|
128
|
-
|
129
|
-
|
130
|
-
def prepare_column_options(column, types) # :nodoc:
|
131
|
-
spec = super
|
132
|
-
spec[:array] = 'true' if column.respond_to?(:array) && column.array
|
133
|
-
spec[:default] = "\"#{column.default_function}\"" if column.default_function
|
134
|
-
spec
|
137
|
+
def supports_index_sort_order?
|
138
|
+
true
|
135
139
|
end
|
136
140
|
|
137
|
-
|
138
|
-
|
139
|
-
super + [:array]
|
141
|
+
def supports_partial_index?
|
142
|
+
true
|
140
143
|
end
|
141
144
|
|
142
|
-
|
143
|
-
# caching.
|
144
|
-
def supports_statement_cache?
|
145
|
+
def supports_expression_index?
|
145
146
|
true
|
146
147
|
end
|
147
148
|
|
148
|
-
def
|
149
|
+
def supports_transaction_isolation?
|
149
150
|
true
|
150
151
|
end
|
151
152
|
|
152
|
-
def
|
153
|
+
def supports_foreign_keys?
|
153
154
|
true
|
154
155
|
end
|
155
156
|
|
156
|
-
def
|
157
|
+
def supports_validate_constraints?
|
157
158
|
true
|
158
159
|
end
|
159
160
|
|
160
|
-
def
|
161
|
+
def supports_views?
|
161
162
|
true
|
162
163
|
end
|
163
164
|
|
164
|
-
def
|
165
|
+
def supports_datetime_with_precision?
|
166
|
+
true
|
167
|
+
end
|
168
|
+
|
169
|
+
def supports_json?
|
170
|
+
postgresql_version >= 90200
|
171
|
+
end
|
172
|
+
|
173
|
+
def supports_comments?
|
174
|
+
true
|
175
|
+
end
|
176
|
+
|
177
|
+
def supports_savepoints?
|
165
178
|
true
|
166
179
|
end
|
167
180
|
|
168
181
|
def index_algorithms
|
169
|
-
{ concurrently:
|
182
|
+
{ concurrently: "CONCURRENTLY" }
|
170
183
|
end
|
171
184
|
|
172
|
-
class StatementPool < ConnectionAdapters::StatementPool
|
185
|
+
class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
|
173
186
|
def initialize(connection, max)
|
174
|
-
super
|
187
|
+
super(max)
|
188
|
+
@connection = connection
|
175
189
|
@counter = 0
|
176
|
-
@cache = Hash.new { |h,pid| h[pid] = {} }
|
177
190
|
end
|
178
191
|
|
179
|
-
def each(&block); cache.each(&block); end
|
180
|
-
def key?(key); cache.key?(key); end
|
181
|
-
def [](key); cache[key]; end
|
182
|
-
def length; cache.length; end
|
183
|
-
|
184
192
|
def next_key
|
185
193
|
"a#{@counter + 1}"
|
186
194
|
end
|
187
195
|
|
188
196
|
def []=(sql, key)
|
189
|
-
|
190
|
-
dealloc(cache.shift.last)
|
191
|
-
end
|
192
|
-
@counter += 1
|
193
|
-
cache[sql] = key
|
194
|
-
end
|
195
|
-
|
196
|
-
def clear
|
197
|
-
cache.each_value do |stmt_key|
|
198
|
-
dealloc stmt_key
|
199
|
-
end
|
200
|
-
cache.clear
|
201
|
-
end
|
202
|
-
|
203
|
-
def delete(sql_key)
|
204
|
-
dealloc cache[sql_key]
|
205
|
-
cache.delete sql_key
|
197
|
+
super.tap { @counter += 1 }
|
206
198
|
end
|
207
199
|
|
208
200
|
private
|
209
|
-
|
210
|
-
def cache
|
211
|
-
@cache[Process.pid]
|
212
|
-
end
|
213
|
-
|
214
201
|
def dealloc(key)
|
215
202
|
@connection.query "DEALLOCATE #{key}" if connection_active?
|
203
|
+
rescue PG::Error
|
216
204
|
end
|
217
205
|
|
218
206
|
def connection_active?
|
219
|
-
@connection.status ==
|
220
|
-
rescue
|
207
|
+
@connection.status == PG::CONNECTION_OK
|
208
|
+
rescue PG::Error
|
221
209
|
false
|
222
210
|
end
|
223
211
|
end
|
224
212
|
|
225
213
|
# Initializes and connects a PostgreSQL adapter.
|
226
214
|
def initialize(connection, logger, connection_parameters, config)
|
227
|
-
super(connection, logger)
|
228
|
-
|
229
|
-
@visitor = Arel::Visitors::PostgreSQL.new self
|
230
|
-
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
|
231
|
-
@prepared_statements = true
|
232
|
-
else
|
233
|
-
@prepared_statements = false
|
234
|
-
end
|
215
|
+
super(connection, logger, config)
|
235
216
|
|
236
|
-
@connection_parameters
|
217
|
+
@connection_parameters = connection_parameters
|
237
218
|
|
238
219
|
# @local_tz is initialized as nil to avoid warnings when connect tries to use it
|
239
220
|
@local_tz = nil
|
240
|
-
@
|
221
|
+
@max_identifier_length = nil
|
241
222
|
|
242
223
|
connect
|
224
|
+
add_pg_encoders
|
243
225
|
@statements = StatementPool.new @connection,
|
244
|
-
self.class.type_cast_config_to_integer(config
|
226
|
+
self.class.type_cast_config_to_integer(config[:statement_limit])
|
245
227
|
|
246
|
-
if postgresql_version <
|
247
|
-
raise "Your version of PostgreSQL (#{postgresql_version}) is too old
|
228
|
+
if postgresql_version < 90100
|
229
|
+
raise "Your version of PostgreSQL (#{postgresql_version}) is too old. Active Record supports PostgreSQL >= 9.1."
|
248
230
|
end
|
249
231
|
|
232
|
+
add_pg_decoders
|
233
|
+
|
250
234
|
@type_map = Type::HashLookupTypeMap.new
|
251
|
-
initialize_type_map
|
252
|
-
@local_tz = execute(
|
235
|
+
initialize_type_map
|
236
|
+
@local_tz = execute("SHOW TIME ZONE", "SCHEMA").first["TimeZone"]
|
253
237
|
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
|
254
238
|
end
|
255
239
|
|
256
240
|
# Clears the prepared statements cache.
|
257
241
|
def clear_cache!
|
258
|
-
@
|
242
|
+
@lock.synchronize do
|
243
|
+
@statements.clear
|
244
|
+
end
|
259
245
|
end
|
260
246
|
|
261
247
|
def truncate(table_name, name = nil)
|
@@ -264,55 +250,62 @@ module ActiveRecord
|
|
264
250
|
|
265
251
|
# Is this connection alive and ready for queries?
|
266
252
|
def active?
|
267
|
-
@
|
253
|
+
@lock.synchronize do
|
254
|
+
@connection.query "SELECT 1"
|
255
|
+
end
|
268
256
|
true
|
269
|
-
rescue
|
257
|
+
rescue PG::Error
|
270
258
|
false
|
271
259
|
end
|
272
260
|
|
273
261
|
# Close then reopen the connection.
|
274
262
|
def reconnect!
|
275
|
-
|
276
|
-
|
277
|
-
|
263
|
+
@lock.synchronize do
|
264
|
+
super
|
265
|
+
@connection.reset
|
266
|
+
configure_connection
|
267
|
+
end
|
278
268
|
end
|
279
269
|
|
280
270
|
def reset!
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
@connection.
|
271
|
+
@lock.synchronize do
|
272
|
+
clear_cache!
|
273
|
+
reset_transaction
|
274
|
+
unless @connection.transaction_status == ::PG::PQTRANS_IDLE
|
275
|
+
@connection.query "ROLLBACK"
|
276
|
+
end
|
277
|
+
@connection.query "DISCARD ALL"
|
278
|
+
configure_connection
|
285
279
|
end
|
286
|
-
@connection.query 'DISCARD ALL'
|
287
|
-
configure_connection
|
288
280
|
end
|
289
281
|
|
290
282
|
# Disconnects from the database if already connected. Otherwise, this
|
291
283
|
# method does nothing.
|
292
284
|
def disconnect!
|
293
|
-
|
294
|
-
|
285
|
+
@lock.synchronize do
|
286
|
+
super
|
287
|
+
@connection.close rescue nil
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def discard! # :nodoc:
|
292
|
+
@connection.socket_io.reopen(IO::NULL) rescue nil
|
293
|
+
@connection = nil
|
295
294
|
end
|
296
295
|
|
297
296
|
def native_database_types #:nodoc:
|
298
297
|
NATIVE_DATABASE_TYPES
|
299
298
|
end
|
300
299
|
|
301
|
-
|
302
|
-
|
303
|
-
true
|
300
|
+
def set_standard_conforming_strings
|
301
|
+
execute("SET standard_conforming_strings = on", "SCHEMA")
|
304
302
|
end
|
305
303
|
|
306
|
-
|
307
|
-
def supports_primary_key? #:nodoc:
|
304
|
+
def supports_ddl_transactions?
|
308
305
|
true
|
309
306
|
end
|
310
307
|
|
311
|
-
def
|
312
|
-
execute('SET standard_conforming_strings = on', 'SCHEMA')
|
313
|
-
end
|
314
|
-
|
315
|
-
def supports_ddl_transactions?
|
308
|
+
def supports_advisory_locks?
|
316
309
|
true
|
317
310
|
end
|
318
311
|
|
@@ -320,13 +313,12 @@ module ActiveRecord
|
|
320
313
|
true
|
321
314
|
end
|
322
315
|
|
323
|
-
# Returns true if pg > 9.1
|
324
316
|
def supports_extensions?
|
325
|
-
|
317
|
+
true
|
326
318
|
end
|
327
319
|
|
328
|
-
# Range datatypes weren't introduced until PostgreSQL 9.2
|
329
320
|
def supports_ranges?
|
321
|
+
# Range datatypes weren't introduced until PostgreSQL 9.2
|
330
322
|
postgresql_version >= 90200
|
331
323
|
end
|
332
324
|
|
@@ -334,6 +326,28 @@ module ActiveRecord
|
|
334
326
|
postgresql_version >= 90300
|
335
327
|
end
|
336
328
|
|
329
|
+
def supports_foreign_tables?
|
330
|
+
postgresql_version >= 90300
|
331
|
+
end
|
332
|
+
|
333
|
+
def supports_pgcrypto_uuid?
|
334
|
+
postgresql_version >= 90400
|
335
|
+
end
|
336
|
+
|
337
|
+
def get_advisory_lock(lock_id) # :nodoc:
|
338
|
+
unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
|
339
|
+
raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
|
340
|
+
end
|
341
|
+
query_value("SELECT pg_try_advisory_lock(#{lock_id})")
|
342
|
+
end
|
343
|
+
|
344
|
+
def release_advisory_lock(lock_id) # :nodoc:
|
345
|
+
unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
|
346
|
+
raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
|
347
|
+
end
|
348
|
+
query_value("SELECT pg_advisory_unlock(#{lock_id})")
|
349
|
+
end
|
350
|
+
|
337
351
|
def enable_extension(name)
|
338
352
|
exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
|
339
353
|
reload_type_map
|
@@ -347,49 +361,31 @@ module ActiveRecord
|
|
347
361
|
end
|
348
362
|
|
349
363
|
def extension_enabled?(name)
|
350
|
-
|
351
|
-
|
352
|
-
'SCHEMA'
|
353
|
-
res.cast_values.first
|
354
|
-
end
|
364
|
+
res = exec_query("SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled", "SCHEMA")
|
365
|
+
res.cast_values.first
|
355
366
|
end
|
356
367
|
|
357
368
|
def extensions
|
358
|
-
|
359
|
-
exec_query("SELECT extname from pg_extension", "SCHEMA").cast_values
|
360
|
-
else
|
361
|
-
super
|
362
|
-
end
|
369
|
+
exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
|
363
370
|
end
|
364
371
|
|
365
372
|
# Returns the configured supported identifier length supported by PostgreSQL
|
366
|
-
def
|
367
|
-
@
|
373
|
+
def max_identifier_length
|
374
|
+
@max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
|
368
375
|
end
|
376
|
+
alias table_alias_length max_identifier_length
|
377
|
+
alias index_name_length max_identifier_length
|
369
378
|
|
370
379
|
# Set the authorized user for this session
|
371
380
|
def session_auth=(user)
|
372
381
|
clear_cache!
|
373
|
-
|
382
|
+
execute("SET SESSION AUTHORIZATION #{user}")
|
374
383
|
end
|
375
384
|
|
376
385
|
def use_insert_returning?
|
377
386
|
@use_insert_returning
|
378
387
|
end
|
379
388
|
|
380
|
-
def valid_type?(type)
|
381
|
-
!native_database_types[type].nil?
|
382
|
-
end
|
383
|
-
|
384
|
-
def update_table_definition(table_name, base) #:nodoc:
|
385
|
-
PostgreSQL::Table.new(table_name, base)
|
386
|
-
end
|
387
|
-
|
388
|
-
def lookup_cast_type(sql_type) # :nodoc:
|
389
|
-
oid = execute("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").first['oid'].to_i
|
390
|
-
super(oid)
|
391
|
-
end
|
392
|
-
|
393
389
|
def column_name_for_operation(operation, node) # :nodoc:
|
394
390
|
OPERATION_ALIASES.fetch(operation) { operation.downcase }
|
395
391
|
end
|
@@ -400,94 +396,115 @@ module ActiveRecord
|
|
400
396
|
"average" => "avg",
|
401
397
|
}
|
402
398
|
|
403
|
-
|
399
|
+
# Returns the version of the connected PostgreSQL server.
|
400
|
+
def postgresql_version
|
401
|
+
@connection.server_version
|
402
|
+
end
|
404
403
|
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
end
|
404
|
+
def default_index_type?(index) # :nodoc:
|
405
|
+
index.using == :btree || super
|
406
|
+
end
|
409
407
|
|
410
|
-
|
408
|
+
private
|
409
|
+
# See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
|
410
|
+
VALUE_LIMIT_VIOLATION = "22001"
|
411
|
+
NUMERIC_VALUE_OUT_OF_RANGE = "22003"
|
412
|
+
NOT_NULL_VIOLATION = "23502"
|
411
413
|
FOREIGN_KEY_VIOLATION = "23503"
|
412
414
|
UNIQUE_VIOLATION = "23505"
|
415
|
+
SERIALIZATION_FAILURE = "40001"
|
416
|
+
DEADLOCK_DETECTED = "40P01"
|
417
|
+
LOCK_NOT_AVAILABLE = "55P03"
|
418
|
+
QUERY_CANCELED = "57014"
|
413
419
|
|
414
420
|
def translate_exception(exception, message)
|
415
421
|
return exception unless exception.respond_to?(:result)
|
416
422
|
|
417
|
-
case exception.result.try(:error_field,
|
423
|
+
case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
|
418
424
|
when UNIQUE_VIOLATION
|
419
|
-
RecordNotUnique.new(message
|
425
|
+
RecordNotUnique.new(message)
|
420
426
|
when FOREIGN_KEY_VIOLATION
|
421
|
-
InvalidForeignKey.new(message
|
427
|
+
InvalidForeignKey.new(message)
|
428
|
+
when VALUE_LIMIT_VIOLATION
|
429
|
+
ValueTooLong.new(message)
|
430
|
+
when NUMERIC_VALUE_OUT_OF_RANGE
|
431
|
+
RangeError.new(message)
|
432
|
+
when NOT_NULL_VIOLATION
|
433
|
+
NotNullViolation.new(message)
|
434
|
+
when SERIALIZATION_FAILURE
|
435
|
+
SerializationFailure.new(message)
|
436
|
+
when DEADLOCK_DETECTED
|
437
|
+
Deadlocked.new(message)
|
438
|
+
when LOCK_NOT_AVAILABLE
|
439
|
+
LockWaitTimeout.new(message)
|
440
|
+
when QUERY_CANCELED
|
441
|
+
QueryCanceled.new(message)
|
422
442
|
else
|
423
443
|
super
|
424
444
|
end
|
425
445
|
end
|
426
446
|
|
427
|
-
|
428
|
-
|
429
|
-
def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
|
447
|
+
def get_oid_type(oid, fmod, column_name, sql_type = "".freeze)
|
430
448
|
if !type_map.key?(oid)
|
431
|
-
load_additional_types(
|
449
|
+
load_additional_types([oid])
|
432
450
|
end
|
433
451
|
|
434
452
|
type_map.fetch(oid, fmod, sql_type) {
|
435
453
|
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
|
436
|
-
Type
|
454
|
+
Type.default_value.tap do |cast_type|
|
437
455
|
type_map.register_type(oid, cast_type)
|
438
456
|
end
|
439
457
|
}
|
440
458
|
end
|
441
459
|
|
442
|
-
def initialize_type_map(m
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
m.
|
447
|
-
m.register_type
|
448
|
-
m.alias_type
|
449
|
-
m.register_type
|
450
|
-
register_class_with_limit m,
|
451
|
-
m.alias_type
|
452
|
-
m.alias_type
|
453
|
-
m.alias_type
|
454
|
-
m.register_type
|
455
|
-
register_class_with_limit m,
|
456
|
-
register_class_with_limit m,
|
457
|
-
m.alias_type
|
458
|
-
m.register_type
|
459
|
-
|
460
|
-
|
461
|
-
m.register_type
|
462
|
-
m.register_type
|
463
|
-
m.register_type
|
464
|
-
m.register_type
|
465
|
-
m.register_type
|
466
|
-
m.register_type
|
467
|
-
m.register_type
|
468
|
-
m.register_type
|
469
|
-
m.register_type
|
470
|
-
m.register_type
|
471
|
-
m.register_type
|
472
|
-
m.register_type
|
473
|
-
m.register_type
|
474
|
-
m.register_type
|
475
|
-
|
476
|
-
|
477
|
-
m.
|
478
|
-
m.
|
479
|
-
m.
|
480
|
-
|
481
|
-
m.
|
482
|
-
m.alias_type 'lseg', 'varchar'
|
483
|
-
m.alias_type 'box', 'varchar'
|
484
|
-
|
485
|
-
m.register_type 'timestamp' do |_, _, sql_type|
|
460
|
+
def initialize_type_map(m = type_map)
|
461
|
+
m.register_type "int2", Type::Integer.new(limit: 2)
|
462
|
+
m.register_type "int4", Type::Integer.new(limit: 4)
|
463
|
+
m.register_type "int8", Type::Integer.new(limit: 8)
|
464
|
+
m.register_type "oid", OID::Oid.new
|
465
|
+
m.register_type "float4", Type::Float.new
|
466
|
+
m.alias_type "float8", "float4"
|
467
|
+
m.register_type "text", Type::Text.new
|
468
|
+
register_class_with_limit m, "varchar", Type::String
|
469
|
+
m.alias_type "char", "varchar"
|
470
|
+
m.alias_type "name", "varchar"
|
471
|
+
m.alias_type "bpchar", "varchar"
|
472
|
+
m.register_type "bool", Type::Boolean.new
|
473
|
+
register_class_with_limit m, "bit", OID::Bit
|
474
|
+
register_class_with_limit m, "varbit", OID::BitVarying
|
475
|
+
m.alias_type "timestamptz", "timestamp"
|
476
|
+
m.register_type "date", OID::Date.new
|
477
|
+
|
478
|
+
m.register_type "money", OID::Money.new
|
479
|
+
m.register_type "bytea", OID::Bytea.new
|
480
|
+
m.register_type "point", OID::Point.new
|
481
|
+
m.register_type "hstore", OID::Hstore.new
|
482
|
+
m.register_type "json", Type::Json.new
|
483
|
+
m.register_type "jsonb", OID::Jsonb.new
|
484
|
+
m.register_type "cidr", OID::Cidr.new
|
485
|
+
m.register_type "inet", OID::Inet.new
|
486
|
+
m.register_type "uuid", OID::Uuid.new
|
487
|
+
m.register_type "xml", OID::Xml.new
|
488
|
+
m.register_type "tsvector", OID::SpecializedString.new(:tsvector)
|
489
|
+
m.register_type "macaddr", OID::SpecializedString.new(:macaddr)
|
490
|
+
m.register_type "citext", OID::SpecializedString.new(:citext)
|
491
|
+
m.register_type "ltree", OID::SpecializedString.new(:ltree)
|
492
|
+
m.register_type "line", OID::SpecializedString.new(:line)
|
493
|
+
m.register_type "lseg", OID::SpecializedString.new(:lseg)
|
494
|
+
m.register_type "box", OID::SpecializedString.new(:box)
|
495
|
+
m.register_type "path", OID::SpecializedString.new(:path)
|
496
|
+
m.register_type "polygon", OID::SpecializedString.new(:polygon)
|
497
|
+
m.register_type "circle", OID::SpecializedString.new(:circle)
|
498
|
+
|
499
|
+
m.register_type "interval" do |_, _, sql_type|
|
486
500
|
precision = extract_precision(sql_type)
|
487
|
-
OID::
|
501
|
+
OID::SpecializedString.new(:interval, precision: precision)
|
488
502
|
end
|
489
503
|
|
490
|
-
m
|
504
|
+
register_class_with_precision m, "time", Type::Time
|
505
|
+
register_class_with_precision m, "timestamp", OID::DateTime
|
506
|
+
|
507
|
+
m.register_type "numeric" do |_, fmod, sql_type|
|
491
508
|
precision = extract_precision(sql_type)
|
492
509
|
scale = extract_scale(sql_type)
|
493
510
|
|
@@ -507,51 +524,45 @@ module ActiveRecord
|
|
507
524
|
end
|
508
525
|
end
|
509
526
|
|
510
|
-
load_additional_types
|
511
|
-
end
|
512
|
-
|
513
|
-
def extract_limit(sql_type) # :nodoc:
|
514
|
-
case sql_type
|
515
|
-
when /^bigint/i, /^int8/i
|
516
|
-
8
|
517
|
-
when /^smallint/i
|
518
|
-
2
|
519
|
-
else
|
520
|
-
super
|
521
|
-
end
|
527
|
+
load_additional_types
|
522
528
|
end
|
523
529
|
|
524
530
|
# Extracts the value from a PostgreSQL column default definition.
|
525
|
-
def extract_value_from_default(
|
531
|
+
def extract_value_from_default(default)
|
526
532
|
case default
|
527
533
|
# Quoted types
|
528
|
-
|
529
|
-
|
534
|
+
when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
|
535
|
+
# The default 'now'::date is CURRENT_DATE
|
536
|
+
if $1 == "now".freeze && $2 == "date".freeze
|
537
|
+
nil
|
538
|
+
else
|
539
|
+
$1.gsub("''".freeze, "'".freeze)
|
540
|
+
end
|
530
541
|
# Boolean types
|
531
|
-
|
532
|
-
|
542
|
+
when "true".freeze, "false".freeze
|
543
|
+
default
|
533
544
|
# Numeric types
|
534
|
-
|
535
|
-
|
545
|
+
when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
|
546
|
+
$1
|
536
547
|
# Object identifier types
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
548
|
+
when /\A-?\d+\z/
|
549
|
+
$1
|
550
|
+
else
|
551
|
+
# Anything else is blank, some user type, or some function
|
552
|
+
# and we can't know the value of that, so return nil.
|
553
|
+
nil
|
543
554
|
end
|
544
555
|
end
|
545
556
|
|
546
|
-
def extract_default_function(default_value, default)
|
557
|
+
def extract_default_function(default_value, default)
|
547
558
|
default if has_default_function?(default_value, default)
|
548
559
|
end
|
549
560
|
|
550
|
-
def has_default_function?(default_value, default)
|
551
|
-
!default_value &&
|
561
|
+
def has_default_function?(default_value, default)
|
562
|
+
!default_value && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
|
552
563
|
end
|
553
564
|
|
554
|
-
def load_additional_types(
|
565
|
+
def load_additional_types(oids = nil)
|
555
566
|
initializer = OID::TypeMapInitializer.new(type_map)
|
556
567
|
|
557
568
|
if supports_ranges?
|
@@ -570,57 +581,85 @@ module ActiveRecord
|
|
570
581
|
if oids
|
571
582
|
query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
|
572
583
|
else
|
573
|
-
query += initializer.query_conditions_for_initial_load
|
584
|
+
query += initializer.query_conditions_for_initial_load
|
574
585
|
end
|
575
586
|
|
576
|
-
execute_and_clear(query,
|
587
|
+
execute_and_clear(query, "SCHEMA", []) do |records|
|
577
588
|
initializer.run(records)
|
578
589
|
end
|
579
590
|
end
|
580
591
|
|
581
592
|
FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
|
582
593
|
|
583
|
-
def execute_and_clear(sql, name, binds)
|
584
|
-
|
585
|
-
|
594
|
+
def execute_and_clear(sql, name, binds, prepare: false)
|
595
|
+
if without_prepared_statement?(binds)
|
596
|
+
result = exec_no_cache(sql, name, [])
|
597
|
+
elsif !prepare
|
598
|
+
result = exec_no_cache(sql, name, binds)
|
599
|
+
else
|
600
|
+
result = exec_cache(sql, name, binds)
|
601
|
+
end
|
586
602
|
ret = yield result
|
587
603
|
result.clear
|
588
604
|
ret
|
589
605
|
end
|
590
606
|
|
591
607
|
def exec_no_cache(sql, name, binds)
|
592
|
-
|
608
|
+
type_casted_binds = type_casted_binds(binds)
|
609
|
+
log(sql, name, binds, type_casted_binds) do
|
610
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
611
|
+
@connection.exec_params(sql, type_casted_binds)
|
612
|
+
end
|
613
|
+
end
|
593
614
|
end
|
594
615
|
|
595
616
|
def exec_cache(sql, name, binds)
|
596
617
|
stmt_key = prepare_statement(sql)
|
597
|
-
type_casted_binds = binds
|
598
|
-
[col, type_cast(val, col)]
|
599
|
-
}
|
618
|
+
type_casted_binds = type_casted_binds(binds)
|
600
619
|
|
601
|
-
log(sql, name, type_casted_binds, stmt_key) do
|
602
|
-
|
620
|
+
log(sql, name, binds, type_casted_binds, stmt_key) do
|
621
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
622
|
+
@connection.exec_prepared(stmt_key, type_casted_binds)
|
623
|
+
end
|
603
624
|
end
|
604
625
|
rescue ActiveRecord::StatementInvalid => e
|
605
|
-
|
606
|
-
|
607
|
-
#
|
608
|
-
#
|
609
|
-
|
610
|
-
|
611
|
-
begin
|
612
|
-
code = pgerror.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
|
613
|
-
rescue
|
614
|
-
raise e
|
615
|
-
end
|
616
|
-
if FEATURE_NOT_SUPPORTED == code
|
617
|
-
@statements.delete sql_key(sql)
|
618
|
-
retry
|
626
|
+
raise unless is_cached_plan_failure?(e)
|
627
|
+
|
628
|
+
# Nothing we can do if we are in a transaction because all commands
|
629
|
+
# will raise InFailedSQLTransaction
|
630
|
+
if in_transaction?
|
631
|
+
raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
|
619
632
|
else
|
620
|
-
|
633
|
+
@lock.synchronize do
|
634
|
+
# outside of transactions we can simply flush this query and retry
|
635
|
+
@statements.delete sql_key(sql)
|
636
|
+
end
|
637
|
+
retry
|
621
638
|
end
|
622
639
|
end
|
623
640
|
|
641
|
+
# Annoyingly, the code for prepared statements whose return value may
|
642
|
+
# have changed is FEATURE_NOT_SUPPORTED.
|
643
|
+
#
|
644
|
+
# This covers various different error types so we need to do additional
|
645
|
+
# work to classify the exception definitively as a
|
646
|
+
# ActiveRecord::PreparedStatementCacheExpired
|
647
|
+
#
|
648
|
+
# Check here for more details:
|
649
|
+
# https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
|
650
|
+
CACHED_PLAN_HEURISTIC = "cached plan must not change result type".freeze
|
651
|
+
def is_cached_plan_failure?(e)
|
652
|
+
pgerror = e.cause
|
653
|
+
code = pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE)
|
654
|
+
code == FEATURE_NOT_SUPPORTED && pgerror.message.include?(CACHED_PLAN_HEURISTIC)
|
655
|
+
rescue
|
656
|
+
false
|
657
|
+
end
|
658
|
+
|
659
|
+
def in_transaction?
|
660
|
+
open_transactions > 0
|
661
|
+
end
|
662
|
+
|
624
663
|
# Returns the statement identifier for the client side cache
|
625
664
|
# of statements
|
626
665
|
def sql_key(sql)
|
@@ -630,35 +669,31 @@ module ActiveRecord
|
|
630
669
|
# Prepare the statement if it hasn't been prepared, return
|
631
670
|
# the statement key.
|
632
671
|
def prepare_statement(sql)
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
672
|
+
@lock.synchronize do
|
673
|
+
sql_key = sql_key(sql)
|
674
|
+
unless @statements.key? sql_key
|
675
|
+
nextkey = @statements.next_key
|
676
|
+
begin
|
677
|
+
@connection.prepare nextkey, sql
|
678
|
+
rescue => e
|
679
|
+
raise translate_exception_class(e, sql)
|
680
|
+
end
|
681
|
+
# Clear the queue
|
682
|
+
@connection.get_last_result
|
683
|
+
@statements[sql_key] = nextkey
|
640
684
|
end
|
641
|
-
|
642
|
-
@connection.get_last_result
|
643
|
-
@statements[sql_key] = nextkey
|
685
|
+
@statements[sql_key]
|
644
686
|
end
|
645
|
-
@statements[sql_key]
|
646
687
|
end
|
647
688
|
|
648
689
|
# Connects to a PostgreSQL server and sets up the adapter depending on the
|
649
690
|
# connected server's characteristics.
|
650
691
|
def connect
|
651
|
-
@connection =
|
652
|
-
|
653
|
-
# Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
|
654
|
-
# PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
|
655
|
-
# should know about this but can't detect it there, so deal with it here.
|
656
|
-
OID::Money.precision = (postgresql_version >= 80300) ? 19 : 10
|
657
|
-
|
692
|
+
@connection = PG.connect(@connection_parameters)
|
658
693
|
configure_connection
|
659
694
|
rescue ::PG::Error => error
|
660
695
|
if error.message.include?("does not exist")
|
661
|
-
raise ActiveRecord::NoDatabaseError
|
696
|
+
raise ActiveRecord::NoDatabaseError
|
662
697
|
else
|
663
698
|
raise
|
664
699
|
end
|
@@ -670,51 +705,40 @@ module ActiveRecord
|
|
670
705
|
if @config[:encoding]
|
671
706
|
@connection.set_client_encoding(@config[:encoding])
|
672
707
|
end
|
673
|
-
self.client_min_messages = @config[:min_messages] ||
|
708
|
+
self.client_min_messages = @config[:min_messages] || "warning"
|
674
709
|
self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
|
675
710
|
|
676
711
|
# Use standard-conforming strings so we don't have to do the E'...' dance.
|
677
712
|
set_standard_conforming_strings
|
678
713
|
|
714
|
+
variables = @config.fetch(:variables, {}).stringify_keys
|
715
|
+
|
679
716
|
# If using Active Record's time zone support configure the connection to return
|
680
717
|
# TIMESTAMP WITH ZONE types in UTC.
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
718
|
+
unless variables["timezone"]
|
719
|
+
if ActiveRecord::Base.default_timezone == :utc
|
720
|
+
variables["timezone"] = "UTC"
|
721
|
+
elsif @local_tz
|
722
|
+
variables["timezone"] = @local_tz
|
723
|
+
end
|
686
724
|
end
|
687
725
|
|
688
726
|
# SET statements from :variables config hash
|
689
|
-
#
|
690
|
-
variables = @config[:variables] || {}
|
727
|
+
# https://www.postgresql.org/docs/current/static/sql-set.html
|
691
728
|
variables.map do |k, v|
|
692
|
-
if v ==
|
729
|
+
if v == ":default" || v == :default
|
693
730
|
# Sets the value to the global or compile default
|
694
|
-
execute("SET SESSION #{k} TO DEFAULT",
|
731
|
+
execute("SET SESSION #{k} TO DEFAULT", "SCHEMA")
|
695
732
|
elsif !v.nil?
|
696
|
-
execute("SET SESSION #{k} TO #{quote(v)}",
|
733
|
+
execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
|
697
734
|
end
|
698
735
|
end
|
699
736
|
end
|
700
737
|
|
701
|
-
# Returns the current ID of a table's sequence.
|
702
|
-
def last_insert_id(sequence_name) #:nodoc:
|
703
|
-
Integer(last_insert_id_value(sequence_name))
|
704
|
-
end
|
705
|
-
|
706
|
-
def last_insert_id_value(sequence_name)
|
707
|
-
last_insert_id_result(sequence_name).rows.first.first
|
708
|
-
end
|
709
|
-
|
710
|
-
def last_insert_id_result(sequence_name) #:nodoc:
|
711
|
-
exec_query("SELECT currval('#{sequence_name}')", 'SQL')
|
712
|
-
end
|
713
|
-
|
714
738
|
# Returns the list of a table's column names, data types, and default values.
|
715
739
|
#
|
716
740
|
# The underlying query is roughly:
|
717
|
-
# SELECT column.name, column.type, default.value
|
741
|
+
# SELECT column.name, column.type, default.value, column.comment
|
718
742
|
# FROM column LEFT JOIN default
|
719
743
|
# ON column.table_id = default.table_id
|
720
744
|
# AND column.num = default.column_num
|
@@ -729,26 +753,111 @@ module ActiveRecord
|
|
729
753
|
# Query implementation notes:
|
730
754
|
# - format_type includes the column size constraint, e.g. varchar(50)
|
731
755
|
# - ::regclass is a function that gives the id for a table name
|
732
|
-
def column_definitions(table_name)
|
733
|
-
|
756
|
+
def column_definitions(table_name)
|
757
|
+
query(<<-end_sql, "SCHEMA")
|
734
758
|
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
|
735
|
-
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
|
736
|
-
|
737
|
-
|
738
|
-
|
759
|
+
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
|
760
|
+
c.collname, col_description(a.attrelid, a.attnum) AS comment
|
761
|
+
FROM pg_attribute a
|
762
|
+
LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
763
|
+
LEFT JOIN pg_type t ON a.atttypid = t.oid
|
764
|
+
LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation
|
765
|
+
WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
|
739
766
|
AND a.attnum > 0 AND NOT a.attisdropped
|
740
767
|
ORDER BY a.attnum
|
741
768
|
end_sql
|
742
769
|
end
|
743
770
|
|
744
|
-
def extract_table_ref_from_insert_sql(sql)
|
745
|
-
sql[/into\s
|
771
|
+
def extract_table_ref_from_insert_sql(sql)
|
772
|
+
sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
|
746
773
|
$1.strip if $1
|
747
774
|
end
|
748
775
|
|
749
|
-
def
|
750
|
-
PostgreSQL
|
776
|
+
def arel_visitor
|
777
|
+
Arel::Visitors::PostgreSQL.new(self)
|
778
|
+
end
|
779
|
+
|
780
|
+
def can_perform_case_insensitive_comparison_for?(column)
|
781
|
+
@case_insensitive_cache ||= {}
|
782
|
+
@case_insensitive_cache[column.sql_type] ||= begin
|
783
|
+
sql = <<-end_sql
|
784
|
+
SELECT exists(
|
785
|
+
SELECT * FROM pg_proc
|
786
|
+
WHERE proname = 'lower'
|
787
|
+
AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
|
788
|
+
) OR exists(
|
789
|
+
SELECT * FROM pg_proc
|
790
|
+
INNER JOIN pg_cast
|
791
|
+
ON ARRAY[casttarget]::oidvector = proargtypes
|
792
|
+
WHERE proname = 'lower'
|
793
|
+
AND castsource = #{quote column.sql_type}::regtype
|
794
|
+
)
|
795
|
+
end_sql
|
796
|
+
execute_and_clear(sql, "SCHEMA", []) do |result|
|
797
|
+
result.getvalue(0, 0)
|
798
|
+
end
|
799
|
+
end
|
800
|
+
end
|
801
|
+
|
802
|
+
def add_pg_encoders
|
803
|
+
map = PG::TypeMapByClass.new
|
804
|
+
map[Integer] = PG::TextEncoder::Integer.new
|
805
|
+
map[TrueClass] = PG::TextEncoder::Boolean.new
|
806
|
+
map[FalseClass] = PG::TextEncoder::Boolean.new
|
807
|
+
@connection.type_map_for_queries = map
|
751
808
|
end
|
809
|
+
|
810
|
+
def add_pg_decoders
|
811
|
+
coders_by_name = {
|
812
|
+
"int2" => PG::TextDecoder::Integer,
|
813
|
+
"int4" => PG::TextDecoder::Integer,
|
814
|
+
"int8" => PG::TextDecoder::Integer,
|
815
|
+
"oid" => PG::TextDecoder::Integer,
|
816
|
+
"float4" => PG::TextDecoder::Float,
|
817
|
+
"float8" => PG::TextDecoder::Float,
|
818
|
+
"bool" => PG::TextDecoder::Boolean,
|
819
|
+
}
|
820
|
+
known_coder_types = coders_by_name.keys.map { |n| quote(n) }
|
821
|
+
query = <<-SQL % known_coder_types.join(", ")
|
822
|
+
SELECT t.oid, t.typname
|
823
|
+
FROM pg_type as t
|
824
|
+
WHERE t.typname IN (%s)
|
825
|
+
SQL
|
826
|
+
coders = execute_and_clear(query, "SCHEMA", []) do |result|
|
827
|
+
result
|
828
|
+
.map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
|
829
|
+
.compact
|
830
|
+
end
|
831
|
+
|
832
|
+
map = PG::TypeMapByOid.new
|
833
|
+
coders.each { |coder| map.add_coder(coder) }
|
834
|
+
@connection.type_map_for_results = map
|
835
|
+
end
|
836
|
+
|
837
|
+
def construct_coder(row, coder_class)
|
838
|
+
return unless coder_class
|
839
|
+
coder_class.new(oid: row["oid"].to_i, name: row["typname"])
|
840
|
+
end
|
841
|
+
|
842
|
+
ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
|
843
|
+
ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)
|
844
|
+
ActiveRecord::Type.register(:bit, OID::Bit, adapter: :postgresql)
|
845
|
+
ActiveRecord::Type.register(:bit_varying, OID::BitVarying, adapter: :postgresql)
|
846
|
+
ActiveRecord::Type.register(:binary, OID::Bytea, adapter: :postgresql)
|
847
|
+
ActiveRecord::Type.register(:cidr, OID::Cidr, adapter: :postgresql)
|
848
|
+
ActiveRecord::Type.register(:date, OID::Date, adapter: :postgresql)
|
849
|
+
ActiveRecord::Type.register(:datetime, OID::DateTime, adapter: :postgresql)
|
850
|
+
ActiveRecord::Type.register(:decimal, OID::Decimal, adapter: :postgresql)
|
851
|
+
ActiveRecord::Type.register(:enum, OID::Enum, adapter: :postgresql)
|
852
|
+
ActiveRecord::Type.register(:hstore, OID::Hstore, adapter: :postgresql)
|
853
|
+
ActiveRecord::Type.register(:inet, OID::Inet, adapter: :postgresql)
|
854
|
+
ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
|
855
|
+
ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
|
856
|
+
ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)
|
857
|
+
ActiveRecord::Type.register(:legacy_point, OID::LegacyPoint, adapter: :postgresql)
|
858
|
+
ActiveRecord::Type.register(:uuid, OID::Uuid, adapter: :postgresql)
|
859
|
+
ActiveRecord::Type.register(:vector, OID::Vector, adapter: :postgresql)
|
860
|
+
ActiveRecord::Type.register(:xml, OID::Xml, adapter: :postgresql)
|
752
861
|
end
|
753
862
|
end
|
754
863
|
end
|