activerecord 4.2.0

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.

Files changed (221) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1372 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +218 -0
  5. data/examples/performance.rb +184 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record.rb +173 -0
  8. data/lib/active_record/aggregations.rb +266 -0
  9. data/lib/active_record/association_relation.rb +22 -0
  10. data/lib/active_record/associations.rb +1724 -0
  11. data/lib/active_record/associations/alias_tracker.rb +87 -0
  12. data/lib/active_record/associations/association.rb +253 -0
  13. data/lib/active_record/associations/association_scope.rb +194 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +111 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
  16. data/lib/active_record/associations/builder/association.rb +149 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +116 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +91 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +124 -0
  20. data/lib/active_record/associations/builder/has_many.rb +15 -0
  21. data/lib/active_record/associations/builder/has_one.rb +23 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +38 -0
  23. data/lib/active_record/associations/collection_association.rb +634 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1027 -0
  25. data/lib/active_record/associations/has_many_association.rb +184 -0
  26. data/lib/active_record/associations/has_many_through_association.rb +238 -0
  27. data/lib/active_record/associations/has_one_association.rb +105 -0
  28. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  29. data/lib/active_record/associations/join_dependency.rb +282 -0
  30. data/lib/active_record/associations/join_dependency/join_association.rb +122 -0
  31. data/lib/active_record/associations/join_dependency/join_base.rb +22 -0
  32. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  33. data/lib/active_record/associations/preloader.rb +203 -0
  34. data/lib/active_record/associations/preloader/association.rb +162 -0
  35. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  36. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  37. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  38. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  39. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  40. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  41. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  42. data/lib/active_record/associations/preloader/through_association.rb +96 -0
  43. data/lib/active_record/associations/singular_association.rb +86 -0
  44. data/lib/active_record/associations/through_association.rb +96 -0
  45. data/lib/active_record/attribute.rb +149 -0
  46. data/lib/active_record/attribute_assignment.rb +212 -0
  47. data/lib/active_record/attribute_decorators.rb +66 -0
  48. data/lib/active_record/attribute_methods.rb +439 -0
  49. data/lib/active_record/attribute_methods/before_type_cast.rb +71 -0
  50. data/lib/active_record/attribute_methods/dirty.rb +181 -0
  51. data/lib/active_record/attribute_methods/primary_key.rb +128 -0
  52. data/lib/active_record/attribute_methods/query.rb +40 -0
  53. data/lib/active_record/attribute_methods/read.rb +103 -0
  54. data/lib/active_record/attribute_methods/serialization.rb +70 -0
  55. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -0
  56. data/lib/active_record/attribute_methods/write.rb +83 -0
  57. data/lib/active_record/attribute_set.rb +77 -0
  58. data/lib/active_record/attribute_set/builder.rb +86 -0
  59. data/lib/active_record/attributes.rb +139 -0
  60. data/lib/active_record/autosave_association.rb +439 -0
  61. data/lib/active_record/base.rb +317 -0
  62. data/lib/active_record/callbacks.rb +313 -0
  63. data/lib/active_record/coders/json.rb +13 -0
  64. data/lib/active_record/coders/yaml_column.rb +38 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +659 -0
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +373 -0
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +133 -0
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +574 -0
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +991 -0
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +219 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -0
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +883 -0
  78. data/lib/active_record/connection_adapters/column.rb +82 -0
  79. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +282 -0
  81. data/lib/active_record/connection_adapters/mysql_adapter.rb +491 -0
  82. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  111. data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
  112. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +588 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +754 -0
  117. data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +628 -0
  119. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  120. data/lib/active_record/connection_handling.rb +132 -0
  121. data/lib/active_record/core.rb +566 -0
  122. data/lib/active_record/counter_cache.rb +175 -0
  123. data/lib/active_record/dynamic_matchers.rb +140 -0
  124. data/lib/active_record/enum.rb +198 -0
  125. data/lib/active_record/errors.rb +252 -0
  126. data/lib/active_record/explain.rb +38 -0
  127. data/lib/active_record/explain_registry.rb +30 -0
  128. data/lib/active_record/explain_subscriber.rb +29 -0
  129. data/lib/active_record/fixture_set/file.rb +56 -0
  130. data/lib/active_record/fixtures.rb +1007 -0
  131. data/lib/active_record/gem_version.rb +15 -0
  132. data/lib/active_record/inheritance.rb +247 -0
  133. data/lib/active_record/integration.rb +113 -0
  134. data/lib/active_record/locale/en.yml +47 -0
  135. data/lib/active_record/locking/optimistic.rb +204 -0
  136. data/lib/active_record/locking/pessimistic.rb +77 -0
  137. data/lib/active_record/log_subscriber.rb +75 -0
  138. data/lib/active_record/migration.rb +1051 -0
  139. data/lib/active_record/migration/command_recorder.rb +197 -0
  140. data/lib/active_record/migration/join_table.rb +15 -0
  141. data/lib/active_record/model_schema.rb +340 -0
  142. data/lib/active_record/nested_attributes.rb +548 -0
  143. data/lib/active_record/no_touching.rb +52 -0
  144. data/lib/active_record/null_relation.rb +81 -0
  145. data/lib/active_record/persistence.rb +532 -0
  146. data/lib/active_record/query_cache.rb +56 -0
  147. data/lib/active_record/querying.rb +68 -0
  148. data/lib/active_record/railtie.rb +162 -0
  149. data/lib/active_record/railties/console_sandbox.rb +5 -0
  150. data/lib/active_record/railties/controller_runtime.rb +50 -0
  151. data/lib/active_record/railties/databases.rake +391 -0
  152. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  153. data/lib/active_record/readonly_attributes.rb +23 -0
  154. data/lib/active_record/reflection.rb +881 -0
  155. data/lib/active_record/relation.rb +681 -0
  156. data/lib/active_record/relation/batches.rb +138 -0
  157. data/lib/active_record/relation/calculations.rb +403 -0
  158. data/lib/active_record/relation/delegation.rb +140 -0
  159. data/lib/active_record/relation/finder_methods.rb +528 -0
  160. data/lib/active_record/relation/merger.rb +170 -0
  161. data/lib/active_record/relation/predicate_builder.rb +126 -0
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +47 -0
  163. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  164. data/lib/active_record/relation/query_methods.rb +1176 -0
  165. data/lib/active_record/relation/spawn_methods.rb +75 -0
  166. data/lib/active_record/result.rb +131 -0
  167. data/lib/active_record/runtime_registry.rb +22 -0
  168. data/lib/active_record/sanitization.rb +191 -0
  169. data/lib/active_record/schema.rb +64 -0
  170. data/lib/active_record/schema_dumper.rb +251 -0
  171. data/lib/active_record/schema_migration.rb +56 -0
  172. data/lib/active_record/scoping.rb +87 -0
  173. data/lib/active_record/scoping/default.rb +134 -0
  174. data/lib/active_record/scoping/named.rb +164 -0
  175. data/lib/active_record/serialization.rb +22 -0
  176. data/lib/active_record/serializers/xml_serializer.rb +193 -0
  177. data/lib/active_record/statement_cache.rb +111 -0
  178. data/lib/active_record/store.rb +205 -0
  179. data/lib/active_record/tasks/database_tasks.rb +296 -0
  180. data/lib/active_record/tasks/mysql_database_tasks.rb +145 -0
  181. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  182. data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
  183. data/lib/active_record/timestamp.rb +121 -0
  184. data/lib/active_record/transactions.rb +417 -0
  185. data/lib/active_record/translation.rb +22 -0
  186. data/lib/active_record/type.rb +23 -0
  187. data/lib/active_record/type/big_integer.rb +13 -0
  188. data/lib/active_record/type/binary.rb +50 -0
  189. data/lib/active_record/type/boolean.rb +30 -0
  190. data/lib/active_record/type/date.rb +46 -0
  191. data/lib/active_record/type/date_time.rb +43 -0
  192. data/lib/active_record/type/decimal.rb +40 -0
  193. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  194. data/lib/active_record/type/decorator.rb +14 -0
  195. data/lib/active_record/type/float.rb +19 -0
  196. data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
  197. data/lib/active_record/type/integer.rb +55 -0
  198. data/lib/active_record/type/mutable.rb +16 -0
  199. data/lib/active_record/type/numeric.rb +36 -0
  200. data/lib/active_record/type/serialized.rb +56 -0
  201. data/lib/active_record/type/string.rb +36 -0
  202. data/lib/active_record/type/text.rb +11 -0
  203. data/lib/active_record/type/time.rb +26 -0
  204. data/lib/active_record/type/time_value.rb +38 -0
  205. data/lib/active_record/type/type_map.rb +64 -0
  206. data/lib/active_record/type/unsigned_integer.rb +15 -0
  207. data/lib/active_record/type/value.rb +101 -0
  208. data/lib/active_record/validations.rb +90 -0
  209. data/lib/active_record/validations/associated.rb +51 -0
  210. data/lib/active_record/validations/presence.rb +67 -0
  211. data/lib/active_record/validations/uniqueness.rb +229 -0
  212. data/lib/active_record/version.rb +8 -0
  213. data/lib/rails/generators/active_record.rb +17 -0
  214. data/lib/rails/generators/active_record/migration.rb +18 -0
  215. data/lib/rails/generators/active_record/migration/migration_generator.rb +70 -0
  216. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +22 -0
  217. data/lib/rails/generators/active_record/migration/templates/migration.rb +45 -0
  218. data/lib/rails/generators/active_record/model/model_generator.rb +52 -0
  219. data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
  220. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  221. metadata +309 -0
