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

/vendor/bundle/ruby/2.4.0/gems/activerecord-5.1.6/lib/active_record/connection_adapters/postgresql_adapter.rb

https://bitbucket.org/deeparaman/api-sample-app
Ruby | 871 lines | 653 code | 112 blank | 106 comment | 38 complexity | 8eafc480cbb786ca25b1da05aaaa4fc1 MD5 | raw file
Possible License(s): Unlicense, BSD-3-Clause, MIT, BSD-2-Clause, Apache-2.0
  1. # Make sure we're using pg high enough for type casts and Ruby 2.2+ compatibility
  2. gem "pg", ">= 0.18", "< 2.0"
  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. rescue PG::Error
  173. end
  174. def connection_active?
  175. @connection.status == PG::CONNECTION_OK
  176. rescue PG::Error
  177. false
  178. end
  179. end
  180. # Initializes and connects a PostgreSQL adapter.
  181. def initialize(connection, logger, connection_parameters, config)
  182. super(connection, logger, config)
  183. @connection_parameters = connection_parameters
  184. # @local_tz is initialized as nil to avoid warnings when connect tries to use it
  185. @local_tz = nil
  186. @max_identifier_length = nil
  187. connect
  188. add_pg_encoders
  189. @statements = StatementPool.new @connection,
  190. self.class.type_cast_config_to_integer(config[:statement_limit])
  191. if postgresql_version < 90100
  192. raise "Your version of PostgreSQL (#{postgresql_version}) is too old. Active Record supports PostgreSQL >= 9.1."
  193. end
  194. add_pg_decoders
  195. @type_map = Type::HashLookupTypeMap.new
  196. initialize_type_map(type_map)
  197. @local_tz = execute("SHOW TIME ZONE", "SCHEMA").first["TimeZone"]
  198. @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
  199. end
  200. # Clears the prepared statements cache.
  201. def clear_cache!
  202. @lock.synchronize do
  203. @statements.clear
  204. end
  205. end
  206. def truncate(table_name, name = nil)
  207. exec_query "TRUNCATE TABLE #{quote_table_name(table_name)}", name, []
  208. end
  209. # Is this connection alive and ready for queries?
  210. def active?
  211. @lock.synchronize do
  212. @connection.query "SELECT 1"
  213. end
  214. true
  215. rescue PG::Error
  216. false
  217. end
  218. # Close then reopen the connection.
  219. def reconnect!
  220. @lock.synchronize do
  221. super
  222. @connection.reset
  223. configure_connection
  224. end
  225. end
  226. def reset!
  227. @lock.synchronize do
  228. clear_cache!
  229. reset_transaction
  230. unless @connection.transaction_status == ::PG::PQTRANS_IDLE
  231. @connection.query "ROLLBACK"
  232. end
  233. @connection.query "DISCARD ALL"
  234. configure_connection
  235. end
  236. end
  237. # Disconnects from the database if already connected. Otherwise, this
  238. # method does nothing.
  239. def disconnect!
  240. @lock.synchronize do
  241. super
  242. @connection.close rescue nil
  243. end
  244. end
  245. def native_database_types #:nodoc:
  246. NATIVE_DATABASE_TYPES
  247. end
  248. def set_standard_conforming_strings
  249. execute("SET standard_conforming_strings = on", "SCHEMA")
  250. end
  251. def supports_ddl_transactions?
  252. true
  253. end
  254. def supports_advisory_locks?
  255. true
  256. end
  257. def supports_explain?
  258. true
  259. end
  260. def supports_extensions?
  261. true
  262. end
  263. # Range datatypes weren't introduced until PostgreSQL 9.2
  264. def supports_ranges?
  265. postgresql_version >= 90200
  266. end
  267. def supports_materialized_views?
  268. postgresql_version >= 90300
  269. end
  270. def supports_pgcrypto_uuid?
  271. postgresql_version >= 90400
  272. end
  273. def get_advisory_lock(lock_id) # :nodoc:
  274. unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
  275. raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
  276. end
  277. query_value("SELECT pg_try_advisory_lock(#{lock_id})")
  278. end
  279. def release_advisory_lock(lock_id) # :nodoc:
  280. unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
  281. raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
  282. end
  283. query_value("SELECT pg_advisory_unlock(#{lock_id})")
  284. end
  285. def enable_extension(name)
  286. exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
  287. reload_type_map
  288. }
  289. end
  290. def disable_extension(name)
  291. exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
  292. reload_type_map
  293. }
  294. end
  295. def extension_enabled?(name)
  296. if supports_extensions?
  297. res = exec_query("SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled", "SCHEMA")
  298. res.cast_values.first
  299. end
  300. end
  301. def extensions
  302. if supports_extensions?
  303. exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
  304. else
  305. super
  306. end
  307. end
  308. # Returns the configured supported identifier length supported by PostgreSQL
  309. def max_identifier_length
  310. @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
  311. end
  312. alias table_alias_length max_identifier_length
  313. alias index_name_length max_identifier_length
  314. # Set the authorized user for this session
  315. def session_auth=(user)
  316. clear_cache!
  317. execute("SET SESSION AUTHORIZATION #{user}")
  318. end
  319. def use_insert_returning?
  320. @use_insert_returning
  321. end
  322. def update_table_definition(table_name, base) #:nodoc:
  323. PostgreSQL::Table.new(table_name, base)
  324. end
  325. def column_name_for_operation(operation, node) # :nodoc:
  326. OPERATION_ALIASES.fetch(operation) { operation.downcase }
  327. end
  328. OPERATION_ALIASES = { # :nodoc:
  329. "maximum" => "max",
  330. "minimum" => "min",
  331. "average" => "avg",
  332. }
  333. # Returns the version of the connected PostgreSQL server.
  334. def postgresql_version
  335. @connection.server_version
  336. end
  337. def default_index_type?(index) # :nodoc:
  338. index.using == :btree || super
  339. end
  340. private
  341. # See http://www.postgresql.org/docs/current/static/errcodes-appendix.html
  342. VALUE_LIMIT_VIOLATION = "22001"
  343. NUMERIC_VALUE_OUT_OF_RANGE = "22003"
  344. NOT_NULL_VIOLATION = "23502"
  345. FOREIGN_KEY_VIOLATION = "23503"
  346. UNIQUE_VIOLATION = "23505"
  347. SERIALIZATION_FAILURE = "40001"
  348. DEADLOCK_DETECTED = "40P01"
  349. def translate_exception(exception, message)
  350. return exception unless exception.respond_to?(:result)
  351. case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
  352. when UNIQUE_VIOLATION
  353. RecordNotUnique.new(message)
  354. when FOREIGN_KEY_VIOLATION
  355. InvalidForeignKey.new(message)
  356. when VALUE_LIMIT_VIOLATION
  357. ValueTooLong.new(message)
  358. when NUMERIC_VALUE_OUT_OF_RANGE
  359. RangeError.new(message)
  360. when NOT_NULL_VIOLATION
  361. NotNullViolation.new(message)
  362. when SERIALIZATION_FAILURE
  363. SerializationFailure.new(message)
  364. when DEADLOCK_DETECTED
  365. Deadlocked.new(message)
  366. else
  367. super
  368. end
  369. end
  370. def get_oid_type(oid, fmod, column_name, sql_type = "".freeze)
  371. if !type_map.key?(oid)
  372. load_additional_types(type_map, [oid])
  373. end
  374. type_map.fetch(oid, fmod, sql_type) {
  375. warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
  376. Type.default_value.tap do |cast_type|
  377. type_map.register_type(oid, cast_type)
  378. end
  379. }
  380. end
  381. def initialize_type_map(m)
  382. register_class_with_limit m, "int2", Type::Integer
  383. register_class_with_limit m, "int4", Type::Integer
  384. register_class_with_limit m, "int8", Type::Integer
  385. m.register_type "oid", OID::Oid.new
  386. m.register_type "float4", Type::Float.new
  387. m.alias_type "float8", "float4"
  388. m.register_type "text", Type::Text.new
  389. register_class_with_limit m, "varchar", Type::String
  390. m.alias_type "char", "varchar"
  391. m.alias_type "name", "varchar"
  392. m.alias_type "bpchar", "varchar"
  393. m.register_type "bool", Type::Boolean.new
  394. register_class_with_limit m, "bit", OID::Bit
  395. register_class_with_limit m, "varbit", OID::BitVarying
  396. m.alias_type "timestamptz", "timestamp"
  397. m.register_type "date", Type::Date.new
  398. m.register_type "money", OID::Money.new
  399. m.register_type "bytea", OID::Bytea.new
  400. m.register_type "point", OID::Point.new
  401. m.register_type "hstore", OID::Hstore.new
  402. m.register_type "json", OID::Json.new
  403. m.register_type "jsonb", OID::Jsonb.new
  404. m.register_type "cidr", OID::Cidr.new
  405. m.register_type "inet", OID::Inet.new
  406. m.register_type "uuid", OID::Uuid.new
  407. m.register_type "xml", OID::Xml.new
  408. m.register_type "tsvector", OID::SpecializedString.new(:tsvector)
  409. m.register_type "macaddr", OID::SpecializedString.new(:macaddr)
  410. m.register_type "citext", OID::SpecializedString.new(:citext)
  411. m.register_type "ltree", OID::SpecializedString.new(:ltree)
  412. m.register_type "line", OID::SpecializedString.new(:line)
  413. m.register_type "lseg", OID::SpecializedString.new(:lseg)
  414. m.register_type "box", OID::SpecializedString.new(:box)
  415. m.register_type "path", OID::SpecializedString.new(:path)
  416. m.register_type "polygon", OID::SpecializedString.new(:polygon)
  417. m.register_type "circle", OID::SpecializedString.new(:circle)
  418. m.register_type "interval" do |_, _, sql_type|
  419. precision = extract_precision(sql_type)
  420. OID::SpecializedString.new(:interval, precision: precision)
  421. end
  422. register_class_with_precision m, "time", Type::Time
  423. register_class_with_precision m, "timestamp", OID::DateTime
  424. m.register_type "numeric" do |_, fmod, sql_type|
  425. precision = extract_precision(sql_type)
  426. scale = extract_scale(sql_type)
  427. # The type for the numeric depends on the width of the field,
  428. # so we'll do something special here.
  429. #
  430. # When dealing with decimal columns:
  431. #
  432. # places after decimal = fmod - 4 & 0xffff
  433. # places before decimal = (fmod - 4) >> 16 & 0xffff
  434. if fmod && (fmod - 4 & 0xffff).zero?
  435. # FIXME: Remove this class, and the second argument to
  436. # lookups on PG
  437. Type::DecimalWithoutScale.new(precision: precision)
  438. else
  439. OID::Decimal.new(precision: precision, scale: scale)
  440. end
  441. end
  442. load_additional_types(m)
  443. end
  444. def extract_limit(sql_type)
  445. case sql_type
  446. when /^bigint/i, /^int8/i
  447. 8
  448. when /^smallint/i
  449. 2
  450. else
  451. super
  452. end
  453. end
  454. # Extracts the value from a PostgreSQL column default definition.
  455. def extract_value_from_default(default)
  456. case default
  457. # Quoted types
  458. when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
  459. # The default 'now'::date is CURRENT_DATE
  460. if $1 == "now".freeze && $2 == "date".freeze
  461. nil
  462. else
  463. $1.gsub("''".freeze, "'".freeze)
  464. end
  465. # Boolean types
  466. when "true".freeze, "false".freeze
  467. default
  468. # Numeric types
  469. when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
  470. $1
  471. # Object identifier types
  472. when /\A-?\d+\z/
  473. $1
  474. else
  475. # Anything else is blank, some user type, or some function
  476. # and we can't know the value of that, so return nil.
  477. nil
  478. end
  479. end
  480. def extract_default_function(default_value, default)
  481. default if has_default_function?(default_value, default)
  482. end
  483. def has_default_function?(default_value, default)
  484. !default_value && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
  485. end
  486. def load_additional_types(type_map, oids = nil)
  487. initializer = OID::TypeMapInitializer.new(type_map)
  488. if supports_ranges?
  489. query = <<-SQL
  490. SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
  491. FROM pg_type as t
  492. LEFT JOIN pg_range as r ON oid = rngtypid
  493. SQL
  494. else
  495. query = <<-SQL
  496. SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
  497. FROM pg_type as t
  498. SQL
  499. end
  500. if oids
  501. query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
  502. else
  503. query += initializer.query_conditions_for_initial_load(type_map)
  504. end
  505. execute_and_clear(query, "SCHEMA", []) do |records|
  506. initializer.run(records)
  507. end
  508. end
  509. FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
  510. def execute_and_clear(sql, name, binds, prepare: false)
  511. if without_prepared_statement?(binds)
  512. result = exec_no_cache(sql, name, [])
  513. elsif !prepare
  514. result = exec_no_cache(sql, name, binds)
  515. else
  516. result = exec_cache(sql, name, binds)
  517. end
  518. ret = yield result
  519. result.clear
  520. ret
  521. end
  522. def exec_no_cache(sql, name, binds)
  523. type_casted_binds = type_casted_binds(binds)
  524. log(sql, name, binds, type_casted_binds) do
  525. ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
  526. @connection.async_exec(sql, type_casted_binds)
  527. end
  528. end
  529. end
  530. def exec_cache(sql, name, binds)
  531. stmt_key = prepare_statement(sql)
  532. type_casted_binds = type_casted_binds(binds)
  533. log(sql, name, binds, type_casted_binds, stmt_key) do
  534. ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
  535. @connection.exec_prepared(stmt_key, type_casted_binds)
  536. end
  537. end
  538. rescue ActiveRecord::StatementInvalid => e
  539. raise unless is_cached_plan_failure?(e)
  540. # Nothing we can do if we are in a transaction because all commands
  541. # will raise InFailedSQLTransaction
  542. if in_transaction?
  543. raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
  544. else
  545. @lock.synchronize do
  546. # outside of transactions we can simply flush this query and retry
  547. @statements.delete sql_key(sql)
  548. end
  549. retry
  550. end
  551. end
  552. # Annoyingly, the code for prepared statements whose return value may
  553. # have changed is FEATURE_NOT_SUPPORTED.
  554. #
  555. # This covers various different error types so we need to do additional
  556. # work to classify the exception definitively as a
  557. # ActiveRecord::PreparedStatementCacheExpired
  558. #
  559. # Check here for more details:
  560. # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
  561. CACHED_PLAN_HEURISTIC = "cached plan must not change result type".freeze
  562. def is_cached_plan_failure?(e)
  563. pgerror = e.cause
  564. code = pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE)
  565. code == FEATURE_NOT_SUPPORTED && pgerror.message.include?(CACHED_PLAN_HEURISTIC)
  566. rescue
  567. false
  568. end
  569. def in_transaction?
  570. open_transactions > 0
  571. end
  572. # Returns the statement identifier for the client side cache
  573. # of statements
  574. def sql_key(sql)
  575. "#{schema_search_path}-#{sql}"
  576. end
  577. # Prepare the statement if it hasn't been prepared, return
  578. # the statement key.
  579. def prepare_statement(sql)
  580. @lock.synchronize do
  581. sql_key = sql_key(sql)
  582. unless @statements.key? sql_key
  583. nextkey = @statements.next_key
  584. begin
  585. @connection.prepare nextkey, sql
  586. rescue => e
  587. raise translate_exception_class(e, sql)
  588. end
  589. # Clear the queue
  590. @connection.get_last_result
  591. @statements[sql_key] = nextkey
  592. end
  593. @statements[sql_key]
  594. end
  595. end
  596. # Connects to a PostgreSQL server and sets up the adapter depending on the
  597. # connected server's characteristics.
  598. def connect
  599. @connection = PG.connect(@connection_parameters)
  600. configure_connection
  601. rescue ::PG::Error => error
  602. if error.message.include?("does not exist")
  603. raise ActiveRecord::NoDatabaseError
  604. else
  605. raise
  606. end
  607. end
  608. # Configures the encoding, verbosity, schema search path, and time zone of the connection.
  609. # This is called by #connect and should not be called manually.
  610. def configure_connection
  611. if @config[:encoding]
  612. @connection.set_client_encoding(@config[:encoding])
  613. end
  614. self.client_min_messages = @config[:min_messages] || "warning"
  615. self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
  616. # Use standard-conforming strings so we don't have to do the E'...' dance.
  617. set_standard_conforming_strings
  618. # If using Active Record's time zone support configure the connection to return
  619. # TIMESTAMP WITH ZONE types in UTC.
  620. # (SET TIME ZONE does not use an equals sign like other SET variables)
  621. if ActiveRecord::Base.default_timezone == :utc
  622. execute("SET time zone 'UTC'", "SCHEMA")
  623. elsif @local_tz
  624. execute("SET time zone '#{@local_tz}'", "SCHEMA")
  625. end
  626. # SET statements from :variables config hash
  627. # http://www.postgresql.org/docs/current/static/sql-set.html
  628. variables = @config[:variables] || {}
  629. variables.map do |k, v|
  630. if v == ":default" || v == :default
  631. # Sets the value to the global or compile default
  632. execute("SET SESSION #{k} TO DEFAULT", "SCHEMA")
  633. elsif !v.nil?
  634. execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
  635. end
  636. end
  637. end
  638. # Returns the current ID of a table's sequence.
  639. def last_insert_id_result(sequence_name)
  640. exec_query("SELECT currval('#{sequence_name}')", "SQL")
  641. end
  642. # Returns the list of a table's column names, data types, and default values.
  643. #
  644. # The underlying query is roughly:
  645. # SELECT column.name, column.type, default.value, column.comment
  646. # FROM column LEFT JOIN default
  647. # ON column.table_id = default.table_id
  648. # AND column.num = default.column_num
  649. # WHERE column.table_id = get_table_id('table_name')
  650. # AND column.num > 0
  651. # AND NOT column.is_dropped
  652. # ORDER BY column.num
  653. #
  654. # If the table name is not prefixed with a schema, the database will
  655. # take the first match from the schema search path.
  656. #
  657. # Query implementation notes:
  658. # - format_type includes the column size constraint, e.g. varchar(50)
  659. # - ::regclass is a function that gives the id for a table name
  660. def column_definitions(table_name)
  661. query(<<-end_sql, "SCHEMA")
  662. SELECT a.attname, format_type(a.atttypid, a.atttypmod),
  663. pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
  664. c.collname, col_description(a.attrelid, a.attnum) AS comment
  665. FROM pg_attribute a
  666. LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
  667. LEFT JOIN pg_type t ON a.atttypid = t.oid
  668. LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation
  669. WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
  670. AND a.attnum > 0 AND NOT a.attisdropped
  671. ORDER BY a.attnum
  672. end_sql
  673. end
  674. def extract_table_ref_from_insert_sql(sql)
  675. sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
  676. $1.strip if $1
  677. end
  678. def create_table_definition(*args)
  679. PostgreSQL::TableDefinition.new(*args)
  680. end
  681. def can_perform_case_insensitive_comparison_for?(column)
  682. @case_insensitive_cache ||= {}
  683. @case_insensitive_cache[column.sql_type] ||= begin
  684. sql = <<-end_sql
  685. SELECT exists(
  686. SELECT * FROM pg_proc
  687. WHERE proname = 'lower'
  688. AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
  689. ) OR exists(
  690. SELECT * FROM pg_proc
  691. INNER JOIN pg_cast
  692. ON ARRAY[casttarget]::oidvector = proargtypes
  693. WHERE proname = 'lower'
  694. AND castsource = #{quote column.sql_type}::regtype
  695. )
  696. end_sql
  697. execute_and_clear(sql, "SCHEMA", []) do |result|
  698. result.getvalue(0, 0)
  699. end
  700. end
  701. end
  702. def add_pg_encoders
  703. map = PG::TypeMapByClass.new
  704. map[Integer] = PG::TextEncoder::Integer.new
  705. map[TrueClass] = PG::TextEncoder::Boolean.new
  706. map[FalseClass] = PG::TextEncoder::Boolean.new
  707. @connection.type_map_for_queries = map
  708. end
  709. def add_pg_decoders
  710. coders_by_name = {
  711. "int2" => PG::TextDecoder::Integer,
  712. "int4" => PG::TextDecoder::Integer,
  713. "int8" => PG::TextDecoder::Integer,
  714. "oid" => PG::TextDecoder::Integer,
  715. "float4" => PG::TextDecoder::Float,
  716. "float8" => PG::TextDecoder::Float,
  717. "bool" => PG::TextDecoder::Boolean,
  718. }
  719. known_coder_types = coders_by_name.keys.map { |n| quote(n) }
  720. query = <<-SQL % known_coder_types.join(", ")
  721. SELECT t.oid, t.typname
  722. FROM pg_type as t
  723. WHERE t.typname IN (%s)
  724. SQL
  725. coders = execute_and_clear(query, "SCHEMA", []) do |result|
  726. result
  727. .map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
  728. .compact
  729. end
  730. map = PG::TypeMapByOid.new
  731. coders.each { |coder| map.add_coder(coder) }
  732. @connection.type_map_for_results = map
  733. end
  734. def construct_coder(row, coder_class)
  735. return unless coder_class
  736. coder_class.new(oid: row["oid"].to_i, name: row["typname"])
  737. end
  738. ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
  739. ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)
  740. ActiveRecord::Type.register(:bit, OID::Bit, adapter: :postgresql)
  741. ActiveRecord::Type.register(:bit_varying, OID::BitVarying, adapter: :postgresql)
  742. ActiveRecord::Type.register(:binary, OID::Bytea, adapter: :postgresql)
  743. ActiveRecord::Type.register(:cidr, OID::Cidr, adapter: :postgresql)
  744. ActiveRecord::Type.register(:datetime, OID::DateTime, adapter: :postgresql)
  745. ActiveRecord::Type.register(:decimal, OID::Decimal, adapter: :postgresql)
  746. ActiveRecord::Type.register(:enum, OID::Enum, adapter: :postgresql)
  747. ActiveRecord::Type.register(:hstore, OID::Hstore, adapter: :postgresql)
  748. ActiveRecord::Type.register(:inet, OID::Inet, adapter: :postgresql)
  749. ActiveRecord::Type.register(:json, OID::Json, adapter: :postgresql)
  750. ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
  751. ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
  752. ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)
  753. ActiveRecord::Type.register(:legacy_point, OID::LegacyPoint, adapter: :postgresql)
  754. ActiveRecord::Type.register(:uuid, OID::Uuid, adapter: :postgresql)
  755. ActiveRecord::Type.register(:vector, OID::Vector, adapter: :postgresql)
  756. ActiveRecord::Type.register(:xml, OID::Xml, adapter: :postgresql)
  757. end
  758. end
  759. end