PageRenderTime 56ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb

https://bitbucket.org/degzpf/rails-fixes
Ruby | 883 lines | 663 code | 114 blank | 106 comment | 39 complexity | 581e6ca8eb8c3ac0a84d2431e29c6d41 MD5 | raw file
  1. # Make sure we're using pg high enough for type casts and Ruby 2.2+ compatibility
  2. gem "pg", "~> 0.18"
  3. require "pg"
  4. require "active_record/connection_adapters/abstract_adapter"
  5. require "active_record/connection_adapters/statement_pool"
  6. require "active_record/connection_adapters/postgresql/column"
  7. require "active_record/connection_adapters/postgresql/database_statements"
  8. require "active_record/connection_adapters/postgresql/explain_pretty_printer"
  9. require "active_record/connection_adapters/postgresql/oid"
  10. require "active_record/connection_adapters/postgresql/quoting"
  11. require "active_record/connection_adapters/postgresql/referential_integrity"
  12. require "active_record/connection_adapters/postgresql/schema_creation"
  13. require "active_record/connection_adapters/postgresql/schema_definitions"
  14. require "active_record/connection_adapters/postgresql/schema_dumper"
  15. require "active_record/connection_adapters/postgresql/schema_statements"
  16. require "active_record/connection_adapters/postgresql/type_metadata"
  17. require "active_record/connection_adapters/postgresql/utils"
  18. module ActiveRecord
  19. module ConnectionHandling # :nodoc:
  20. # Establishes a connection to the database that's used by all Active Record objects
  21. def postgresql_connection(config)
  22. conn_params = config.symbolize_keys
  23. conn_params.delete_if { |_, v| v.nil? }
  24. # Map ActiveRecords param names to PGs.
  25. conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
  26. conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
  27. # Forward only valid config params to PG::Connection.connect.
  28. valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
  29. conn_params.slice!(*valid_conn_param_keys)
  30. # The postgres drivers don't allow the creation of an unconnected PG::Connection object,
  31. # so just pass a nil connection object for the time being.
  32. ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, conn_params, config)
  33. end
  34. end
  35. module ConnectionAdapters
  36. # The PostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
  37. #
  38. # Options:
  39. #
  40. # * <tt>:host</tt> - Defaults to a Unix-domain socket in /tmp. On machines without Unix-domain sockets,
  41. # the default is to connect to localhost.
  42. # * <tt>:port</tt> - Defaults to 5432.
  43. # * <tt>:username</tt> - Defaults to be the same as the operating system name of the user running the application.
  44. # * <tt>:password</tt> - Password to be used if the server demands password authentication.
  45. # * <tt>:database</tt> - Defaults to be the same as the user name.
  46. # * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
  47. # as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
  48. # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
  49. # <encoding></tt> call on the connection.
  50. # * <tt>:min_messages</tt> - An optional client min messages that is used in a
  51. # <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
  52. # * <tt>:variables</tt> - An optional hash of additional parameters that
  53. # will be used in <tt>SET SESSION key = val</tt> calls on the connection.
  54. # * <tt>:insert_returning</tt> - An optional boolean to control the use of <tt>RETURNING</tt> for <tt>INSERT</tt> statements
  55. # defaults to true.
  56. #
  57. # Any further options are used as connection parameters to libpq. See
  58. # http://www.postgresql.org/docs/current/static/libpq-connect.html for the
  59. # list of parameters.
  60. #
  61. # In addition, default connection parameters of libpq can be set per environment variables.
  62. # See http://www.postgresql.org/docs/current/static/libpq-envars.html .
  63. class PostgreSQLAdapter < AbstractAdapter
  64. ADAPTER_NAME = "PostgreSQL".freeze
  65. NATIVE_DATABASE_TYPES = {
  66. primary_key: "bigserial primary key",
  67. string: { name: "character varying" },
  68. text: { name: "text" },
  69. integer: { name: "integer" },
  70. float: { name: "float" },
  71. decimal: { name: "decimal" },
  72. datetime: { name: "timestamp" },
  73. time: { name: "time" },
  74. date: { name: "date" },
  75. daterange: { name: "daterange" },
  76. numrange: { name: "numrange" },
  77. tsrange: { name: "tsrange" },
  78. tstzrange: { name: "tstzrange" },
  79. int4range: { name: "int4range" },
  80. int8range: { name: "int8range" },
  81. binary: { name: "bytea" },
  82. boolean: { name: "boolean" },
  83. xml: { name: "xml" },
  84. tsvector: { name: "tsvector" },
  85. hstore: { name: "hstore" },
  86. inet: { name: "inet" },
  87. cidr: { name: "cidr" },
  88. macaddr: { name: "macaddr" },
  89. uuid: { name: "uuid" },
  90. json: { name: "json" },
  91. jsonb: { name: "jsonb" },
  92. ltree: { name: "ltree" },
  93. citext: { name: "citext" },
  94. point: { name: "point" },
  95. line: { name: "line" },
  96. lseg: { name: "lseg" },
  97. box: { name: "box" },
  98. path: { name: "path" },
  99. polygon: { name: "polygon" },
  100. circle: { name: "circle" },
  101. bit: { name: "bit" },
  102. bit_varying: { name: "bit varying" },
  103. money: { name: "money" },
  104. interval: { name: "interval" },
  105. oid: { name: "oid" },
  106. }
  107. OID = PostgreSQL::OID #:nodoc:
  108. include PostgreSQL::Quoting
  109. include PostgreSQL::ReferentialIntegrity
  110. include PostgreSQL::SchemaStatements
  111. include PostgreSQL::DatabaseStatements
  112. include PostgreSQL::ColumnDumper
  113. def schema_creation # :nodoc:
  114. PostgreSQL::SchemaCreation.new self
  115. end
  116. def arel_visitor # :nodoc:
  117. Arel::Visitors::PostgreSQL.new(self)
  118. end
  119. # Returns true, since this connection adapter supports prepared statement
  120. # caching.
  121. def supports_statement_cache?
  122. true
  123. end
  124. def supports_index_sort_order?
  125. true
  126. end
  127. def supports_partial_index?
  128. true
  129. end
  130. def supports_expression_index?
  131. true
  132. end
  133. def supports_transaction_isolation?
  134. true
  135. end
  136. def supports_foreign_keys?
  137. true
  138. end
  139. def supports_views?
  140. true
  141. end
  142. def supports_datetime_with_precision?
  143. true
  144. end
  145. def supports_json?
  146. postgresql_version >= 90200
  147. end
  148. def supports_comments?
  149. true
  150. end
  151. def supports_savepoints?
  152. true
  153. end
  154. def index_algorithms
  155. { concurrently: "CONCURRENTLY" }
  156. end
  157. class StatementPool < ConnectionAdapters::StatementPool
  158. def initialize(connection, max)
  159. super(max)
  160. @connection = connection
  161. @counter = 0
  162. end
  163. def next_key
  164. "a#{@counter + 1}"
  165. end
  166. def []=(sql, key)
  167. super.tap { @counter += 1 }
  168. end
  169. private
  170. def dealloc(key)
  171. @connection.query "DEALLOCATE #{key}" if connection_active?
  172. end
  173. def connection_active?
  174. @connection.status == PG::CONNECTION_OK
  175. rescue PG::Error
  176. false
  177. end
  178. end
  179. # Initializes and connects a PostgreSQL adapter.
  180. def initialize(connection, logger, connection_parameters, config)
  181. super(connection, logger, config)
  182. @connection_parameters = connection_parameters
  183. # @local_tz is initialized as nil to avoid warnings when connect tries to use it
  184. @local_tz = nil
  185. @max_identifier_length = nil
  186. connect
  187. add_pg_encoders
  188. @statements = StatementPool.new @connection,
  189. self.class.type_cast_config_to_integer(config[:statement_limit])
  190. if postgresql_version < 90100
  191. raise "Your version of PostgreSQL (#{postgresql_version}) is too old. Active Record supports PostgreSQL >= 9.1."
  192. end
  193. add_pg_decoders
  194. @type_map = Type::HashLookupTypeMap.new
  195. initialize_type_map(type_map)
  196. @local_tz = execute("SHOW TIME ZONE", "SCHEMA").first["TimeZone"]
  197. @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
  198. end
  199. # Clears the prepared statements cache.
  200. def clear_cache!
  201. @lock.synchronize do
  202. @statements.clear
  203. end
  204. end
  205. def truncate(table_name, name = nil)
  206. exec_query "TRUNCATE TABLE #{quote_table_name(table_name)}", name, []
  207. end
  208. # Is this connection alive and ready for queries?
  209. def active?
  210. @lock.synchronize do
  211. @connection.query "SELECT 1"
  212. end
  213. true
  214. rescue PG::Error
  215. false
  216. end
  217. # Close then reopen the connection.
  218. def reconnect!
  219. @lock.synchronize do
  220. super
  221. @connection.reset
  222. configure_connection
  223. end
  224. end
  225. def reset!
  226. @lock.synchronize do
  227. clear_cache!
  228. reset_transaction
  229. unless @connection.transaction_status == ::PG::PQTRANS_IDLE
  230. @connection.query "ROLLBACK"
  231. end
  232. @connection.query "DISCARD ALL"
  233. configure_connection
  234. end
  235. end
  236. # Disconnects from the database if already connected. Otherwise, this
  237. # method does nothing.
  238. def disconnect!
  239. @lock.synchronize do
  240. super
  241. @connection.close rescue nil
  242. end
  243. end
  244. def native_database_types #:nodoc:
  245. NATIVE_DATABASE_TYPES
  246. end
  247. def set_standard_conforming_strings
  248. execute("SET standard_conforming_strings = on", "SCHEMA")
  249. end
  250. def supports_ddl_transactions?
  251. true
  252. end
  253. def supports_advisory_locks?
  254. true
  255. end
  256. def supports_explain?
  257. true
  258. end
  259. def supports_extensions?
  260. true
  261. end
  262. # Range datatypes weren't introduced until PostgreSQL 9.2
  263. def supports_ranges?
  264. postgresql_version >= 90200
  265. end
  266. def supports_materialized_views?
  267. postgresql_version >= 90300
  268. end
  269. def supports_pgcrypto_uuid?
  270. postgresql_version >= 90400
  271. end
  272. def get_advisory_lock(lock_id) # :nodoc:
  273. unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
  274. raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
  275. end
  276. query_value("SELECT pg_try_advisory_lock(#{lock_id})")
  277. end
  278. def release_advisory_lock(lock_id) # :nodoc:
  279. unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
  280. raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
  281. end
  282. query_value("SELECT pg_advisory_unlock(#{lock_id})")
  283. end
  284. def enable_extension(name)
  285. exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
  286. reload_type_map
  287. }
  288. end
  289. def enable_extension_with_schema(name, schema)
  290. exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\" SCHEMA \"#{schema}\"").tap {
  291. reload_type_map
  292. }
  293. end
  294. def disable_extension(name)
  295. exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
  296. reload_type_map
  297. }
  298. end
  299. def extension_enabled?(name)
  300. if supports_extensions?
  301. res = exec_query("SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled", "SCHEMA")
  302. res.cast_values.first
  303. end
  304. end
  305. def extensions
  306. if supports_extensions?
  307. exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
  308. else
  309. super
  310. end
  311. end
  312. def extensions_and_schemas
  313. if supports_extensions?
  314. exec_query("SELECT pg_namespace.nspname, pg_extension.extname FROM pg_extension , pg_namespace WHERE pg_extension.extnamespace = pg_namespace.oid;", "SCHEMA").cast_values
  315. else
  316. super
  317. end
  318. end
  319. # Returns the configured supported identifier length supported by PostgreSQL
  320. def table_alias_length
  321. @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
  322. end
  323. alias index_name_length table_alias_length
  324. # Set the authorized user for this session
  325. def session_auth=(user)
  326. clear_cache!
  327. execute("SET SESSION AUTHORIZATION #{user}")
  328. end
  329. def use_insert_returning?
  330. @use_insert_returning
  331. end
  332. def update_table_definition(table_name, base) #:nodoc:
  333. PostgreSQL::Table.new(table_name, base)
  334. end
  335. def column_name_for_operation(operation, node) # :nodoc:
  336. OPERATION_ALIASES.fetch(operation) { operation.downcase }
  337. end
  338. OPERATION_ALIASES = { # :nodoc:
  339. "maximum" => "max",
  340. "minimum" => "min",
  341. "average" => "avg",
  342. }
  343. # Returns the version of the connected PostgreSQL server.
  344. def postgresql_version
  345. @connection.server_version
  346. end
  347. def default_index_type?(index) # :nodoc:
  348. index.using == :btree || super
  349. end
  350. private
  351. # See http://www.postgresql.org/docs/current/static/errcodes-appendix.html
  352. VALUE_LIMIT_VIOLATION = "22001"
  353. NUMERIC_VALUE_OUT_OF_RANGE = "22003"
  354. NOT_NULL_VIOLATION = "23502"
  355. FOREIGN_KEY_VIOLATION = "23503"
  356. UNIQUE_VIOLATION = "23505"
  357. SERIALIZATION_FAILURE = "40001"
  358. DEADLOCK_DETECTED = "40P01"
  359. def translate_exception(exception, message)
  360. return exception unless exception.respond_to?(:result)
  361. case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
  362. when UNIQUE_VIOLATION
  363. RecordNotUnique.new(message)
  364. when FOREIGN_KEY_VIOLATION
  365. InvalidForeignKey.new(message)
  366. when VALUE_LIMIT_VIOLATION
  367. ValueTooLong.new(message)
  368. when NUMERIC_VALUE_OUT_OF_RANGE
  369. RangeError.new(message)
  370. when NOT_NULL_VIOLATION
  371. NotNullViolation.new(message)
  372. when SERIALIZATION_FAILURE
  373. SerializationFailure.new(message)
  374. when DEADLOCK_DETECTED
  375. Deadlocked.new(message)
  376. else
  377. super
  378. end
  379. end
  380. def get_oid_type(oid, fmod, column_name, sql_type = "".freeze)
  381. if !type_map.key?(oid)
  382. load_additional_types(type_map, [oid])
  383. end
  384. type_map.fetch(oid, fmod, sql_type) {
  385. warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
  386. Type.default_value.tap do |cast_type|
  387. type_map.register_type(oid, cast_type)
  388. end
  389. }
  390. end
  391. def initialize_type_map(m)
  392. register_class_with_limit m, "int2", Type::Integer
  393. register_class_with_limit m, "int4", Type::Integer
  394. register_class_with_limit m, "int8", Type::Integer
  395. m.register_type "oid", OID::Oid.new
  396. m.register_type "float4", Type::Float.new
  397. m.alias_type "float8", "float4"
  398. m.register_type "text", Type::Text.new
  399. register_class_with_limit m, "varchar", Type::String
  400. m.alias_type "char", "varchar"
  401. m.alias_type "name", "varchar"
  402. m.alias_type "bpchar", "varchar"
  403. m.register_type "bool", Type::Boolean.new
  404. register_class_with_limit m, "bit", OID::Bit
  405. register_class_with_limit m, "varbit", OID::BitVarying
  406. m.alias_type "timestamptz", "timestamp"
  407. m.register_type "date", Type::Date.new
  408. m.register_type "money", OID::Money.new
  409. m.register_type "bytea", OID::Bytea.new
  410. m.register_type "point", OID::Point.new
  411. m.register_type "hstore", OID::Hstore.new
  412. m.register_type "json", OID::Json.new
  413. m.register_type "jsonb", OID::Jsonb.new
  414. m.register_type "cidr", OID::Cidr.new
  415. m.register_type "inet", OID::Inet.new
  416. m.register_type "uuid", OID::Uuid.new
  417. m.register_type "xml", OID::Xml.new
  418. m.register_type "tsvector", OID::SpecializedString.new(:tsvector)
  419. m.register_type "macaddr", OID::SpecializedString.new(:macaddr)
  420. m.register_type "citext", OID::SpecializedString.new(:citext)
  421. m.register_type "ltree", OID::SpecializedString.new(:ltree)
  422. m.register_type "line", OID::SpecializedString.new(:line)
  423. m.register_type "lseg", OID::SpecializedString.new(:lseg)
  424. m.register_type "box", OID::SpecializedString.new(:box)
  425. m.register_type "path", OID::SpecializedString.new(:path)
  426. m.register_type "polygon", OID::SpecializedString.new(:polygon)
  427. m.register_type "circle", OID::SpecializedString.new(:circle)
  428. m.register_type "interval" do |_, _, sql_type|
  429. precision = extract_precision(sql_type)
  430. OID::SpecializedString.new(:interval, precision: precision)
  431. end
  432. register_class_with_precision m, "time", Type::Time
  433. register_class_with_precision m, "timestamp", OID::DateTime
  434. m.register_type "numeric" do |_, fmod, sql_type|
  435. precision = extract_precision(sql_type)
  436. scale = extract_scale(sql_type)
  437. # The type for the numeric depends on the width of the field,
  438. # so we'll do something special here.
  439. #
  440. # When dealing with decimal columns:
  441. #
  442. # places after decimal = fmod - 4 & 0xffff
  443. # places before decimal = (fmod - 4) >> 16 & 0xffff
  444. if fmod && (fmod - 4 & 0xffff).zero?
  445. # FIXME: Remove this class, and the second argument to
  446. # lookups on PG
  447. Type::DecimalWithoutScale.new(precision: precision)
  448. else
  449. OID::Decimal.new(precision: precision, scale: scale)
  450. end
  451. end
  452. load_additional_types(m)
  453. end
  454. def extract_limit(sql_type)
  455. case sql_type
  456. when /^bigint/i, /^int8/i
  457. 8
  458. when /^smallint/i
  459. 2
  460. else
  461. super
  462. end
  463. end
  464. # Extracts the value from a PostgreSQL column default definition.
  465. def extract_value_from_default(default)
  466. case default
  467. # Quoted types
  468. when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
  469. # The default 'now'::date is CURRENT_DATE
  470. if $1 == "now".freeze && $2 == "date".freeze
  471. nil
  472. else
  473. $1.gsub("''".freeze, "'".freeze)
  474. end
  475. # Boolean types
  476. when "true".freeze, "false".freeze
  477. default
  478. # Numeric types
  479. when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
  480. $1
  481. # Object identifier types
  482. when /\A-?\d+\z/
  483. $1
  484. else
  485. # Anything else is blank, some user type, or some function
  486. # and we can't know the value of that, so return nil.
  487. nil
  488. end
  489. end
  490. def extract_default_function(default_value, default)
  491. default if has_default_function?(default_value, default)
  492. end
  493. def has_default_function?(default_value, default)
  494. !default_value && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
  495. end
  496. def load_additional_types(type_map, oids = nil)
  497. initializer = OID::TypeMapInitializer.new(type_map)
  498. if supports_ranges?
  499. query = <<-SQL
  500. SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
  501. FROM pg_type as t
  502. LEFT JOIN pg_range as r ON oid = rngtypid
  503. SQL
  504. else
  505. query = <<-SQL
  506. SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
  507. FROM pg_type as t
  508. SQL
  509. end
  510. if oids
  511. query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
  512. else
  513. query += initializer.query_conditions_for_initial_load(type_map)
  514. end
  515. execute_and_clear(query, "SCHEMA", []) do |records|
  516. initializer.run(records)
  517. end
  518. end
  519. FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
  520. def execute_and_clear(sql, name, binds, prepare: false)
  521. if without_prepared_statement?(binds)
  522. result = exec_no_cache(sql, name, [])
  523. elsif !prepare
  524. result = exec_no_cache(sql, name, binds)
  525. else
  526. result = exec_cache(sql, name, binds)
  527. end
  528. ret = yield result
  529. result.clear
  530. ret
  531. end
  532. def exec_no_cache(sql, name, binds)
  533. type_casted_binds = type_casted_binds(binds)
  534. log(sql, name, binds, type_casted_binds) do
  535. ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
  536. @connection.async_exec(sql, type_casted_binds)
  537. end
  538. end
  539. end
  540. def exec_cache(sql, name, binds)
  541. stmt_key = prepare_statement(sql)
  542. type_casted_binds = type_casted_binds(binds)
  543. log(sql, name, binds, type_casted_binds, stmt_key) do
  544. ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
  545. @connection.exec_prepared(stmt_key, type_casted_binds)
  546. end
  547. end
  548. rescue ActiveRecord::StatementInvalid => e
  549. raise unless is_cached_plan_failure?(e)
  550. # Nothing we can do if we are in a transaction because all commands
  551. # will raise InFailedSQLTransaction
  552. if in_transaction?
  553. raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
  554. else
  555. @lock.synchronize do
  556. # outside of transactions we can simply flush this query and retry
  557. @statements.delete sql_key(sql)
  558. end
  559. retry
  560. end
  561. end
  562. # Annoyingly, the code for prepared statements whose return value may
  563. # have changed is FEATURE_NOT_SUPPORTED.
  564. #
  565. # This covers various different error types so we need to do additional
  566. # work to classify the exception definitively as a
  567. # ActiveRecord::PreparedStatementCacheExpired
  568. #
  569. # Check here for more details:
  570. # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
  571. CACHED_PLAN_HEURISTIC = "cached plan must not change result type".freeze
  572. def is_cached_plan_failure?(e)
  573. pgerror = e.cause
  574. code = pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE)
  575. code == FEATURE_NOT_SUPPORTED && pgerror.message.include?(CACHED_PLAN_HEURISTIC)
  576. rescue
  577. false
  578. end
  579. def in_transaction?
  580. open_transactions > 0
  581. end
  582. # Returns the statement identifier for the client side cache
  583. # of statements
  584. def sql_key(sql)
  585. "#{schema_search_path}-#{sql}"
  586. end
  587. # Prepare the statement if it hasn't been prepared, return
  588. # the statement key.
  589. def prepare_statement(sql)
  590. @lock.synchronize do
  591. sql_key = sql_key(sql)
  592. unless @statements.key? sql_key
  593. nextkey = @statements.next_key
  594. begin
  595. @connection.prepare nextkey, sql
  596. rescue => e
  597. raise translate_exception_class(e, sql)
  598. end
  599. # Clear the queue
  600. @connection.get_last_result
  601. @statements[sql_key] = nextkey
  602. end
  603. @statements[sql_key]
  604. end
  605. end
  606. # Connects to a PostgreSQL server and sets up the adapter depending on the
  607. # connected server's characteristics.
  608. def connect
  609. @connection = PG.connect(@connection_parameters)
  610. configure_connection
  611. rescue ::PG::Error => error
  612. if error.message.include?("does not exist")
  613. raise ActiveRecord::NoDatabaseError
  614. else
  615. raise
  616. end
  617. end
  618. # Configures the encoding, verbosity, schema search path, and time zone of the connection.
  619. # This is called by #connect and should not be called manually.
  620. def configure_connection
  621. if @config[:encoding]
  622. @connection.set_client_encoding(@config[:encoding])
  623. end
  624. self.client_min_messages = @config[:min_messages] || "warning"
  625. self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
  626. # Use standard-conforming strings so we don't have to do the E'...' dance.
  627. set_standard_conforming_strings
  628. # If using Active Record's time zone support configure the connection to return
  629. # TIMESTAMP WITH ZONE types in UTC.
  630. # (SET TIME ZONE does not use an equals sign like other SET variables)
  631. if ActiveRecord::Base.default_timezone == :utc
  632. execute("SET time zone 'UTC'", "SCHEMA")
  633. elsif @local_tz
  634. execute("SET time zone '#{@local_tz}'", "SCHEMA")
  635. end
  636. # SET statements from :variables config hash
  637. # http://www.postgresql.org/docs/current/static/sql-set.html
  638. variables = @config[:variables] || {}
  639. variables.map do |k, v|
  640. if v == ":default" || v == :default
  641. # Sets the value to the global or compile default
  642. execute("SET SESSION #{k} TO DEFAULT", "SCHEMA")
  643. elsif !v.nil?
  644. execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
  645. end
  646. end
  647. end
  648. # Returns the current ID of a table's sequence.
  649. def last_insert_id_result(sequence_name)
  650. exec_query("SELECT currval('#{sequence_name}')", "SQL")
  651. end
  652. # Returns the list of a table's column names, data types, and default values.
  653. #
  654. # The underlying query is roughly:
  655. # SELECT column.name, column.type, default.value, column.comment
  656. # FROM column LEFT JOIN default
  657. # ON column.table_id = default.table_id
  658. # AND column.num = default.column_num
  659. # WHERE column.table_id = get_table_id('table_name')
  660. # AND column.num > 0
  661. # AND NOT column.is_dropped
  662. # ORDER BY column.num
  663. #
  664. # If the table name is not prefixed with a schema, the database will
  665. # take the first match from the schema search path.
  666. #
  667. # Query implementation notes:
  668. # - format_type includes the column size constraint, e.g. varchar(50)
  669. # - ::regclass is a function that gives the id for a table name
  670. def column_definitions(table_name)
  671. query(<<-end_sql, "SCHEMA")
  672. SELECT a.attname, format_type(a.atttypid, a.atttypmod),
  673. pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
  674. c.collname, col_description(a.attrelid, a.attnum) AS comment
  675. FROM pg_attribute a
  676. LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
  677. LEFT JOIN pg_type t ON a.atttypid = t.oid
  678. LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation
  679. WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
  680. AND a.attnum > 0 AND NOT a.attisdropped
  681. ORDER BY a.attnum
  682. end_sql
  683. end
  684. def extract_table_ref_from_insert_sql(sql)
  685. sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
  686. $1.strip if $1
  687. end
  688. def create_table_definition(*args)
  689. PostgreSQL::TableDefinition.new(*args)
  690. end
  691. def can_perform_case_insensitive_comparison_for?(column)
  692. @case_insensitive_cache ||= {}
  693. @case_insensitive_cache[column.sql_type] ||= begin
  694. sql = <<-end_sql
  695. SELECT exists(
  696. SELECT * FROM pg_proc
  697. WHERE proname = 'lower'
  698. AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
  699. ) OR exists(
  700. SELECT * FROM pg_proc
  701. INNER JOIN pg_cast
  702. ON ARRAY[casttarget]::oidvector = proargtypes
  703. WHERE proname = 'lower'
  704. AND castsource = #{quote column.sql_type}::regtype
  705. )
  706. end_sql
  707. execute_and_clear(sql, "SCHEMA", []) do |result|
  708. result.getvalue(0, 0)
  709. end
  710. end
  711. end
  712. def add_pg_encoders
  713. map = PG::TypeMapByClass.new
  714. map[Integer] = PG::TextEncoder::Integer.new
  715. map[TrueClass] = PG::TextEncoder::Boolean.new
  716. map[FalseClass] = PG::TextEncoder::Boolean.new
  717. @connection.type_map_for_queries = map
  718. end
  719. def add_pg_decoders
  720. coders_by_name = {
  721. "int2" => PG::TextDecoder::Integer,
  722. "int4" => PG::TextDecoder::Integer,
  723. "int8" => PG::TextDecoder::Integer,
  724. "oid" => PG::TextDecoder::Integer,
  725. "float4" => PG::TextDecoder::Float,
  726. "float8" => PG::TextDecoder::Float,
  727. "bool" => PG::TextDecoder::Boolean,
  728. }
  729. known_coder_types = coders_by_name.keys.map { |n| quote(n) }
  730. query = <<-SQL % known_coder_types.join(", ")
  731. SELECT t.oid, t.typname
  732. FROM pg_type as t
  733. WHERE t.typname IN (%s)
  734. SQL
  735. coders = execute_and_clear(query, "SCHEMA", []) do |result|
  736. result
  737. .map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
  738. .compact
  739. end
  740. map = PG::TypeMapByOid.new
  741. coders.each { |coder| map.add_coder(coder) }
  742. @connection.type_map_for_results = map
  743. end
  744. def construct_coder(row, coder_class)
  745. return unless coder_class
  746. coder_class.new(oid: row["oid"].to_i, name: row["typname"])
  747. end
  748. ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
  749. ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)
  750. ActiveRecord::Type.register(:bit, OID::Bit, adapter: :postgresql)
  751. ActiveRecord::Type.register(:bit_varying, OID::BitVarying, adapter: :postgresql)
  752. ActiveRecord::Type.register(:binary, OID::Bytea, adapter: :postgresql)
  753. ActiveRecord::Type.register(:cidr, OID::Cidr, adapter: :postgresql)
  754. ActiveRecord::Type.register(:datetime, OID::DateTime, adapter: :postgresql)
  755. ActiveRecord::Type.register(:decimal, OID::Decimal, adapter: :postgresql)
  756. ActiveRecord::Type.register(:enum, OID::Enum, adapter: :postgresql)
  757. ActiveRecord::Type.register(:hstore, OID::Hstore, adapter: :postgresql)
  758. ActiveRecord::Type.register(:inet, OID::Inet, adapter: :postgresql)
  759. ActiveRecord::Type.register(:json, OID::Json, adapter: :postgresql)
  760. ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
  761. ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
  762. ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)
  763. ActiveRecord::Type.register(:legacy_point, OID::LegacyPoint, adapter: :postgresql)
  764. ActiveRecord::Type.register(:uuid, OID::Uuid, adapter: :postgresql)
  765. ActiveRecord::Type.register(:vector, OID::Vector, adapter: :postgresql)
  766. ActiveRecord::Type.register(:xml, OID::Xml, adapter: :postgresql)
  767. end
  768. end
  769. end