activerecord 4.2.0 → 5.2.8.1
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 +5 -5
- data/CHANGELOG.md +640 -928
- 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 +264 -247
- data/lib/active_record/association_relation.rb +24 -6
- data/lib/active_record/associations/alias_tracker.rb +29 -35
- data/lib/active_record/associations/association.rb +87 -41
- data/lib/active_record/associations/association_scope.rb +106 -132
- data/lib/active_record/associations/belongs_to_association.rb +55 -36
- 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 +14 -23
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -39
- 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 +145 -266
- data/lib/active_record/associations/collection_proxy.rb +242 -138
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +35 -75
- data/lib/active_record/associations/has_many_through_association.rb +51 -69
- data/lib/active_record/associations/has_one_association.rb +39 -24
- data/lib/active_record/associations/has_one_through_association.rb +18 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +40 -81
- 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 +134 -154
- data/lib/active_record/associations/preloader/association.rb +85 -116
- data/lib/active_record/associations/preloader/through_association.rb +85 -74
- data/lib/active_record/associations/preloader.rb +83 -93
- data/lib/active_record/associations/singular_association.rb +27 -40
- data/lib/active_record/associations/through_association.rb +48 -23
- data/lib/active_record/associations.rb +1732 -1596
- 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 +12 -5
- data/lib/active_record/attribute_methods/dirty.rb +94 -125
- 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 +62 -36
- data/lib/active_record/attribute_methods/write.rb +31 -46
- data/lib/active_record/attribute_methods.rb +170 -117
- data/lib/active_record/attributes.rb +201 -74
- data/lib/active_record/autosave_association.rb +118 -45
- data/lib/active_record/base.rb +60 -48
- 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 +37 -13
- data/lib/active_record/collection_cache_key.rb +53 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -284
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +254 -87
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +72 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -52
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -217
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +617 -212
- data/lib/active_record/connection_adapters/abstract/transaction.rb +139 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +332 -191
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +567 -563
- 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 +42 -195
- data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -115
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -57
- 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 +5 -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 -13
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -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 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +65 -51
- 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 +466 -280
- 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 +439 -330
- 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 -324
- 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 +205 -202
- data/lib/active_record/counter_cache.rb +80 -37
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -105
- data/lib/active_record/enum.rb +136 -90
- data/lib/active_record/errors.rb +180 -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 +11 -6
- data/lib/active_record/fixture_set/file.rb +35 -9
- data/lib/active_record/fixtures.rb +193 -135
- 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 +48 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +92 -98
- 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 +594 -267
- data/lib/active_record/model_schema.rb +292 -111
- data/lib/active_record/nested_attributes.rb +266 -214
- data/lib/active_record/no_touching.rb +8 -2
- data/lib/active_record/null_relation.rb +24 -37
- data/lib/active_record/persistence.rb +350 -119
- data/lib/active_record/query_cache.rb +13 -24
- data/lib/active_record/querying.rb +19 -17
- data/lib/active_record/railtie.rb +117 -35
- 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 +160 -174
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +447 -288
- 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 +259 -244
- data/lib/active_record/relation/delegation.rb +67 -60
- data/lib/active_record/relation/finder_methods.rb +290 -253
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +91 -68
- data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -23
- 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 +118 -92
- data/lib/active_record/relation/query_attribute.rb +45 -0
- data/lib/active_record/relation/query_methods.rb +446 -389
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +18 -16
- 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 -339
- 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 -19
- data/lib/active_record/scoping/default.rb +102 -84
- 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 +136 -95
- data/lib/active_record/tasks/mysql_database_tasks.rb +59 -89
- data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -31
- 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 +208 -123
- 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 -41
- data/lib/active_record/type/date_time.rb +4 -38
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
- 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 +30 -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 +41 -32
- data/lib/active_record/validations.rb +38 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +36 -21
- 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 -6
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -7
- 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.rb +7 -5
- metadata +77 -53
- 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 -149
- data/lib/active_record/attribute_set/builder.rb +0 -86
- data/lib/active_record/attribute_set.rb +0 -77
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- 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 -30
- data/lib/active_record/type/decimal.rb +0 -40
- 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 -55
- 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 -36
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -101
- /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -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
|
@@ -64,24 +68,23 @@ module ActiveRecord
|
|
64
68
|
# <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
|
65
69
|
# * <tt>:variables</tt> - An optional hash of additional parameters that
|
66
70
|
# will be used in <tt>SET SESSION key = val</tt> calls on the connection.
|
67
|
-
# * <tt>:insert_returning</tt> - An optional boolean to control the use
|
71
|
+
# * <tt>:insert_returning</tt> - An optional boolean to control the use of <tt>RETURNING</tt> for <tt>INSERT</tt> statements
|
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)
|
215
|
+
super(connection, logger, config)
|
228
216
|
|
229
|
-
@
|
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
|
235
|
-
|
236
|
-
@connection_parameters, @config = connection_parameters, config
|
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,59 +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
|
-
|
312
|
-
def set_standard_conforming_strings
|
313
|
-
old, self.client_min_messages = client_min_messages, 'panic'
|
314
|
-
execute('SET standard_conforming_strings = on', 'SCHEMA') rescue nil
|
315
|
-
ensure
|
316
|
-
self.client_min_messages = old
|
317
|
-
end
|
318
|
-
|
319
|
-
def supports_ddl_transactions?
|
308
|
+
def supports_advisory_locks?
|
320
309
|
true
|
321
310
|
end
|
322
311
|
|
@@ -324,13 +313,12 @@ module ActiveRecord
|
|
324
313
|
true
|
325
314
|
end
|
326
315
|
|
327
|
-
# Returns true if pg > 9.1
|
328
316
|
def supports_extensions?
|
329
|
-
|
317
|
+
true
|
330
318
|
end
|
331
319
|
|
332
|
-
# Range datatypes weren't introduced until PostgreSQL 9.2
|
333
320
|
def supports_ranges?
|
321
|
+
# Range datatypes weren't introduced until PostgreSQL 9.2
|
334
322
|
postgresql_version >= 90200
|
335
323
|
end
|
336
324
|
|
@@ -338,6 +326,28 @@ module ActiveRecord
|
|
338
326
|
postgresql_version >= 90300
|
339
327
|
end
|
340
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
|
+
|
341
351
|
def enable_extension(name)
|
342
352
|
exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
|
343
353
|
reload_type_map
|
@@ -351,49 +361,31 @@ module ActiveRecord
|
|
351
361
|
end
|
352
362
|
|
353
363
|
def extension_enabled?(name)
|
354
|
-
|
355
|
-
|
356
|
-
'SCHEMA'
|
357
|
-
res.cast_values.first
|
358
|
-
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
|
359
366
|
end
|
360
367
|
|
361
368
|
def extensions
|
362
|
-
|
363
|
-
exec_query("SELECT extname from pg_extension", "SCHEMA").cast_values
|
364
|
-
else
|
365
|
-
super
|
366
|
-
end
|
369
|
+
exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
|
367
370
|
end
|
368
371
|
|
369
372
|
# Returns the configured supported identifier length supported by PostgreSQL
|
370
|
-
def
|
371
|
-
@
|
373
|
+
def max_identifier_length
|
374
|
+
@max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
|
372
375
|
end
|
376
|
+
alias table_alias_length max_identifier_length
|
377
|
+
alias index_name_length max_identifier_length
|
373
378
|
|
374
379
|
# Set the authorized user for this session
|
375
380
|
def session_auth=(user)
|
376
381
|
clear_cache!
|
377
|
-
|
382
|
+
execute("SET SESSION AUTHORIZATION #{user}")
|
378
383
|
end
|
379
384
|
|
380
385
|
def use_insert_returning?
|
381
386
|
@use_insert_returning
|
382
387
|
end
|
383
388
|
|
384
|
-
def valid_type?(type)
|
385
|
-
!native_database_types[type].nil?
|
386
|
-
end
|
387
|
-
|
388
|
-
def update_table_definition(table_name, base) #:nodoc:
|
389
|
-
PostgreSQL::Table.new(table_name, base)
|
390
|
-
end
|
391
|
-
|
392
|
-
def lookup_cast_type(sql_type) # :nodoc:
|
393
|
-
oid = execute("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").first['oid'].to_i
|
394
|
-
super(oid)
|
395
|
-
end
|
396
|
-
|
397
389
|
def column_name_for_operation(operation, node) # :nodoc:
|
398
390
|
OPERATION_ALIASES.fetch(operation) { operation.downcase }
|
399
391
|
end
|
@@ -404,94 +396,115 @@ module ActiveRecord
|
|
404
396
|
"average" => "avg",
|
405
397
|
}
|
406
398
|
|
407
|
-
|
399
|
+
# Returns the version of the connected PostgreSQL server.
|
400
|
+
def postgresql_version
|
401
|
+
@connection.server_version
|
402
|
+
end
|
408
403
|
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
end
|
404
|
+
def default_index_type?(index) # :nodoc:
|
405
|
+
index.using == :btree || super
|
406
|
+
end
|
413
407
|
|
414
|
-
|
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"
|
415
413
|
FOREIGN_KEY_VIOLATION = "23503"
|
416
414
|
UNIQUE_VIOLATION = "23505"
|
415
|
+
SERIALIZATION_FAILURE = "40001"
|
416
|
+
DEADLOCK_DETECTED = "40P01"
|
417
|
+
LOCK_NOT_AVAILABLE = "55P03"
|
418
|
+
QUERY_CANCELED = "57014"
|
417
419
|
|
418
420
|
def translate_exception(exception, message)
|
419
421
|
return exception unless exception.respond_to?(:result)
|
420
422
|
|
421
|
-
case exception.result.try(:error_field,
|
423
|
+
case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
|
422
424
|
when UNIQUE_VIOLATION
|
423
|
-
RecordNotUnique.new(message
|
425
|
+
RecordNotUnique.new(message)
|
424
426
|
when FOREIGN_KEY_VIOLATION
|
425
|
-
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)
|
426
442
|
else
|
427
443
|
super
|
428
444
|
end
|
429
445
|
end
|
430
446
|
|
431
|
-
|
432
|
-
|
433
|
-
def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
|
447
|
+
def get_oid_type(oid, fmod, column_name, sql_type = "".freeze)
|
434
448
|
if !type_map.key?(oid)
|
435
|
-
load_additional_types(
|
449
|
+
load_additional_types([oid])
|
436
450
|
end
|
437
451
|
|
438
452
|
type_map.fetch(oid, fmod, sql_type) {
|
439
453
|
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
|
440
|
-
Type
|
454
|
+
Type.default_value.tap do |cast_type|
|
441
455
|
type_map.register_type(oid, cast_type)
|
442
456
|
end
|
443
457
|
}
|
444
458
|
end
|
445
459
|
|
446
|
-
def initialize_type_map(m
|
447
|
-
|
448
|
-
m.
|
449
|
-
m.
|
450
|
-
m.
|
451
|
-
m.register_type
|
452
|
-
m.alias_type
|
453
|
-
m.register_type
|
454
|
-
register_class_with_limit m,
|
455
|
-
m.alias_type
|
456
|
-
m.alias_type
|
457
|
-
m.alias_type
|
458
|
-
m.register_type
|
459
|
-
register_class_with_limit m,
|
460
|
-
register_class_with_limit m,
|
461
|
-
m.alias_type
|
462
|
-
m.register_type
|
463
|
-
|
464
|
-
|
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
|
-
m.register_type
|
476
|
-
m.register_type
|
477
|
-
m.register_type
|
478
|
-
m.register_type
|
479
|
-
|
480
|
-
|
481
|
-
m.
|
482
|
-
m.
|
483
|
-
m.
|
484
|
-
|
485
|
-
m.
|
486
|
-
m.alias_type 'lseg', 'varchar'
|
487
|
-
m.alias_type 'box', 'varchar'
|
488
|
-
|
489
|
-
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|
|
490
500
|
precision = extract_precision(sql_type)
|
491
|
-
OID::
|
501
|
+
OID::SpecializedString.new(:interval, precision: precision)
|
492
502
|
end
|
493
503
|
|
494
|
-
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|
|
495
508
|
precision = extract_precision(sql_type)
|
496
509
|
scale = extract_scale(sql_type)
|
497
510
|
|
@@ -511,51 +524,47 @@ module ActiveRecord
|
|
511
524
|
end
|
512
525
|
end
|
513
526
|
|
514
|
-
load_additional_types
|
515
|
-
end
|
516
|
-
|
517
|
-
def extract_limit(sql_type) # :nodoc:
|
518
|
-
case sql_type
|
519
|
-
when /^bigint/i, /^int8/i
|
520
|
-
8
|
521
|
-
when /^smallint/i
|
522
|
-
2
|
523
|
-
else
|
524
|
-
super
|
525
|
-
end
|
527
|
+
load_additional_types
|
526
528
|
end
|
527
529
|
|
528
530
|
# Extracts the value from a PostgreSQL column default definition.
|
529
|
-
def extract_value_from_default(
|
531
|
+
def extract_value_from_default(default)
|
530
532
|
case default
|
531
533
|
# Quoted types
|
532
|
-
|
533
|
-
|
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
|
534
541
|
# Boolean types
|
535
|
-
|
536
|
-
|
542
|
+
when "true".freeze, "false".freeze
|
543
|
+
default
|
537
544
|
# Numeric types
|
538
|
-
|
539
|
-
|
545
|
+
when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
|
546
|
+
$1
|
540
547
|
# Object identifier types
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
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
|
547
554
|
end
|
548
555
|
end
|
549
556
|
|
550
|
-
def extract_default_function(default_value, default)
|
557
|
+
def extract_default_function(default_value, default)
|
551
558
|
default if has_default_function?(default_value, default)
|
552
559
|
end
|
553
560
|
|
554
|
-
def has_default_function?(default_value, default)
|
555
|
-
!default_value &&
|
561
|
+
def has_default_function?(default_value, default)
|
562
|
+
!default_value && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
|
556
563
|
end
|
557
564
|
|
558
|
-
def load_additional_types(
|
565
|
+
def load_additional_types(oids = nil)
|
566
|
+
initializer = OID::TypeMapInitializer.new(type_map)
|
567
|
+
|
559
568
|
if supports_ranges?
|
560
569
|
query = <<-SQL
|
561
570
|
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
|
@@ -571,56 +580,86 @@ module ActiveRecord
|
|
571
580
|
|
572
581
|
if oids
|
573
582
|
query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
|
583
|
+
else
|
584
|
+
query += initializer.query_conditions_for_initial_load
|
574
585
|
end
|
575
586
|
|
576
|
-
|
577
|
-
|
578
|
-
|
587
|
+
execute_and_clear(query, "SCHEMA", []) do |records|
|
588
|
+
initializer.run(records)
|
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
|
-
# Use standard-conforming strings
|
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
|