PageRenderTime 25ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 1ms

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

https://bitbucket.org/deeparaman/url_shortner
Ruby | 754 lines | 535 code | 112 blank | 107 comment | 33 complexity | aa4cc54d037c661c3fcff9a38a886f80 MD5 | raw file
Possible License(s): MIT, Unlicense, BSD-3-Clause, 0BSD, Apache-2.0
  1. require 'active_record/connection_adapters/abstract_adapter'
  2. require 'active_record/connection_adapters/statement_pool'
  3. require 'active_record/connection_adapters/postgresql/utils'
  4. require 'active_record/connection_adapters/postgresql/column'
  5. require 'active_record/connection_adapters/postgresql/oid'
  6. require 'active_record/connection_adapters/postgresql/quoting'
  7. require 'active_record/connection_adapters/postgresql/referential_integrity'
  8. require 'active_record/connection_adapters/postgresql/schema_definitions'
  9. require 'active_record/connection_adapters/postgresql/schema_statements'
  10. require 'active_record/connection_adapters/postgresql/database_statements'
  11. require 'arel/visitors/bind_visitor'
  12. # Make sure we're using pg high enough for PGResult#values
  13. gem 'pg', '~> 0.15'
  14. require 'pg'
  15. require 'ipaddr'
  16. module ActiveRecord
  17. module ConnectionHandling # :nodoc:
  18. VALID_CONN_PARAMS = [:host, :hostaddr, :port, :dbname, :user, :password, :connect_timeout,
  19. :client_encoding, :options, :application_name, :fallback_application_name,
  20. :keepalives, :keepalives_idle, :keepalives_interval, :keepalives_count,
  21. :tty, :sslmode, :requiressl, :sslcompression, :sslcert, :sslkey,
  22. :sslrootcert, :sslcrl, :requirepeer, :krbsrvname, :gsslib, :service]
  23. # Establishes a connection to the database that's used by all Active Record objects
  24. def postgresql_connection(config)
  25. conn_params = config.symbolize_keys
  26. conn_params.delete_if { |_, v| v.nil? }
  27. # Map ActiveRecords param names to PGs.
  28. conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
  29. conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
  30. # Forward only valid config params to PGconn.connect.
  31. conn_params.keep_if { |k, _| VALID_CONN_PARAMS.include?(k) }
  32. # The postgres drivers don't allow the creation of an unconnected PGconn object,
  33. # so just pass a nil connection object for the time being.
  34. ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, conn_params, config)
  35. end
  36. end
  37. module ConnectionAdapters
  38. # The PostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
  39. #
  40. # Options:
  41. #
  42. # * <tt>:host</tt> - Defaults to a Unix-domain socket in /tmp. On machines without Unix-domain sockets,
  43. # the default is to connect to localhost.
  44. # * <tt>:port</tt> - Defaults to 5432.
  45. # * <tt>:username</tt> - Defaults to be the same as the operating system name of the user running the application.
  46. # * <tt>:password</tt> - Password to be used if the server demands password authentication.
  47. # * <tt>:database</tt> - Defaults to be the same as the user name.
  48. # * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
  49. # as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
  50. # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
  51. # <encoding></tt> call on the connection.
  52. # * <tt>:min_messages</tt> - An optional client min messages that is used in a
  53. # <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
  54. # * <tt>:variables</tt> - An optional hash of additional parameters that
  55. # will be used in <tt>SET SESSION key = val</tt> calls on the connection.
  56. # * <tt>:insert_returning</tt> - An optional boolean to control the use of <tt>RETURNING</tt> for <tt>INSERT</tt> statements
  57. # defaults to true.
  58. #
  59. # Any further options are used as connection parameters to libpq. See
  60. # http://www.postgresql.org/docs/9.1/static/libpq-connect.html for the
  61. # list of parameters.
  62. #
  63. # In addition, default connection parameters of libpq can be set per environment variables.
  64. # See http://www.postgresql.org/docs/9.1/static/libpq-envars.html .
  65. class PostgreSQLAdapter < AbstractAdapter
  66. ADAPTER_NAME = 'PostgreSQL'.freeze
  67. NATIVE_DATABASE_TYPES = {
  68. primary_key: "serial primary key",
  69. bigserial: "bigserial",
  70. string: { name: "character varying" },
  71. text: { name: "text" },
  72. integer: { name: "integer" },
  73. float: { name: "float" },
  74. decimal: { name: "decimal" },
  75. datetime: { name: "timestamp" },
  76. time: { name: "time" },
  77. date: { name: "date" },
  78. daterange: { name: "daterange" },
  79. numrange: { name: "numrange" },
  80. tsrange: { name: "tsrange" },
  81. tstzrange: { name: "tstzrange" },
  82. int4range: { name: "int4range" },
  83. int8range: { name: "int8range" },
  84. binary: { name: "bytea" },
  85. boolean: { name: "boolean" },
  86. bigint: { name: "bigint" },
  87. xml: { name: "xml" },
  88. tsvector: { name: "tsvector" },
  89. hstore: { name: "hstore" },
  90. inet: { name: "inet" },
  91. cidr: { name: "cidr" },
  92. macaddr: { name: "macaddr" },
  93. uuid: { name: "uuid" },
  94. json: { name: "json" },
  95. jsonb: { name: "jsonb" },
  96. ltree: { name: "ltree" },
  97. citext: { name: "citext" },
  98. point: { name: "point" },
  99. bit: { name: "bit" },
  100. bit_varying: { name: "bit varying" },
  101. money: { name: "money" },
  102. }
  103. OID = PostgreSQL::OID #:nodoc:
  104. include PostgreSQL::Quoting
  105. include PostgreSQL::ReferentialIntegrity
  106. include PostgreSQL::SchemaStatements
  107. include PostgreSQL::DatabaseStatements
  108. include Savepoints
  109. def schema_creation # :nodoc:
  110. PostgreSQL::SchemaCreation.new self
  111. end
  112. # Adds +:array+ option to the default set provided by the
  113. # AbstractAdapter
  114. def prepare_column_options(column, types) # :nodoc:
  115. spec = super
  116. spec[:array] = 'true' if column.respond_to?(:array) && column.array
  117. spec[:default] = "\"#{column.default_function}\"" if column.default_function
  118. spec
  119. end
  120. # Adds +:array+ as a valid migration key
  121. def migration_keys
  122. super + [:array]
  123. end
  124. # Returns +true+, since this connection adapter supports prepared statement
  125. # caching.
  126. def supports_statement_cache?
  127. true
  128. end
  129. def supports_index_sort_order?
  130. true
  131. end
  132. def supports_partial_index?
  133. true
  134. end
  135. def supports_transaction_isolation?
  136. true
  137. end
  138. def supports_foreign_keys?
  139. true
  140. end
  141. def supports_views?
  142. true
  143. end
  144. def index_algorithms
  145. { concurrently: 'CONCURRENTLY' }
  146. end
  147. class StatementPool < ConnectionAdapters::StatementPool
  148. def initialize(connection, max)
  149. super
  150. @counter = 0
  151. @cache = Hash.new { |h,pid| h[pid] = {} }
  152. end
  153. def each(&block); cache.each(&block); end
  154. def key?(key); cache.key?(key); end
  155. def [](key); cache[key]; end
  156. def length; cache.length; end
  157. def next_key
  158. "a#{@counter + 1}"
  159. end
  160. def []=(sql, key)
  161. while @max <= cache.size
  162. dealloc(cache.shift.last)
  163. end
  164. @counter += 1
  165. cache[sql] = key
  166. end
  167. def clear
  168. cache.each_value do |stmt_key|
  169. dealloc stmt_key
  170. end
  171. cache.clear
  172. end
  173. def delete(sql_key)
  174. dealloc cache[sql_key]
  175. cache.delete sql_key
  176. end
  177. private
  178. def cache
  179. @cache[Process.pid]
  180. end
  181. def dealloc(key)
  182. @connection.query "DEALLOCATE #{key}" if connection_active?
  183. end
  184. def connection_active?
  185. @connection.status == PGconn::CONNECTION_OK
  186. rescue PGError
  187. false
  188. end
  189. end
  190. # Initializes and connects a PostgreSQL adapter.
  191. def initialize(connection, logger, connection_parameters, config)
  192. super(connection, logger)
  193. @visitor = Arel::Visitors::PostgreSQL.new self
  194. if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
  195. @prepared_statements = true
  196. else
  197. @prepared_statements = false
  198. end
  199. @connection_parameters, @config = connection_parameters, config
  200. # @local_tz is initialized as nil to avoid warnings when connect tries to use it
  201. @local_tz = nil
  202. @table_alias_length = nil
  203. connect
  204. @statements = StatementPool.new @connection,
  205. self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 })
  206. if postgresql_version < 80200
  207. raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
  208. end
  209. @type_map = Type::HashLookupTypeMap.new
  210. initialize_type_map(type_map)
  211. @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
  212. @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
  213. end
  214. # Clears the prepared statements cache.
  215. def clear_cache!
  216. @statements.clear
  217. end
  218. def truncate(table_name, name = nil)
  219. exec_query "TRUNCATE TABLE #{quote_table_name(table_name)}", name, []
  220. end
  221. # Is this connection alive and ready for queries?
  222. def active?
  223. @connection.query 'SELECT 1'
  224. true
  225. rescue PGError
  226. false
  227. end
  228. # Close then reopen the connection.
  229. def reconnect!
  230. super
  231. @connection.reset
  232. configure_connection
  233. end
  234. def reset!
  235. clear_cache!
  236. reset_transaction
  237. unless @connection.transaction_status == ::PG::PQTRANS_IDLE
  238. @connection.query 'ROLLBACK'
  239. end
  240. @connection.query 'DISCARD ALL'
  241. configure_connection
  242. end
  243. # Disconnects from the database if already connected. Otherwise, this
  244. # method does nothing.
  245. def disconnect!
  246. super
  247. @connection.close rescue nil
  248. end
  249. def native_database_types #:nodoc:
  250. NATIVE_DATABASE_TYPES
  251. end
  252. # Returns true, since this connection adapter supports migrations.
  253. def supports_migrations?
  254. true
  255. end
  256. # Does PostgreSQL support finding primary key on non-Active Record tables?
  257. def supports_primary_key? #:nodoc:
  258. true
  259. end
  260. def set_standard_conforming_strings
  261. execute('SET standard_conforming_strings = on', 'SCHEMA')
  262. end
  263. def supports_ddl_transactions?
  264. true
  265. end
  266. def supports_explain?
  267. true
  268. end
  269. # Returns true if pg > 9.1
  270. def supports_extensions?
  271. postgresql_version >= 90100
  272. end
  273. # Range datatypes weren't introduced until PostgreSQL 9.2
  274. def supports_ranges?
  275. postgresql_version >= 90200
  276. end
  277. def supports_materialized_views?
  278. postgresql_version >= 90300
  279. end
  280. def enable_extension(name)
  281. exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
  282. reload_type_map
  283. }
  284. end
  285. def disable_extension(name)
  286. exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
  287. reload_type_map
  288. }
  289. end
  290. def extension_enabled?(name)
  291. if supports_extensions?
  292. res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled",
  293. 'SCHEMA'
  294. res.cast_values.first
  295. end
  296. end
  297. def extensions
  298. if supports_extensions?
  299. exec_query("SELECT extname from pg_extension", "SCHEMA").cast_values
  300. else
  301. super
  302. end
  303. end
  304. # Returns the configured supported identifier length supported by PostgreSQL
  305. def table_alias_length
  306. @table_alias_length ||= query('SHOW max_identifier_length', 'SCHEMA')[0][0].to_i
  307. end
  308. # Set the authorized user for this session
  309. def session_auth=(user)
  310. clear_cache!
  311. exec_query "SET SESSION AUTHORIZATION #{user}"
  312. end
  313. def use_insert_returning?
  314. @use_insert_returning
  315. end
  316. def valid_type?(type)
  317. !native_database_types[type].nil?
  318. end
  319. def update_table_definition(table_name, base) #:nodoc:
  320. PostgreSQL::Table.new(table_name, base)
  321. end
  322. def lookup_cast_type(sql_type) # :nodoc:
  323. oid = execute("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").first['oid'].to_i
  324. super(oid)
  325. end
  326. def column_name_for_operation(operation, node) # :nodoc:
  327. OPERATION_ALIASES.fetch(operation) { operation.downcase }
  328. end
  329. OPERATION_ALIASES = { # :nodoc:
  330. "maximum" => "max",
  331. "minimum" => "min",
  332. "average" => "avg",
  333. }
  334. protected
  335. # Returns the version of the connected PostgreSQL server.
  336. def postgresql_version
  337. @connection.server_version
  338. end
  339. # See http://www.postgresql.org/docs/9.1/static/errcodes-appendix.html
  340. FOREIGN_KEY_VIOLATION = "23503"
  341. UNIQUE_VIOLATION = "23505"
  342. def translate_exception(exception, message)
  343. return exception unless exception.respond_to?(:result)
  344. case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE)
  345. when UNIQUE_VIOLATION
  346. RecordNotUnique.new(message, exception)
  347. when FOREIGN_KEY_VIOLATION
  348. InvalidForeignKey.new(message, exception)
  349. else
  350. super
  351. end
  352. end
  353. private
  354. def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
  355. if !type_map.key?(oid)
  356. load_additional_types(type_map, [oid])
  357. end
  358. type_map.fetch(oid, fmod, sql_type) {
  359. warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
  360. Type::Value.new.tap do |cast_type|
  361. type_map.register_type(oid, cast_type)
  362. end
  363. }
  364. end
  365. def initialize_type_map(m) # :nodoc:
  366. register_class_with_limit m, 'int2', OID::Integer
  367. register_class_with_limit m, 'int4', OID::Integer
  368. register_class_with_limit m, 'int8', OID::Integer
  369. m.alias_type 'oid', 'int2'
  370. m.register_type 'float4', OID::Float.new
  371. m.alias_type 'float8', 'float4'
  372. m.register_type 'text', Type::Text.new
  373. register_class_with_limit m, 'varchar', Type::String
  374. m.alias_type 'char', 'varchar'
  375. m.alias_type 'name', 'varchar'
  376. m.alias_type 'bpchar', 'varchar'
  377. m.register_type 'bool', Type::Boolean.new
  378. register_class_with_limit m, 'bit', OID::Bit
  379. register_class_with_limit m, 'varbit', OID::BitVarying
  380. m.alias_type 'timestamptz', 'timestamp'
  381. m.register_type 'date', OID::Date.new
  382. m.register_type 'time', OID::Time.new
  383. m.register_type 'money', OID::Money.new
  384. m.register_type 'bytea', OID::Bytea.new
  385. m.register_type 'point', OID::Point.new
  386. m.register_type 'hstore', OID::Hstore.new
  387. m.register_type 'json', OID::Json.new
  388. m.register_type 'jsonb', OID::Jsonb.new
  389. m.register_type 'cidr', OID::Cidr.new
  390. m.register_type 'inet', OID::Inet.new
  391. m.register_type 'uuid', OID::Uuid.new
  392. m.register_type 'xml', OID::Xml.new
  393. m.register_type 'tsvector', OID::SpecializedString.new(:tsvector)
  394. m.register_type 'macaddr', OID::SpecializedString.new(:macaddr)
  395. m.register_type 'citext', OID::SpecializedString.new(:citext)
  396. m.register_type 'ltree', OID::SpecializedString.new(:ltree)
  397. # FIXME: why are we keeping these types as strings?
  398. m.alias_type 'interval', 'varchar'
  399. m.alias_type 'path', 'varchar'
  400. m.alias_type 'line', 'varchar'
  401. m.alias_type 'polygon', 'varchar'
  402. m.alias_type 'circle', 'varchar'
  403. m.alias_type 'lseg', 'varchar'
  404. m.alias_type 'box', 'varchar'
  405. m.register_type 'timestamp' do |_, _, sql_type|
  406. precision = extract_precision(sql_type)
  407. OID::DateTime.new(precision: precision)
  408. end
  409. m.register_type 'numeric' do |_, fmod, sql_type|
  410. precision = extract_precision(sql_type)
  411. scale = extract_scale(sql_type)
  412. # The type for the numeric depends on the width of the field,
  413. # so we'll do something special here.
  414. #
  415. # When dealing with decimal columns:
  416. #
  417. # places after decimal = fmod - 4 & 0xffff
  418. # places before decimal = (fmod - 4) >> 16 & 0xffff
  419. if fmod && (fmod - 4 & 0xffff).zero?
  420. # FIXME: Remove this class, and the second argument to
  421. # lookups on PG
  422. Type::DecimalWithoutScale.new(precision: precision)
  423. else
  424. OID::Decimal.new(precision: precision, scale: scale)
  425. end
  426. end
  427. load_additional_types(m)
  428. end
  429. def extract_limit(sql_type) # :nodoc:
  430. case sql_type
  431. when /^bigint/i, /^int8/i
  432. 8
  433. when /^smallint/i
  434. 2
  435. else
  436. super
  437. end
  438. end
  439. # Extracts the value from a PostgreSQL column default definition.
  440. def extract_value_from_default(oid, default) # :nodoc:
  441. case default
  442. # Quoted types
  443. when /\A[\(B]?'(.*)'::/m
  444. $1.gsub(/''/, "'")
  445. # Boolean types
  446. when 'true', 'false'
  447. default
  448. # Numeric types
  449. when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
  450. $1
  451. # Object identifier types
  452. when /\A-?\d+\z/
  453. $1
  454. else
  455. # Anything else is blank, some user type, or some function
  456. # and we can't know the value of that, so return nil.
  457. nil
  458. end
  459. end
  460. def extract_default_function(default_value, default) # :nodoc:
  461. default if has_default_function?(default_value, default)
  462. end
  463. def has_default_function?(default_value, default) # :nodoc:
  464. !default_value && (%r{\w+\(.*\)} === default)
  465. end
  466. def load_additional_types(type_map, oids = nil) # :nodoc:
  467. initializer = OID::TypeMapInitializer.new(type_map)
  468. if supports_ranges?
  469. query = <<-SQL
  470. SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
  471. FROM pg_type as t
  472. LEFT JOIN pg_range as r ON oid = rngtypid
  473. SQL
  474. else
  475. query = <<-SQL
  476. SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
  477. FROM pg_type as t
  478. SQL
  479. end
  480. if oids
  481. query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
  482. else
  483. query += initializer.query_conditions_for_initial_load(type_map)
  484. end
  485. execute_and_clear(query, 'SCHEMA', []) do |records|
  486. initializer.run(records)
  487. end
  488. end
  489. FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
  490. def execute_and_clear(sql, name, binds)
  491. result = without_prepared_statement?(binds) ? exec_no_cache(sql, name, binds) :
  492. exec_cache(sql, name, binds)
  493. ret = yield result
  494. result.clear
  495. ret
  496. end
  497. def exec_no_cache(sql, name, binds)
  498. log(sql, name, binds) { @connection.async_exec(sql, []) }
  499. end
  500. def exec_cache(sql, name, binds)
  501. stmt_key = prepare_statement(sql)
  502. type_casted_binds = binds.map { |col, val|
  503. [col, type_cast(val, col)]
  504. }
  505. log(sql, name, type_casted_binds, stmt_key) do
  506. @connection.exec_prepared(stmt_key, type_casted_binds.map { |_, val| val })
  507. end
  508. rescue ActiveRecord::StatementInvalid => e
  509. pgerror = e.original_exception
  510. # Get the PG code for the failure. Annoyingly, the code for
  511. # prepared statements whose return value may have changed is
  512. # FEATURE_NOT_SUPPORTED. Check here for more details:
  513. # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
  514. begin
  515. code = pgerror.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
  516. rescue
  517. raise e
  518. end
  519. if FEATURE_NOT_SUPPORTED == code
  520. @statements.delete sql_key(sql)
  521. retry
  522. else
  523. raise e
  524. end
  525. end
  526. # Returns the statement identifier for the client side cache
  527. # of statements
  528. def sql_key(sql)
  529. "#{schema_search_path}-#{sql}"
  530. end
  531. # Prepare the statement if it hasn't been prepared, return
  532. # the statement key.
  533. def prepare_statement(sql)
  534. sql_key = sql_key(sql)
  535. unless @statements.key? sql_key
  536. nextkey = @statements.next_key
  537. begin
  538. @connection.prepare nextkey, sql
  539. rescue => e
  540. raise translate_exception_class(e, sql)
  541. end
  542. # Clear the queue
  543. @connection.get_last_result
  544. @statements[sql_key] = nextkey
  545. end
  546. @statements[sql_key]
  547. end
  548. # Connects to a PostgreSQL server and sets up the adapter depending on the
  549. # connected server's characteristics.
  550. def connect
  551. @connection = PGconn.connect(@connection_parameters)
  552. # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
  553. # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
  554. # should know about this but can't detect it there, so deal with it here.
  555. OID::Money.precision = (postgresql_version >= 80300) ? 19 : 10
  556. configure_connection
  557. rescue ::PG::Error => error
  558. if error.message.include?("does not exist")
  559. raise ActiveRecord::NoDatabaseError.new(error.message, error)
  560. else
  561. raise
  562. end
  563. end
  564. # Configures the encoding, verbosity, schema search path, and time zone of the connection.
  565. # This is called by #connect and should not be called manually.
  566. def configure_connection
  567. if @config[:encoding]
  568. @connection.set_client_encoding(@config[:encoding])
  569. end
  570. self.client_min_messages = @config[:min_messages] || 'warning'
  571. self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
  572. # Use standard-conforming strings so we don't have to do the E'...' dance.
  573. set_standard_conforming_strings
  574. # If using Active Record's time zone support configure the connection to return
  575. # TIMESTAMP WITH ZONE types in UTC.
  576. # (SET TIME ZONE does not use an equals sign like other SET variables)
  577. if ActiveRecord::Base.default_timezone == :utc
  578. execute("SET time zone 'UTC'", 'SCHEMA')
  579. elsif @local_tz
  580. execute("SET time zone '#{@local_tz}'", 'SCHEMA')
  581. end
  582. # SET statements from :variables config hash
  583. # http://www.postgresql.org/docs/8.3/static/sql-set.html
  584. variables = @config[:variables] || {}
  585. variables.map do |k, v|
  586. if v == ':default' || v == :default
  587. # Sets the value to the global or compile default
  588. execute("SET SESSION #{k} TO DEFAULT", 'SCHEMA')
  589. elsif !v.nil?
  590. execute("SET SESSION #{k} TO #{quote(v)}", 'SCHEMA')
  591. end
  592. end
  593. end
  594. # Returns the current ID of a table's sequence.
  595. def last_insert_id(sequence_name) #:nodoc:
  596. Integer(last_insert_id_value(sequence_name))
  597. end
  598. def last_insert_id_value(sequence_name)
  599. last_insert_id_result(sequence_name).rows.first.first
  600. end
  601. def last_insert_id_result(sequence_name) #:nodoc:
  602. exec_query("SELECT currval('#{sequence_name}')", 'SQL')
  603. end
  604. # Returns the list of a table's column names, data types, and default values.
  605. #
  606. # The underlying query is roughly:
  607. # SELECT column.name, column.type, default.value
  608. # FROM column LEFT JOIN default
  609. # ON column.table_id = default.table_id
  610. # AND column.num = default.column_num
  611. # WHERE column.table_id = get_table_id('table_name')
  612. # AND column.num > 0
  613. # AND NOT column.is_dropped
  614. # ORDER BY column.num
  615. #
  616. # If the table name is not prefixed with a schema, the database will
  617. # take the first match from the schema search path.
  618. #
  619. # Query implementation notes:
  620. # - format_type includes the column size constraint, e.g. varchar(50)
  621. # - ::regclass is a function that gives the id for a table name
  622. def column_definitions(table_name) # :nodoc:
  623. exec_query(<<-end_sql, 'SCHEMA').rows
  624. SELECT a.attname, format_type(a.atttypid, a.atttypmod),
  625. pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
  626. FROM pg_attribute a LEFT JOIN pg_attrdef d
  627. ON a.attrelid = d.adrelid AND a.attnum = d.adnum
  628. WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
  629. AND a.attnum > 0 AND NOT a.attisdropped
  630. ORDER BY a.attnum
  631. end_sql
  632. end
  633. def extract_table_ref_from_insert_sql(sql) # :nodoc:
  634. sql[/into\s+([^\(]*).*values\s*\(/im]
  635. $1.strip if $1
  636. end
  637. def create_table_definition(name, temporary, options, as = nil) # :nodoc:
  638. PostgreSQL::TableDefinition.new native_database_types, name, temporary, options, as
  639. end
  640. end
  641. end
  642. end