@@ -0,0 +1,77 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ # Value Object to hold a schema qualified name.
5
+ # This is usually the name of a PostgreSQL relation but it can also represent
6
+ # schema qualified type names. +schema+ and +identifier+ are unquoted to prevent
7
+ # double quoting.
8
+ class Name # :nodoc:
9
+ SEPARATOR = "."
10
+ attr_reader :schema, :identifier
11
+
12
+ def initialize(schema, identifier)
13
+ @schema, @identifier = unquote(schema), unquote(identifier)
14
+ end
15
+
16
+ def to_s
17
+ parts.join SEPARATOR
18
+ end
19
+
20
+ def quoted
21
+ if schema
22
+ PGconn.quote_ident(schema) << SEPARATOR << PGconn.quote_ident(identifier)
23
+ else
24
+ PGconn.quote_ident(identifier)
25
+ end
26
+ end
27
+
28
+ def ==(o)
29
+ o.class == self.class && o.parts == parts
30
+ end
31
+ alias_method :eql?, :==
32
+
33
+ def hash
34
+ parts.hash
35
+ end
36
+
37
+ protected
38
+ def unquote(part)
39
+ if part && part.start_with?('"')
40
+ part[1..-2]
41
+ else
42
+ part
43
+ end
44
+ end
45
+
46
+ def parts
47
+ @parts ||= [@schema, @identifier].compact
48
+ end
49
+ end
50
+
51
+ module Utils # :nodoc:
52
+ extend self
53
+
54
+ # Returns an instance of <tt>ActiveRecord::ConnectionAdapters::PostgreSQL::Name</tt>
55
+ # extracted from +string+.
56
+ # +schema+ is nil if not specified in +string+.
57
+ # +schema+ and +identifier+ exclude surrounding quotes (regardless of whether provided in +string+)
58
+ # +string+ supports the range of schema/table references understood by PostgreSQL, for example:
59
+ #
60
+ # * <tt>table_name</tt>
61
+ # * <tt>"table.name"</tt>
62
+ # * <tt>schema_name.table_name</tt>
63
+ # * <tt>schema_name."table.name"</tt>
64
+ # * <tt>"schema_name".table_name</tt>
65
+ # * <tt>"schema.name"."table name"</tt>
66
+ def extract_schema_qualified_name(string)
67
+ schema, table = string.scan(/[^".\s]+|"[^"]*"/)
68
+ if table.nil?
69
+ table = schema
70
+ schema = nil
71
+ end
72
+ PostgreSQL::Name.new(schema, table)
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,754 @@
1
+ require 'active_record/connection_adapters/abstract_adapter'
2
+ require 'active_record/connection_adapters/statement_pool'
3
+
4
+ require 'active_record/connection_adapters/postgresql/utils'
5
+ require 'active_record/connection_adapters/postgresql/column'
6
+ require 'active_record/connection_adapters/postgresql/oid'
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'
12
+
13
+ require 'arel/visitors/bind_visitor'
14
+
15
+ # Make sure we're using pg high enough for PGResult#values
16
+ gem 'pg', '~> 0.15'
17
+ require 'pg'
18
+
19
+ require 'ipaddr'
20
+
21
+ module ActiveRecord
22
+ 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
+ # Establishes a connection to the database that's used by all Active Record objects
30
+ def postgresql_connection(config)
31
+ conn_params = config.symbolize_keys
32
+
33
+ conn_params.delete_if { |_, v| v.nil? }
34
+
35
+ # Map ActiveRecords param names to PGs.
36
+ conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
37
+ conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
38
+
39
+ # Forward only valid config params to PGconn.connect.
40
+ conn_params.keep_if { |k, _| VALID_CONN_PARAMS.include?(k) }
41
+
42
+ # The postgres drivers don't allow the creation of an unconnected PGconn object,
43
+ # so just pass a nil connection object for the time being.
44
+ ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, conn_params, config)
45
+ end
46
+ end
47
+
48
+ module ConnectionAdapters
49
+ # The PostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
50
+ #
51
+ # Options:
52
+ #
53
+ # * <tt>:host</tt> - Defaults to a Unix-domain socket in /tmp. On machines without Unix-domain sockets,
54
+ # the default is to connect to localhost.
55
+ # * <tt>:port</tt> - Defaults to 5432.
56
+ # * <tt>:username</tt> - Defaults to be the same as the operating system name of the user running the application.
57
+ # * <tt>:password</tt> - Password to be used if the server demands password authentication.
58
+ # * <tt>:database</tt> - Defaults to be the same as the user name.
59
+ # * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
60
+ # as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
61
+ # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
62
+ # <encoding></tt> call on the connection.
63
+ # * <tt>:min_messages</tt> - An optional client min messages that is used in a
64
+ # <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
65
+ # * <tt>:variables</tt> - An optional hash of additional parameters that
66
+ # 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 or <tt>RETURNING</tt> for <tt>INSERT</tt> statements
68
+ # defaults to true.
69
+ #
70
+ # Any further options are used as connection parameters to libpq. See
71
+ # http://www.postgresql.org/docs/9.1/static/libpq-connect.html for the
72
+ # list of parameters.
73
+ #
74
+ # In addition, default connection parameters of libpq can be set per environment variables.
75
+ # See http://www.postgresql.org/docs/9.1/static/libpq-envars.html .
76
+ class PostgreSQLAdapter < AbstractAdapter
77
+ ADAPTER_NAME = 'PostgreSQL'.freeze
78
+
79
+ NATIVE_DATABASE_TYPES = {
80
+ primary_key: "serial primary key",
81
+ bigserial: "bigserial",
82
+ string: { name: "character varying" },
83
+ text: { name: "text" },
84
+ integer: { name: "integer" },
85
+ float: { name: "float" },
86
+ decimal: { name: "decimal" },
87
+ datetime: { name: "timestamp" },
88
+ time: { name: "time" },
89
+ date: { name: "date" },
90
+ daterange: { name: "daterange" },
91
+ numrange: { name: "numrange" },
92
+ tsrange: { name: "tsrange" },
93
+ tstzrange: { name: "tstzrange" },
94
+ int4range: { name: "int4range" },
95
+ int8range: { name: "int8range" },
96
+ binary: { name: "bytea" },
97
+ boolean: { name: "boolean" },
98
+ bigint: { name: "bigint" },
99
+ xml: { name: "xml" },
100
+ tsvector: { name: "tsvector" },
101
+ hstore: { name: "hstore" },
102
+ inet: { name: "inet" },
103
+ cidr: { name: "cidr" },
104
+ macaddr: { name: "macaddr" },
105
+ uuid: { name: "uuid" },
106
+ json: { name: "json" },
107
+ jsonb: { name: "jsonb" },
108
+ ltree: { name: "ltree" },
109
+ citext: { name: "citext" },
110
+ point: { name: "point" },
111
+ bit: { name: "bit" },
112
+ bit_varying: { name: "bit varying" },
113
+ money: { name: "money" },
114
+ }
115
+
116
+ OID = PostgreSQL::OID #:nodoc:
117
+
118
+ include PostgreSQL::Quoting
119
+ include PostgreSQL::ReferentialIntegrity
120
+ include PostgreSQL::SchemaStatements
121
+ include PostgreSQL::DatabaseStatements
122
+ include Savepoints
123
+
124
+ def schema_creation # :nodoc:
125
+ PostgreSQL::SchemaCreation.new self
126
+ end
127
+
128
+ # Adds +:array+ option to the default set provided by the
129
+ # AbstractAdapter
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
135
+ end
136
+
137
+ # Adds +:array+ as a valid migration key
138
+ def migration_keys
139
+ super + [:array]
140
+ end
141
+
142
+ # Returns +true+, since this connection adapter supports prepared statement
143
+ # caching.
144
+ def supports_statement_cache?
145
+ true
146
+ end
147
+
148
+ def supports_index_sort_order?
149
+ true
150
+ end
151
+
152
+ def supports_partial_index?
153
+ true
154
+ end
155
+
156
+ def supports_transaction_isolation?
157
+ true
158
+ end
159
+
160
+ def supports_foreign_keys?
161
+ true
162
+ end
163
+
164
+ def supports_views?
165
+ true
166
+ end
167
+
168
+ def index_algorithms
169
+ { concurrently: 'CONCURRENTLY' }
170
+ end
171
+
172
+ class StatementPool < ConnectionAdapters::StatementPool
173
+ def initialize(connection, max)
174
+ super
175
+ @counter = 0
176
+ @cache = Hash.new { |h,pid| h[pid] = {} }
177
+ end
178
+
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
+ def next_key
185
+ "a#{@counter + 1}"
186
+ end
187
+
188
+ def []=(sql, key)
189
+ while @max <= cache.size
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
206
+ end
207
+
208
+ private
209
+
210
+ def cache
211
+ @cache[Process.pid]
212
+ end
213
+
214
+ def dealloc(key)
215
+ @connection.query "DEALLOCATE #{key}" if connection_active?
216
+ end
217
+
218
+ def connection_active?
219
+ @connection.status == PGconn::CONNECTION_OK
220
+ rescue PGError
221
+ false
222
+ end
223
+ end
224
+
225
+ # Initializes and connects a PostgreSQL adapter.
226
+ 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
235
+
236
+ @connection_parameters, @config = connection_parameters, config
237
+
238
+ # @local_tz is initialized as nil to avoid warnings when connect tries to use it
239
+ @local_tz = nil
240
+ @table_alias_length = nil
241
+
242
+ connect
243
+ @statements = StatementPool.new @connection,
244
+ self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 })
245
+
246
+ if postgresql_version < 80200
247
+ raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
248
+ end
249
+
250
+ @type_map = Type::HashLookupTypeMap.new
251
+ initialize_type_map(type_map)
252
+ @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
253
+ @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
254
+ end
255
+
256
+ # Clears the prepared statements cache.
257
+ def clear_cache!
258
+ @statements.clear
259
+ end
260
+
261
+ def truncate(table_name, name = nil)
262
+ exec_query "TRUNCATE TABLE #{quote_table_name(table_name)}", name, []
263
+ end
264
+
265
+ # Is this connection alive and ready for queries?
266
+ def active?
267
+ @connection.query 'SELECT 1'
268
+ true
269
+ rescue PGError
270
+ false
271
+ end
272
+
273
+ # Close then reopen the connection.
274
+ def reconnect!
275
+ super
276
+ @connection.reset
277
+ configure_connection
278
+ end
279
+
280
+ def reset!
281
+ clear_cache!
282
+ reset_transaction
283
+ unless @connection.transaction_status == ::PG::PQTRANS_IDLE
284
+ @connection.query 'ROLLBACK'
285
+ end
286
+ @connection.query 'DISCARD ALL'
287
+ configure_connection
288
+ end
289
+
290
+ # Disconnects from the database if already connected. Otherwise, this
291
+ # method does nothing.
292
+ def disconnect!
293
+ super
294
+ @connection.close rescue nil
295
+ end
296
+
297
+ def native_database_types #:nodoc:
298
+ NATIVE_DATABASE_TYPES
299
+ end
300
+
301
+ # Returns true, since this connection adapter supports migrations.
302
+ def supports_migrations?
303
+ true
304
+ end
305
+
306
+ # Does PostgreSQL support finding primary key on non-Active Record tables?
307
+ def supports_primary_key? #:nodoc:
308
+ true
309
+ end
310
+
311
+ # Enable standard-conforming strings if available.
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?
320
+ true
321
+ end
322
+
323
+ def supports_explain?
324
+ true
325
+ end
326
+
327
+ # Returns true if pg > 9.1
328
+ def supports_extensions?
329
+ postgresql_version >= 90100
330
+ end
331
+
332
+ # Range datatypes weren't introduced until PostgreSQL 9.2
333
+ def supports_ranges?
334
+ postgresql_version >= 90200
335
+ end
336
+
337
+ def supports_materialized_views?
338
+ postgresql_version >= 90300
339
+ end
340
+
341
+ def enable_extension(name)
342
+ exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
343
+ reload_type_map
344
+ }
345
+ end
346
+
347
+ def disable_extension(name)
348
+ exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
349
+ reload_type_map
350
+ }
351
+ end
352
+
353
+ def extension_enabled?(name)
354
+ if supports_extensions?
355
+ res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled",
356
+ 'SCHEMA'
357
+ res.cast_values.first
358
+ end
359
+ end
360
+
361
+ def extensions
362
+ if supports_extensions?
363
+ exec_query("SELECT extname from pg_extension", "SCHEMA").cast_values
364
+ else
365
+ super
366
+ end
367
+ end
368
+
369
+ # Returns the configured supported identifier length supported by PostgreSQL
370
+ def table_alias_length
371
+ @table_alias_length ||= query('SHOW max_identifier_length', 'SCHEMA')[0][0].to_i
372
+ end
373
+
374
+ # Set the authorized user for this session
375
+ def session_auth=(user)
376
+ clear_cache!
377
+ exec_query "SET SESSION AUTHORIZATION #{user}"
378
+ end
379
+
380
+ def use_insert_returning?
381
+ @use_insert_returning
382
+ end
383
+
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
+ def column_name_for_operation(operation, node) # :nodoc:
398
+ OPERATION_ALIASES.fetch(operation) { operation.downcase }
399
+ end
400
+
401
+ OPERATION_ALIASES = { # :nodoc:
402
+ "maximum" => "max",
403
+ "minimum" => "min",
404
+ "average" => "avg",
405
+ }
406
+
407
+ protected
408
+
409
+ # Returns the version of the connected PostgreSQL server.
410
+ def postgresql_version
411
+ @connection.server_version
412
+ end
413
+
414
+ # See http://www.postgresql.org/docs/9.1/static/errcodes-appendix.html
415
+ FOREIGN_KEY_VIOLATION = "23503"
416
+ UNIQUE_VIOLATION = "23505"
417
+
418
+ def translate_exception(exception, message)
419
+ return exception unless exception.respond_to?(:result)
420
+
421
+ case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE)
422
+ when UNIQUE_VIOLATION
423
+ RecordNotUnique.new(message, exception)
424
+ when FOREIGN_KEY_VIOLATION
425
+ InvalidForeignKey.new(message, exception)
426
+ else
427
+ super
428
+ end
429
+ end
430
+
431
+ private
432
+
433
+ def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
434
+ if !type_map.key?(oid)
435
+ load_additional_types(type_map, [oid])
436
+ end
437
+
438
+ type_map.fetch(oid, fmod, sql_type) {
439
+ warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
440
+ Type::Value.new.tap do |cast_type|
441
+ type_map.register_type(oid, cast_type)
442
+ end
443
+ }
444
+ end
445
+
446
+ def initialize_type_map(m) # :nodoc:
447
+ register_class_with_limit m, 'int2', OID::Integer
448
+ m.alias_type 'int4', 'int2'
449
+ m.alias_type 'int8', 'int2'
450
+ m.alias_type 'oid', 'int2'
451
+ m.register_type 'float4', OID::Float.new
452
+ m.alias_type 'float8', 'float4'
453
+ m.register_type 'text', Type::Text.new
454
+ register_class_with_limit m, 'varchar', Type::String
455
+ m.alias_type 'char', 'varchar'
456
+ m.alias_type 'name', 'varchar'
457
+ m.alias_type 'bpchar', 'varchar'
458
+ m.register_type 'bool', Type::Boolean.new
459
+ register_class_with_limit m, 'bit', OID::Bit
460
+ register_class_with_limit m, 'varbit', OID::BitVarying
461
+ m.alias_type 'timestamptz', 'timestamp'
462
+ m.register_type 'date', OID::Date.new
463
+ m.register_type 'time', OID::Time.new
464
+
465
+ m.register_type 'money', OID::Money.new
466
+ m.register_type 'bytea', OID::Bytea.new
467
+ m.register_type 'point', OID::Point.new
468
+ m.register_type 'hstore', OID::Hstore.new
469
+ m.register_type 'json', OID::Json.new
470
+ m.register_type 'jsonb', OID::Jsonb.new
471
+ m.register_type 'cidr', OID::Cidr.new
472
+ m.register_type 'inet', OID::Inet.new
473
+ m.register_type 'uuid', OID::Uuid.new
474
+ m.register_type 'xml', OID::Xml.new
475
+ m.register_type 'tsvector', OID::SpecializedString.new(:tsvector)
476
+ m.register_type 'macaddr', OID::SpecializedString.new(:macaddr)
477
+ m.register_type 'citext', OID::SpecializedString.new(:citext)
478
+ m.register_type 'ltree', OID::SpecializedString.new(:ltree)
479
+
480
+ # FIXME: why are we keeping these types as strings?
481
+ m.alias_type 'interval', 'varchar'
482
+ m.alias_type 'path', 'varchar'
483
+ m.alias_type 'line', 'varchar'
484
+ m.alias_type 'polygon', 'varchar'
485
+ m.alias_type 'circle', 'varchar'
486
+ m.alias_type 'lseg', 'varchar'
487
+ m.alias_type 'box', 'varchar'
488
+
489
+ m.register_type 'timestamp' do |_, _, sql_type|
490
+ precision = extract_precision(sql_type)
491
+ OID::DateTime.new(precision: precision)
492
+ end
493
+
494
+ m.register_type 'numeric' do |_, fmod, sql_type|
495
+ precision = extract_precision(sql_type)
496
+ scale = extract_scale(sql_type)
497
+
498
+ # The type for the numeric depends on the width of the field,
499
+ # so we'll do something special here.
500
+ #
501
+ # When dealing with decimal columns:
502
+ #
503
+ # places after decimal = fmod - 4 & 0xffff
504
+ # places before decimal = (fmod - 4) >> 16 & 0xffff
505
+ if fmod && (fmod - 4 & 0xffff).zero?
506
+ # FIXME: Remove this class, and the second argument to
507
+ # lookups on PG
508
+ Type::DecimalWithoutScale.new(precision: precision)
509
+ else
510
+ OID::Decimal.new(precision: precision, scale: scale)
511
+ end
512
+ end
513
+
514
+ load_additional_types(m)
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
526
+ end
527
+
528
+ # Extracts the value from a PostgreSQL column default definition.
529
+ def extract_value_from_default(oid, default) # :nodoc:
530
+ case default
531
+ # Quoted types
532
+ when /\A[\(B]?'(.*)'::/m
533
+ $1.gsub(/''/, "'")
534
+ # Boolean types
535
+ when 'true', 'false'
536
+ default
537
+ # Numeric types
538
+ when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
539
+ $1
540
+ # Object identifier types
541
+ when /\A-?\d+\z/
542
+ $1
543
+ else
544
+ # Anything else is blank, some user type, or some function
545
+ # and we can't know the value of that, so return nil.
546
+ nil
547
+ end
548
+ end
549
+
550
+ def extract_default_function(default_value, default) # :nodoc:
551
+ default if has_default_function?(default_value, default)
552
+ end
553
+
554
+ def has_default_function?(default_value, default) # :nodoc:
555
+ !default_value && (%r{\w+\(.*\)} === default)
556
+ end
557
+
558
+ def load_additional_types(type_map, oids = nil) # :nodoc:
559
+ if supports_ranges?
560
+ query = <<-SQL
561
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
562
+ FROM pg_type as t
563
+ LEFT JOIN pg_range as r ON oid = rngtypid
564
+ SQL
565
+ else
566
+ query = <<-SQL
567
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
568
+ FROM pg_type as t
569
+ SQL
570
+ end
571
+
572
+ if oids
573
+ query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
574
+ end
575
+
576
+ initializer = OID::TypeMapInitializer.new(type_map)
577
+ records = execute(query, 'SCHEMA')
578
+ initializer.run(records)
579
+ end
580
+
581
+ FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
582
+
583
+ def execute_and_clear(sql, name, binds)
584
+ result = without_prepared_statement?(binds) ? exec_no_cache(sql, name, binds) :
585
+ exec_cache(sql, name, binds)
586
+ ret = yield result
587
+ result.clear
588
+ ret
589
+ end
590
+
591
+ def exec_no_cache(sql, name, binds)
592
+ log(sql, name, binds) { @connection.async_exec(sql, []) }
593
+ end
594
+
595
+ def exec_cache(sql, name, binds)
596
+ stmt_key = prepare_statement(sql)
597
+ type_casted_binds = binds.map { |col, val|
598
+ [col, type_cast(val, col)]
599
+ }
600
+
601
+ log(sql, name, type_casted_binds, stmt_key) do
602
+ @connection.exec_prepared(stmt_key, type_casted_binds.map { |_, val| val })
603
+ end
604
+ rescue ActiveRecord::StatementInvalid => e
605
+ pgerror = e.original_exception
606
+
607
+ # Get the PG code for the failure. Annoyingly, the code for
608
+ # prepared statements whose return value may have changed is
609
+ # FEATURE_NOT_SUPPORTED. Check here for more details:
610
+ # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
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
619
+ else
620
+ raise e
621
+ end
622
+ end
623
+
624
+ # Returns the statement identifier for the client side cache
625
+ # of statements
626
+ def sql_key(sql)
627
+ "#{schema_search_path}-#{sql}"
628
+ end
629
+
630
+ # Prepare the statement if it hasn't been prepared, return
631
+ # the statement key.
632
+ def prepare_statement(sql)
633
+ sql_key = sql_key(sql)
634
+ unless @statements.key? sql_key
635
+ nextkey = @statements.next_key
636
+ begin
637
+ @connection.prepare nextkey, sql
638
+ rescue => e
639
+ raise translate_exception_class(e, sql)
640
+ end
641
+ # Clear the queue
642
+ @connection.get_last_result
643
+ @statements[sql_key] = nextkey
644
+ end
645
+ @statements[sql_key]
646
+ end
647
+
648
+ # Connects to a PostgreSQL server and sets up the adapter depending on the
649
+ # connected server's characteristics.
650
+ def connect
651
+ @connection = PGconn.connect(@connection_parameters)
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
+
658
+ configure_connection
659
+ rescue ::PG::Error => error
660
+ if error.message.include?("does not exist")
661
+ raise ActiveRecord::NoDatabaseError.new(error.message, error)
662
+ else
663
+ raise
664
+ end
665
+ end
666
+
667
+ # Configures the encoding, verbosity, schema search path, and time zone of the connection.
668
+ # This is called by #connect and should not be called manually.
669
+ def configure_connection
670
+ if @config[:encoding]
671
+ @connection.set_client_encoding(@config[:encoding])
672
+ end
673
+ self.client_min_messages = @config[:min_messages] || 'warning'
674
+ self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
675
+
676
+ # Use standard-conforming strings if available so we don't have to do the E'...' dance.
677
+ set_standard_conforming_strings
678
+
679
+ # If using Active Record's time zone support configure the connection to return
680
+ # TIMESTAMP WITH ZONE types in UTC.
681
+ # (SET TIME ZONE does not use an equals sign like other SET variables)
682
+ if ActiveRecord::Base.default_timezone == :utc
683
+ execute("SET time zone 'UTC'", 'SCHEMA')
684
+ elsif @local_tz
685
+ execute("SET time zone '#{@local_tz}'", 'SCHEMA')
686
+ end
687
+
688
+ # SET statements from :variables config hash
689
+ # http://www.postgresql.org/docs/8.3/static/sql-set.html
690
+ variables = @config[:variables] || {}
691
+ variables.map do |k, v|
692
+ if v == ':default' || v == :default
693
+ # Sets the value to the global or compile default
694
+ execute("SET SESSION #{k} TO DEFAULT", 'SCHEMA')
695
+ elsif !v.nil?
696
+ execute("SET SESSION #{k} TO #{quote(v)}", 'SCHEMA')
697
+ end
698
+ end
699
+ end
700
+
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
+ # Returns the list of a table's column names, data types, and default values.
715
+ #
716
+ # The underlying query is roughly:
717
+ # SELECT column.name, column.type, default.value
718
+ # FROM column LEFT JOIN default
719
+ # ON column.table_id = default.table_id
720
+ # AND column.num = default.column_num
721
+ # WHERE column.table_id = get_table_id('table_name')
722
+ # AND column.num > 0
723
+ # AND NOT column.is_dropped
724
+ # ORDER BY column.num
725
+ #
726
+ # If the table name is not prefixed with a schema, the database will
727
+ # take the first match from the schema search path.
728
+ #
729
+ # Query implementation notes:
730
+ # - format_type includes the column size constraint, e.g. varchar(50)
731
+ # - ::regclass is a function that gives the id for a table name
732
+ def column_definitions(table_name) # :nodoc:
733
+ exec_query(<<-end_sql, 'SCHEMA').rows
734
+ SELECT a.attname, format_type(a.atttypid, a.atttypmod),
735
+ pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
736
+ FROM pg_attribute a LEFT JOIN pg_attrdef d
737
+ ON a.attrelid = d.adrelid AND a.attnum = d.adnum
738
+ WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
739
+ AND a.attnum > 0 AND NOT a.attisdropped
740
+ ORDER BY a.attnum
741
+ end_sql
742
+ end
743
+
744
+ def extract_table_ref_from_insert_sql(sql) # :nodoc:
745
+ sql[/into\s+([^\(]*).*values\s*\(/im]
746
+ $1.strip if $1
747
+ end
748
+
749
+ def create_table_definition(name, temporary, options, as = nil) # :nodoc:
750
+ PostgreSQL::TableDefinition.new native_database_types, name, temporary, options, as
751
+ end
752
+ end
753
+ end
754
+ end