PageRenderTime 103ms CodeModel.GetById 26ms RepoModel.GetById 2ms app.codeStats 0ms

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

https://github.com/zipme/rails
Ruby | 921 lines | 626 code | 131 blank | 164 comment | 34 complexity | ba87abb4cc8c8cd3e62a13235803edbc MD5 | raw file
  1. require 'active_record/connection_adapters/abstract_adapter'
  2. require 'active_record/connection_adapters/statement_pool'
  3. require 'active_record/connection_adapters/postgresql/oid'
  4. require 'active_record/connection_adapters/postgresql/cast'
  5. require 'active_record/connection_adapters/postgresql/array_parser'
  6. require 'active_record/connection_adapters/postgresql/quoting'
  7. require 'active_record/connection_adapters/postgresql/schema_statements'
  8. require 'active_record/connection_adapters/postgresql/database_statements'
  9. require 'active_record/connection_adapters/postgresql/referential_integrity'
  10. require 'arel/visitors/bind_visitor'
  11. # Make sure we're using pg high enough for PGResult#values
  12. gem 'pg', '~> 0.11'
  13. require 'pg'
  14. require 'ipaddr'
  15. module ActiveRecord
  16. module ConnectionHandling # :nodoc:
  17. VALID_CONN_PARAMS = [:host, :hostaddr, :port, :dbname, :user, :password, :connect_timeout,
  18. :client_encoding, :options, :application_name, :fallback_application_name,
  19. :keepalives, :keepalives_idle, :keepalives_interval, :keepalives_count,
  20. :tty, :sslmode, :requiressl, :sslcert, :sslkey, :sslrootcert, :sslcrl,
  21. :requirepeer, :krbsrvname, :gsslib, :service]
  22. # Establishes a connection to the database that's used by all Active Record objects
  23. def postgresql_connection(config)
  24. conn_params = config.symbolize_keys
  25. conn_params.delete_if { |_, v| v.nil? }
  26. # Map ActiveRecords param names to PGs.
  27. conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
  28. conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
  29. # Forward only valid config params to PGconn.connect.
  30. conn_params.keep_if { |k, _| VALID_CONN_PARAMS.include?(k) }
  31. # The postgres drivers don't allow the creation of an unconnected PGconn object,
  32. # so just pass a nil connection object for the time being.
  33. ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, conn_params, config)
  34. end
  35. end
  36. module ConnectionAdapters
  37. # PostgreSQL-specific extensions to column definitions in a table.
  38. class PostgreSQLColumn < Column #:nodoc:
  39. attr_accessor :array
  40. # Instantiates a new PostgreSQL column definition in a table.
  41. def initialize(name, default, oid_type, sql_type = nil, null = true)
  42. @oid_type = oid_type
  43. if sql_type =~ /\[\]$/
  44. @array = true
  45. super(name, self.class.extract_value_from_default(default), sql_type[0..sql_type.length - 3], null)
  46. else
  47. @array = false
  48. super(name, self.class.extract_value_from_default(default), sql_type, null)
  49. end
  50. end
  51. # :stopdoc:
  52. class << self
  53. include ConnectionAdapters::PostgreSQLColumn::Cast
  54. include ConnectionAdapters::PostgreSQLColumn::ArrayParser
  55. attr_accessor :money_precision
  56. end
  57. # :startdoc:
  58. # Extracts the value from a PostgreSQL column default definition.
  59. def self.extract_value_from_default(default)
  60. # This is a performance optimization for Ruby 1.9.2 in development.
  61. # If the value is nil, we return nil straight away without checking
  62. # the regular expressions. If we check each regular expression,
  63. # Regexp#=== will call NilClass#to_str, which will trigger
  64. # method_missing (defined by whiny nil in ActiveSupport) which
  65. # makes this method very very slow.
  66. return default unless default
  67. case default
  68. when /\A'(.*)'::(num|date|tstz|ts|int4|int8)range\z/m
  69. $1
  70. # Numeric types
  71. when /\A\(?(-?\d+(\.\d*)?\)?(::bigint)?)\z/
  72. $1
  73. # Character types
  74. when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m
  75. $1
  76. # Binary data types
  77. when /\A'(.*)'::bytea\z/m
  78. $1
  79. # Date/time types
  80. when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
  81. $1
  82. when /\A'(.*)'::interval\z/
  83. $1
  84. # Boolean type
  85. when 'true'
  86. true
  87. when 'false'
  88. false
  89. # Geometric types
  90. when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
  91. $1
  92. # Network address types
  93. when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
  94. $1
  95. # Bit string types
  96. when /\AB'(.*)'::"?bit(?: varying)?"?\z/
  97. $1
  98. # XML type
  99. when /\A'(.*)'::xml\z/m
  100. $1
  101. # Arrays
  102. when /\A'(.*)'::"?\D+"?\[\]\z/
  103. $1
  104. # Hstore
  105. when /\A'(.*)'::hstore\z/
  106. $1
  107. # JSON
  108. when /\A'(.*)'::json\z/
  109. $1
  110. # Object identifier types
  111. when /\A-?\d+\z/
  112. $1
  113. else
  114. # Anything else is blank, some user type, or some function
  115. # and we can't know the value of that, so return nil.
  116. nil
  117. end
  118. end
  119. def type_cast(value)
  120. return if value.nil?
  121. return super if encoded?
  122. @oid_type.type_cast value
  123. end
  124. private
  125. def extract_limit(sql_type)
  126. case sql_type
  127. when /^bigint/i; 8
  128. when /^smallint/i; 2
  129. when /^timestamp/i; nil
  130. else super
  131. end
  132. end
  133. # Extracts the scale from PostgreSQL-specific data types.
  134. def extract_scale(sql_type)
  135. # Money type has a fixed scale of 2.
  136. sql_type =~ /^money/ ? 2 : super
  137. end
  138. # Extracts the precision from PostgreSQL-specific data types.
  139. def extract_precision(sql_type)
  140. if sql_type == 'money'
  141. self.class.money_precision
  142. elsif sql_type =~ /timestamp/i
  143. $1.to_i if sql_type =~ /\((\d+)\)/
  144. else
  145. super
  146. end
  147. end
  148. # Maps PostgreSQL-specific data types to logical Rails types.
  149. def simplified_type(field_type)
  150. case field_type
  151. # Numeric and monetary types
  152. when /^(?:real|double precision)$/
  153. :float
  154. # Monetary types
  155. when 'money'
  156. :decimal
  157. when 'hstore'
  158. :hstore
  159. when 'ltree'
  160. :ltree
  161. # Network address types
  162. when 'inet'
  163. :inet
  164. when 'cidr'
  165. :cidr
  166. when 'macaddr'
  167. :macaddr
  168. # Character types
  169. when /^(?:character varying|bpchar)(?:\(\d+\))?$/
  170. :string
  171. # Binary data types
  172. when 'bytea'
  173. :binary
  174. # Date/time types
  175. when /^timestamp with(?:out)? time zone$/
  176. :datetime
  177. when /^interval(?:|\(\d+\))$/
  178. :string
  179. # Geometric types
  180. when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/
  181. :string
  182. # Bit strings
  183. when /^bit(?: varying)?(?:\(\d+\))?$/
  184. :string
  185. # XML type
  186. when 'xml'
  187. :xml
  188. # tsvector type
  189. when 'tsvector'
  190. :tsvector
  191. # Arrays
  192. when /^\D+\[\]$/
  193. :string
  194. # Object identifier types
  195. when 'oid'
  196. :integer
  197. # UUID type
  198. when 'uuid'
  199. :uuid
  200. # JSON type
  201. when 'json'
  202. :json
  203. # Small and big integer types
  204. when /^(?:small|big)int$/
  205. :integer
  206. when /(num|date|tstz|ts|int4|int8)range$/
  207. field_type.to_sym
  208. # Pass through all types that are not specific to PostgreSQL.
  209. else
  210. super
  211. end
  212. end
  213. end
  214. # The PostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
  215. #
  216. # Options:
  217. #
  218. # * <tt>:host</tt> - Defaults to a Unix-domain socket in /tmp. On machines without Unix-domain sockets,
  219. # the default is to connect to localhost.
  220. # * <tt>:port</tt> - Defaults to 5432.
  221. # * <tt>:username</tt> - Defaults to be the same as the operating system name of the user running the application.
  222. # * <tt>:password</tt> - Password to be used if the server demands password authentication.
  223. # * <tt>:database</tt> - Defaults to be the same as the user name.
  224. # * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
  225. # as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
  226. # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
  227. # <encoding></tt> call on the connection.
  228. # * <tt>:min_messages</tt> - An optional client min messages that is used in a
  229. # <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
  230. # * <tt>:variables</tt> - An optional hash of additional parameters that
  231. # will be used in <tt>SET SESSION key = val</tt> calls on the connection.
  232. # * <tt>:insert_returning</tt> - An optional boolean to control the use or <tt>RETURNING</tt> for <tt>INSERT</tt> statements
  233. # defaults to true.
  234. #
  235. # Any further options are used as connection parameters to libpq. See
  236. # http://www.postgresql.org/docs/9.1/static/libpq-connect.html for the
  237. # list of parameters.
  238. #
  239. # In addition, default connection parameters of libpq can be set per environment variables.
  240. # See http://www.postgresql.org/docs/9.1/static/libpq-envars.html .
  241. class PostgreSQLAdapter < AbstractAdapter
  242. class ColumnDefinition < ActiveRecord::ConnectionAdapters::ColumnDefinition
  243. attr_accessor :array
  244. end
  245. module ColumnMethods
  246. def xml(*args)
  247. options = args.extract_options!
  248. column(args[0], 'xml', options)
  249. end
  250. def tsvector(*args)
  251. options = args.extract_options!
  252. column(args[0], 'tsvector', options)
  253. end
  254. def int4range(name, options = {})
  255. column(name, 'int4range', options)
  256. end
  257. def int8range(name, options = {})
  258. column(name, 'int8range', options)
  259. end
  260. def tsrange(name, options = {})
  261. column(name, 'tsrange', options)
  262. end
  263. def tstzrange(name, options = {})
  264. column(name, 'tstzrange', options)
  265. end
  266. def numrange(name, options = {})
  267. column(name, 'numrange', options)
  268. end
  269. def daterange(name, options = {})
  270. column(name, 'daterange', options)
  271. end
  272. def hstore(name, options = {})
  273. column(name, 'hstore', options)
  274. end
  275. def ltree(name, options = {})
  276. column(name, 'ltree', options)
  277. end
  278. def inet(name, options = {})
  279. column(name, 'inet', options)
  280. end
  281. def cidr(name, options = {})
  282. column(name, 'cidr', options)
  283. end
  284. def macaddr(name, options = {})
  285. column(name, 'macaddr', options)
  286. end
  287. def uuid(name, options = {})
  288. column(name, 'uuid', options)
  289. end
  290. def json(name, options = {})
  291. column(name, 'json', options)
  292. end
  293. end
  294. class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
  295. include ColumnMethods
  296. def primary_key(name, type = :primary_key, options = {})
  297. return super unless type == :uuid
  298. options[:default] ||= 'uuid_generate_v4()'
  299. options[:primary_key] = true
  300. column name, type, options
  301. end
  302. def column(name, type = nil, options = {})
  303. super
  304. column = self[name]
  305. column.array = options[:array]
  306. self
  307. end
  308. def xml(options = {})
  309. column(args[0], :text, options)
  310. end
  311. private
  312. def create_column_definition(name, type)
  313. ColumnDefinition.new name, type
  314. end
  315. end
  316. class Table < ActiveRecord::ConnectionAdapters::Table
  317. include ColumnMethods
  318. end
  319. ADAPTER_NAME = 'PostgreSQL'
  320. NATIVE_DATABASE_TYPES = {
  321. primary_key: "serial primary key",
  322. string: { name: "character varying", limit: 255 },
  323. text: { name: "text" },
  324. integer: { name: "integer" },
  325. float: { name: "float" },
  326. decimal: { name: "decimal" },
  327. datetime: { name: "timestamp" },
  328. timestamp: { name: "timestamp" },
  329. time: { name: "time" },
  330. date: { name: "date" },
  331. daterange: { name: "daterange" },
  332. numrange: { name: "numrange" },
  333. tsrange: { name: "tsrange" },
  334. tstzrange: { name: "tstzrange" },
  335. int4range: { name: "int4range" },
  336. int8range: { name: "int8range" },
  337. binary: { name: "bytea" },
  338. boolean: { name: "boolean" },
  339. xml: { name: "xml" },
  340. tsvector: { name: "tsvector" },
  341. hstore: { name: "hstore" },
  342. inet: { name: "inet" },
  343. cidr: { name: "cidr" },
  344. macaddr: { name: "macaddr" },
  345. uuid: { name: "uuid" },
  346. json: { name: "json" },
  347. ltree: { name: "ltree" }
  348. }
  349. include Quoting
  350. include ReferentialIntegrity
  351. include SchemaStatements
  352. include DatabaseStatements
  353. # Returns 'PostgreSQL' as adapter name for identification purposes.
  354. def adapter_name
  355. ADAPTER_NAME
  356. end
  357. # Adds `:array` option to the default set provided by the
  358. # AbstractAdapter
  359. def prepare_column_options(column, types)
  360. spec = super
  361. spec[:array] = 'true' if column.respond_to?(:array) && column.array
  362. spec
  363. end
  364. # Adds `:array` as a valid migration key
  365. def migration_keys
  366. super + [:array]
  367. end
  368. # Returns +true+, since this connection adapter supports prepared statement
  369. # caching.
  370. def supports_statement_cache?
  371. true
  372. end
  373. def supports_index_sort_order?
  374. true
  375. end
  376. def supports_partial_index?
  377. true
  378. end
  379. def supports_transaction_isolation?
  380. true
  381. end
  382. def index_algorithms
  383. { concurrently: 'CONCURRENTLY' }
  384. end
  385. class StatementPool < ConnectionAdapters::StatementPool
  386. def initialize(connection, max)
  387. super
  388. @counter = 0
  389. @cache = Hash.new { |h,pid| h[pid] = {} }
  390. end
  391. def each(&block); cache.each(&block); end
  392. def key?(key); cache.key?(key); end
  393. def [](key); cache[key]; end
  394. def length; cache.length; end
  395. def next_key
  396. "a#{@counter + 1}"
  397. end
  398. def []=(sql, key)
  399. while @max <= cache.size
  400. dealloc(cache.shift.last)
  401. end
  402. @counter += 1
  403. cache[sql] = key
  404. end
  405. def clear
  406. cache.each_value do |stmt_key|
  407. dealloc stmt_key
  408. end
  409. cache.clear
  410. end
  411. def delete(sql_key)
  412. dealloc cache[sql_key]
  413. cache.delete sql_key
  414. end
  415. private
  416. def cache
  417. @cache[Process.pid]
  418. end
  419. def dealloc(key)
  420. @connection.query "DEALLOCATE #{key}" if connection_active?
  421. end
  422. def connection_active?
  423. @connection.status == PGconn::CONNECTION_OK
  424. rescue PGError
  425. false
  426. end
  427. end
  428. class BindSubstitution < Arel::Visitors::PostgreSQL # :nodoc:
  429. include Arel::Visitors::BindVisitor
  430. end
  431. # Initializes and connects a PostgreSQL adapter.
  432. def initialize(connection, logger, connection_parameters, config)
  433. super(connection, logger)
  434. if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
  435. @visitor = Arel::Visitors::PostgreSQL.new self
  436. else
  437. @visitor = unprepared_visitor
  438. end
  439. @connection_parameters, @config = connection_parameters, config
  440. # @local_tz is initialized as nil to avoid warnings when connect tries to use it
  441. @local_tz = nil
  442. @table_alias_length = nil
  443. connect
  444. @statements = StatementPool.new @connection,
  445. self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 })
  446. if postgresql_version < 80200
  447. raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
  448. end
  449. initialize_type_map
  450. @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
  451. @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
  452. end
  453. # Clears the prepared statements cache.
  454. def clear_cache!
  455. @statements.clear
  456. end
  457. # Is this connection alive and ready for queries?
  458. def active?
  459. @connection.connect_poll != PG::PGRES_POLLING_FAILED
  460. rescue PGError
  461. false
  462. end
  463. # Close then reopen the connection.
  464. def reconnect!
  465. super
  466. @connection.reset
  467. configure_connection
  468. end
  469. def reset!
  470. clear_cache!
  471. super
  472. end
  473. # Disconnects from the database if already connected. Otherwise, this
  474. # method does nothing.
  475. def disconnect!
  476. super
  477. @connection.close rescue nil
  478. end
  479. def native_database_types #:nodoc:
  480. NATIVE_DATABASE_TYPES
  481. end
  482. # Returns true, since this connection adapter supports migrations.
  483. def supports_migrations?
  484. true
  485. end
  486. # Does PostgreSQL support finding primary key on non-Active Record tables?
  487. def supports_primary_key? #:nodoc:
  488. true
  489. end
  490. # Enable standard-conforming strings if available.
  491. def set_standard_conforming_strings
  492. old, self.client_min_messages = client_min_messages, 'panic'
  493. execute('SET standard_conforming_strings = on', 'SCHEMA') rescue nil
  494. ensure
  495. self.client_min_messages = old
  496. end
  497. def supports_insert_with_returning?
  498. true
  499. end
  500. def supports_ddl_transactions?
  501. true
  502. end
  503. # Returns true, since this connection adapter supports savepoints.
  504. def supports_savepoints?
  505. true
  506. end
  507. # Returns true.
  508. def supports_explain?
  509. true
  510. end
  511. # Returns true if pg > 9.2
  512. def supports_extensions?
  513. postgresql_version >= 90200
  514. end
  515. # Range datatypes weren't introduced until PostgreSQL 9.2
  516. def supports_ranges?
  517. postgresql_version >= 90200
  518. end
  519. def enable_extension(name)
  520. exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
  521. reload_type_map
  522. }
  523. end
  524. def disable_extension(name)
  525. exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
  526. reload_type_map
  527. }
  528. end
  529. def extension_enabled?(name)
  530. if supports_extensions?
  531. res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL)",
  532. 'SCHEMA'
  533. res.column_types['exists'].type_cast res.rows.first.first
  534. end
  535. end
  536. def extensions
  537. if supports_extensions?
  538. res = exec_query "SELECT extname from pg_extension", "SCHEMA"
  539. res.rows.map { |r| res.column_types['extname'].type_cast r.first }
  540. else
  541. super
  542. end
  543. end
  544. # Returns the configured supported identifier length supported by PostgreSQL
  545. def table_alias_length
  546. @table_alias_length ||= query('SHOW max_identifier_length', 'SCHEMA')[0][0].to_i
  547. end
  548. # Set the authorized user for this session
  549. def session_auth=(user)
  550. clear_cache!
  551. exec_query "SET SESSION AUTHORIZATION #{user}"
  552. end
  553. module Utils
  554. extend self
  555. # Returns an array of <tt>[schema_name, table_name]</tt> extracted from +name+.
  556. # +schema_name+ is nil if not specified in +name+.
  557. # +schema_name+ and +table_name+ exclude surrounding quotes (regardless of whether provided in +name+)
  558. # +name+ supports the range of schema/table references understood by PostgreSQL, for example:
  559. #
  560. # * <tt>table_name</tt>
  561. # * <tt>"table.name"</tt>
  562. # * <tt>schema_name.table_name</tt>
  563. # * <tt>schema_name."table.name"</tt>
  564. # * <tt>"schema.name"."table name"</tt>
  565. def extract_schema_and_table(name)
  566. table, schema = name.scan(/[^".\s]+|"[^"]*"/)[0..1].collect{|m| m.gsub(/(^"|"$)/,'') }.reverse
  567. [schema, table]
  568. end
  569. end
  570. def use_insert_returning?
  571. @use_insert_returning
  572. end
  573. def valid_type?(type)
  574. !native_database_types[type].nil?
  575. end
  576. protected
  577. # Returns the version of the connected PostgreSQL server.
  578. def postgresql_version
  579. @connection.server_version
  580. end
  581. # See http://www.postgresql.org/docs/9.1/static/errcodes-appendix.html
  582. FOREIGN_KEY_VIOLATION = "23503"
  583. UNIQUE_VIOLATION = "23505"
  584. def translate_exception(exception, message)
  585. return exception unless exception.respond_to?(:result)
  586. case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE)
  587. when UNIQUE_VIOLATION
  588. RecordNotUnique.new(message, exception)
  589. when FOREIGN_KEY_VIOLATION
  590. InvalidForeignKey.new(message, exception)
  591. else
  592. super
  593. end
  594. end
  595. private
  596. def reload_type_map
  597. OID::TYPE_MAP.clear
  598. initialize_type_map
  599. end
  600. def initialize_type_map
  601. result = execute('SELECT oid, typname, typelem, typdelim, typinput FROM pg_type', 'SCHEMA')
  602. leaves, nodes = result.partition { |row| row['typelem'] == '0' }
  603. # populate the leaf nodes
  604. leaves.find_all { |row| OID.registered_type? row['typname'] }.each do |row|
  605. OID::TYPE_MAP[row['oid'].to_i] = OID::NAMES[row['typname']]
  606. end
  607. arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' }
  608. # populate composite types
  609. nodes.find_all { |row| OID::TYPE_MAP.key? row['typelem'].to_i }.each do |row|
  610. if OID.registered_type? row['typname']
  611. # this composite type is explicitly registered
  612. vector = OID::NAMES[row['typname']]
  613. else
  614. # use the default for composite types
  615. vector = OID::Vector.new row['typdelim'], OID::TYPE_MAP[row['typelem'].to_i]
  616. end
  617. OID::TYPE_MAP[row['oid'].to_i] = vector
  618. end
  619. # populate array types
  620. arrays.find_all { |row| OID::TYPE_MAP.key? row['typelem'].to_i }.each do |row|
  621. array = OID::Array.new OID::TYPE_MAP[row['typelem'].to_i]
  622. OID::TYPE_MAP[row['oid'].to_i] = array
  623. end
  624. end
  625. FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
  626. def exec_no_cache(sql, binds)
  627. @connection.async_exec(sql)
  628. end
  629. def exec_cache(sql, binds)
  630. stmt_key = prepare_statement sql
  631. # Clear the queue
  632. @connection.get_last_result
  633. @connection.send_query_prepared(stmt_key, binds.map { |col, val|
  634. type_cast(val, col)
  635. })
  636. @connection.block
  637. @connection.get_last_result
  638. rescue PGError => e
  639. # Get the PG code for the failure. Annoyingly, the code for
  640. # prepared statements whose return value may have changed is
  641. # FEATURE_NOT_SUPPORTED. Check here for more details:
  642. # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
  643. begin
  644. code = e.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
  645. rescue
  646. raise e
  647. end
  648. if FEATURE_NOT_SUPPORTED == code
  649. @statements.delete sql_key(sql)
  650. retry
  651. else
  652. raise e
  653. end
  654. end
  655. # Returns the statement identifier for the client side cache
  656. # of statements
  657. def sql_key(sql)
  658. "#{schema_search_path}-#{sql}"
  659. end
  660. # Prepare the statement if it hasn't been prepared, return
  661. # the statement key.
  662. def prepare_statement(sql)
  663. sql_key = sql_key(sql)
  664. unless @statements.key? sql_key
  665. nextkey = @statements.next_key
  666. @connection.prepare nextkey, sql
  667. @statements[sql_key] = nextkey
  668. end
  669. @statements[sql_key]
  670. end
  671. # The internal PostgreSQL identifier of the money data type.
  672. MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
  673. # The internal PostgreSQL identifier of the BYTEA data type.
  674. BYTEA_COLUMN_TYPE_OID = 17 #:nodoc:
  675. # Connects to a PostgreSQL server and sets up the adapter depending on the
  676. # connected server's characteristics.
  677. def connect
  678. @connection = PGconn.connect(@connection_parameters)
  679. # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
  680. # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
  681. # should know about this but can't detect it there, so deal with it here.
  682. PostgreSQLColumn.money_precision = (postgresql_version >= 80300) ? 19 : 10
  683. configure_connection
  684. end
  685. # Configures the encoding, verbosity, schema search path, and time zone of the connection.
  686. # This is called by #connect and should not be called manually.
  687. def configure_connection
  688. if @config[:encoding]
  689. @connection.set_client_encoding(@config[:encoding])
  690. end
  691. self.client_min_messages = @config[:min_messages] || 'warning'
  692. self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
  693. # Use standard-conforming strings if available so we don't have to do the E'...' dance.
  694. set_standard_conforming_strings
  695. # If using Active Record's time zone support configure the connection to return
  696. # TIMESTAMP WITH ZONE types in UTC.
  697. # (SET TIME ZONE does not use an equals sign like other SET variables)
  698. if ActiveRecord::Base.default_timezone == :utc
  699. execute("SET time zone 'UTC'", 'SCHEMA')
  700. elsif @local_tz
  701. execute("SET time zone '#{@local_tz}'", 'SCHEMA')
  702. end
  703. # SET statements from :variables config hash
  704. # http://www.postgresql.org/docs/8.3/static/sql-set.html
  705. variables = @config[:variables] || {}
  706. variables.map do |k, v|
  707. if v == ':default' || v == :default
  708. # Sets the value to the global or compile default
  709. execute("SET SESSION #{k.to_s} TO DEFAULT", 'SCHEMA')
  710. elsif !v.nil?
  711. execute("SET SESSION #{k.to_s} TO #{quote(v)}", 'SCHEMA')
  712. end
  713. end
  714. end
  715. # Returns the current ID of a table's sequence.
  716. def last_insert_id(sequence_name) #:nodoc:
  717. Integer(last_insert_id_value(sequence_name))
  718. end
  719. def last_insert_id_value(sequence_name)
  720. last_insert_id_result(sequence_name).rows.first.first
  721. end
  722. def last_insert_id_result(sequence_name) #:nodoc:
  723. exec_query("SELECT currval('#{sequence_name}')", 'SQL')
  724. end
  725. # Executes a SELECT query and returns the results, performing any data type
  726. # conversions that are required to be performed here instead of in PostgreSQLColumn.
  727. def select(sql, name = nil, binds = [])
  728. exec_query(sql, name, binds)
  729. end
  730. def select_raw(sql, name = nil)
  731. res = execute(sql, name)
  732. results = result_as_array(res)
  733. fields = res.fields
  734. res.clear
  735. return fields, results
  736. end
  737. # Returns the list of a table's column names, data types, and default values.
  738. #
  739. # The underlying query is roughly:
  740. # SELECT column.name, column.type, default.value
  741. # FROM column LEFT JOIN default
  742. # ON column.table_id = default.table_id
  743. # AND column.num = default.column_num
  744. # WHERE column.table_id = get_table_id('table_name')
  745. # AND column.num > 0
  746. # AND NOT column.is_dropped
  747. # ORDER BY column.num
  748. #
  749. # If the table name is not prefixed with a schema, the database will
  750. # take the first match from the schema search path.
  751. #
  752. # Query implementation notes:
  753. # - format_type includes the column size constraint, e.g. varchar(50)
  754. # - ::regclass is a function that gives the id for a table name
  755. def column_definitions(table_name) #:nodoc:
  756. exec_query(<<-end_sql, 'SCHEMA').rows
  757. SELECT a.attname, format_type(a.atttypid, a.atttypmod),
  758. pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
  759. FROM pg_attribute a LEFT JOIN pg_attrdef d
  760. ON a.attrelid = d.adrelid AND a.attnum = d.adnum
  761. WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
  762. AND a.attnum > 0 AND NOT a.attisdropped
  763. ORDER BY a.attnum
  764. end_sql
  765. end
  766. def extract_pg_identifier_from_name(name)
  767. match_data = name.start_with?('"') ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
  768. if match_data
  769. rest = name[match_data[0].length, name.length]
  770. rest = rest[1, rest.length] if rest.start_with? "."
  771. [match_data[1], (rest.length > 0 ? rest : nil)]
  772. end
  773. end
  774. def extract_table_ref_from_insert_sql(sql)
  775. sql[/into\s+([^\(]*).*values\s*\(/i]
  776. $1.strip if $1
  777. end
  778. def create_table_definition(name, temporary, options)
  779. TableDefinition.new native_database_types, name, temporary, options
  780. end
  781. def update_table_definition(table_name, base)
  782. Table.new(table_name, base)
  783. end
  784. end
  785. end
  786. end