1 require 'active_record/connection_adapters/abstract_adapter'
4 require_library_or_gem
'pg'
7 require_library_or_gem
'postgres'
9 alias_method
:nfields, :num_fields unless self.method_defined
?(:nfields)
10 alias_method
:ntuples, :num_tuples unless self.method_defined
?(:ntuples)
11 alias_method
:ftype, :type unless self.method_defined
?(:ftype)
12 alias_method
:cmd_tuples, :cmdtuples unless self.method_defined
?(:cmd_tuples)
21 # Establishes a connection to the database that's used by all Active Record objects
22 def self.postgresql_connection(config
) # :nodoc:
23 config
= config
.symbolize_keys
25 port
= config
[:port] || 5432
26 username
= config
[:username].to_s
if config
[:username]
27 password
= config
[:password].to_s
if config
[:password]
29 if config
.has_key
?(:database)
30 database
= config
[:database]
32 raise ArgumentError
, "No database specified. Missing argument: database."
35 # The postgres drivers don't allow the creation of an unconnected PGconn object,
36 # so just pass a nil connection object for the time being.
37 ConnectionAdapters
::PostgreSQLAdapter.new(nil, logger
, [host
, port
, nil, nil, database
, username
, password
], config
)
41 module ConnectionAdapters
42 # PostgreSQL-specific extensions to column definitions in a table.
43 class PostgreSQLColumn
< Column
#:nodoc:
44 # Instantiates a new PostgreSQL column definition in a table.
45 def initialize(name
, default
, sql_type
= nil, null
= true)
46 super(name
, self.class.extract_value_from_default(default
), sql_type
, null
)
50 def extract_limit(sql_type
)
58 # Extracts the scale from PostgreSQL-specific data types.
59 def extract_scale(sql_type
)
60 # Money type has a fixed scale of 2.
61 sql_type
=~
/^money/ ? 2 : super
64 # Extracts the precision from PostgreSQL-specific data types.
65 def extract_precision(sql_type
)
66 # Actual code is defined dynamically in PostgreSQLAdapter.connect
67 # depending on the server specifics
71 # Maps PostgreSQL-specific data types to logical Rails types.
72 def simplified_type(field_type
)
74 # Numeric and monetary types
75 when /^(?:real|double precision)$/
81 when /^(?:character varying|bpchar)(?:\(\d+\))?$/
87 when /^timestamp with(?:out)? time zone$/
92 when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/
94 # Network address types
95 when /^(?:cidr|inet|macaddr)$/
98 when /^bit(?: varying)?(?:\(\d+\))?$/
106 # Object identifier types
109 # Pass through all types that are not specific to PostgreSQL.
115 # Extracts the value from a PostgreSQL column default definition.
116 def self.extract_value_from_default(default
)
119 when /\A\(?(-?\d+(\.\d*)?\)?)\z/
122 when /\A'(.*)'::(?:character varying|bpchar|text)\z/m
124 # Character types (8.1 formatting)
125 when /\AE'(.*)'::(?:character varying|bpchar|text)\z/m
126 $1.gsub(/\\(\d\d\d)/) { $1.oct
.chr
}
128 when /\A'(.*)'::bytea\z/m
131 when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
133 when /\A'(.*)'::interval\z/
141 when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
143 # Network address types
144 when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
147 when /\AB'(.*)'::"?bit(?: varying)?"?\z/
150 when /\A'(.*)'::xml\z/m
153 when /\A'(.*)'::"?\D+"?\[\]\z/
155 # Object identifier types
159 # Anything else is blank, some user type, or some function
160 # and we can't know the value of that, so return nil.
167 module ConnectionAdapters
168 # The PostgreSQL adapter works both with the native C (http://ruby.scripting.ca/postgres/) and the pure
169 # Ruby (available both as gem and from http://rubyforge.org/frs/?group_id=234&release_id=1944) drivers.
173 # * <tt>:host</tt> - Defaults to "localhost".
174 # * <tt>:port</tt> - Defaults to 5432.
175 # * <tt>:username</tt> - Defaults to nothing.
176 # * <tt>:password</tt> - Defaults to nothing.
177 # * <tt>:database</tt> - The name of the database. No default, must be provided.
178 # * <tt>:schema_search_path</tt> - An optional schema search path for the connection given as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
179 # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO <encoding></tt> call on the connection.
180 # * <tt>:min_messages</tt> - An optional client min messages that is used in a <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
181 # * <tt>:allow_concurrency</tt> - If true, use async query methods so Ruby threads don't deadlock; otherwise, use blocking query methods.
182 class PostgreSQLAdapter
< AbstractAdapter
183 ADAPTER_NAME
= 'PostgreSQL'.freeze
185 NATIVE_DATABASE_TYPES
= {
186 :primary_key => "serial primary key".freeze
,
187 :string => { :name => "character varying", :limit => 255 },
188 :text => { :name => "text" },
189 :integer => { :name => "integer" },
190 :float => { :name => "float" },
191 :decimal => { :name => "decimal" },
192 :datetime => { :name => "timestamp" },
193 :timestamp => { :name => "timestamp" },
194 :time => { :name => "time" },
195 :date => { :name => "date" },
196 :binary => { :name => "bytea" },
197 :boolean => { :name => "boolean" }
200 # Returns 'PostgreSQL' as adapter name for identification purposes.
205 # Initializes and connects a PostgreSQL adapter.
206 def initialize(connection
, logger
, connection_parameters
, config
)
207 super(connection
, logger
)
208 @connection_parameters, @config = connection_parameters
, config
213 # Is this connection alive and ready for queries?
215 if @connection.respond_to
?(:status)
216 @connection.status
== PGconn
::CONNECTION_OK
218 # We're asking the driver, not ActiveRecord, so use @connection.query instead of #query
219 @connection.query
'SELECT 1'
222 # postgres-pr raises a NoMethodError when querying if no connection is available.
223 rescue PGError
, NoMethodError
227 # Close then reopen the connection.
229 if @connection.respond_to
?(:reset)
238 # Close the connection.
240 @connection.close
rescue nil
243 def native_database_types
#:nodoc:
244 NATIVE_DATABASE_TYPES
247 # Does PostgreSQL support migrations?
248 def supports_migrations
?
252 # Does PostgreSQL support standard conforming strings?
253 def supports_standard_conforming_strings
?
254 # Temporarily set the client message level above error to prevent unintentional
255 # error messages in the logs when working on a PostgreSQL database server that
256 # does not support standard conforming strings.
257 client_min_messages_old
= client_min_messages
258 self.client_min_messages
= 'panic'
260 # postgres-pr does not raise an exception when client_min_messages is set higher
261 # than error and "SHOW standard_conforming_strings" fails, but returns an empty
263 has_support
= query('SHOW standard_conforming_strings')[0][0] rescue false
264 self.client_min_messages
= client_min_messages_old
268 def supports_insert_with_returning
?
269 postgresql_version
>= 80200
272 def supports_ddl_transactions
?
276 def supports_savepoints
?
280 # Returns the configured supported identifier length supported by PostgreSQL,
281 # or report the default of 63 on PostgreSQL 7.x.
282 def table_alias_length
283 @table_alias_length ||= (postgresql_version
>= 80000 ? query('SHOW max_identifier_length')[0][0].to_i
: 63)
286 # QUOTING ==================================================
288 # Escapes binary strings for bytea input to the database.
289 def escape_bytea(value
)
290 if PGconn
.respond_to
?(:escape_bytea)
291 self.class.instance_eval
do
292 define_method(:escape_bytea) do |value
|
293 PGconn
.escape_bytea(value
) if value
297 self.class.instance_eval
do
298 define_method(:escape_bytea) do |value
|
301 value
.each_byte
{ |c
| result
<< sprintf('\\\\%03o', c
) }
310 # Unescapes bytea output from a database to the binary string it represents.
311 # NOTE: This is NOT an inverse of escape_bytea! This is only to be used
312 # on escaped binary output from database drive.
313 def unescape_bytea(value
)
314 # In each case, check if the value actually is escaped PostgreSQL bytea output
315 # or an unescaped Active Record attribute that was just written.
316 if PGconn
.respond_to
?(:unescape_bytea)
317 self.class.instance_eval
do
318 define_method(:unescape_bytea) do |value
|
319 if value
=~
/\\\d{3}/
320 PGconn
.unescape_bytea(value
)
327 self.class.instance_eval
do
328 define_method(:unescape_bytea) do |value
|
329 if value
=~
/\\\d{3}/
331 i
, max
= 0, value
.size
339 char
= value
[i
+1..i
+3].oct
353 unescape_bytea(value
)
356 # Quotes PostgreSQL-specific data types for SQL input.
357 def quote(value
, column
= nil) #:nodoc:
358 if value
.kind_of
?(String
) && column
&& column
.type
== :binary
359 "#{quoted_string_prefix}'#{escape_bytea(value)}'"
360 elsif value
.kind_of
?(String
) && column
&& column
.sql_type
=~
/^xml$/
361 "xml '#{quote_string(value)}'"
362 elsif value
.kind_of
?(Numeric
) && column
&& column
.sql_type
=~
/^money$/
363 # Not truly string input, so doesn't require (or allow) escape string syntax.
365 elsif value
.kind_of
?(String
) && column
&& column
.sql_type
=~
/^bit/
368 "B'#{value}'" # Bit-string notation
370 "X'#{value}'" # Hexadecimal notation
377 # Quotes strings for use in SQL input in the postgres driver for better performance.
378 def quote_string(s
) #:nodoc:
379 if PGconn
.respond_to
?(:escape)
380 self.class.instance_eval
do
381 define_method(:quote_string) do |s
|
386 # There are some incorrectly compiled postgres drivers out there
387 # that don't define PGconn.escape.
388 self.class.instance_eval
do
389 remove_method(:quote_string)
395 # Quotes column names for use in SQL queries.
396 def quote_column_name(name
) #:nodoc:
400 # Quote date/time values for use in SQL input. Includes microseconds
401 # if the value is a Time responding to usec.
402 def quoted_date(value) #:nodoc:
403 if value.acts_like?(:time) && value.respond_to?(:usec)
404 "#{super}.#{sprintf("%06d", value.usec)}"
410 # REFERENTIAL INTEGRITY ====================================
412 def supports_disable_referential_integrity?() #:nodoc:
413 version = query("SHOW server_version")[0][0].split('.')
414 (version[0].to_i >= 8 && version[1].to_i >= 1) ? true : false
419 def disable_referential_integrity(&block) #:nodoc:
420 if supports_disable_referential_integrity?() then
421 execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
425 if supports_disable_referential_integrity?() then
426 execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
430 # DATABASE STATEMENTS ======================================
432 # Executes a SELECT query and returns an array of rows. Each row is an
433 # array of field values.
434 def select_rows(sql, name = nil)
435 select_raw(sql, name).last
438 # Executes an INSERT query and returns the new record's ID
439 def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
440 # Extract the table from the insert sql. Yuck.
441 table = sql.split(" ", 4)[2].gsub('"', '')
443 # Try an insert with 'returning id' if available (PG >= 8.2)
444 if supports_insert_with_returning?
445 pk, sequence_name = *pk_and_sequence_for(table) unless pk
447 id = select_value("#{sql} RETURNING #{quote_column_name(pk)}")
453 # Otherwise, insert then grab last_insert_id.
457 # If neither pk nor sequence name is given, look them up.
458 unless pk || sequence_name
459 pk, sequence_name = *pk_and_sequence_for(table)
462 # If a pk is given, fallback to default sequence name.
463 # Don't fetch last insert id for a table without a pk.
464 if pk && sequence_name ||= default_sequence_name(table, pk)
465 last_insert_id(table, sequence_name)
470 # create a 2D array representing the result set
471 def result_as_array(res) #:nodoc:
472 # check if we have any binary column and if they need escaping
474 for j in 0...res.nfields do
475 # unescape string passed BYTEA field (OID == 17)
476 unescape_col << ( res.ftype(j)==17 )
480 for i in 0...res.ntuples do
482 for j in 0...res.nfields do
483 data = res.getvalue(i,j)
484 data = unescape_bytea(data) if unescape_col[j] and data.is_a?(String)
492 # Queries the database and returns the results in an Array-like object
493 def query(sql, name = nil) #:nodoc:
496 res = @connection.async_exec(sql)
498 res = @connection.exec(sql)
500 return result_as_array(res)
504 # Executes an SQL statement, returning a PGresult object on success
505 # or raising a PGError exception otherwise.
506 def execute(sql, name = nil)
509 @connection.async_exec(sql)
511 @connection.exec(sql)
516 # Executes an UPDATE query and returns the number of affected tuples.
517 def update_sql(sql, name = nil)
521 # Begins a transaction.
522 def begin_db_transaction
526 # Commits a transaction.
527 def commit_db_transaction
531 # Aborts a transaction.
532 def rollback_db_transaction
536 if defined?(PGconn::PQTRANS_IDLE)
537 # The ruby-pg driver supports inspecting the transaction status,
538 # while the ruby-postgres driver does not.
539 def outside_transaction?
540 @connection.transaction_status == PGconn::PQTRANS_IDLE
545 execute("SAVEPOINT #{current_savepoint_name}")
548 def rollback_to_savepoint
549 execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
552 def release_savepoint
553 execute("RELEASE SAVEPOINT #{current_savepoint_name}")
556 # SCHEMA STATEMENTS ========================================
558 def recreate_database(name) #:nodoc:
560 create_database(name)
563 # Create a new PostgreSQL database. Options include <tt>:owner</tt>, <tt>:template</tt>,
564 # <tt>:encoding</tt>, <tt>:tablespace</tt>, and <tt>:connection_limit</tt> (note that MySQL uses
565 # <tt>:charset</tt> while PostgreSQL uses <tt>:encoding</tt>).
568 # create_database config[:database], config
569 # create_database 'foo_development', :encoding => 'unicode'
570 def create_database(name, options = {})
571 options = options.reverse_merge(:encoding => "utf8")
573 option_string = options.symbolize_keys.sum do |key, value|
576 " OWNER = \"#{value}\""
578 " TEMPLATE = \"#{value}\""
580 " ENCODING = '#{value}'"
582 " TABLESPACE = \"#{value}\""
583 when :connection_limit
584 " CONNECTION LIMIT = #{value}"
590 execute "CREATE DATABASE #{quote_table_name(name)}#{option_string}"
593 # Drops a PostgreSQL database
596 # drop_database 'matt_development'
597 def drop_database(name) #:nodoc:
598 if postgresql_version >= 80200
599 execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
602 execute "DROP DATABASE #{quote_table_name(name)}"
603 rescue ActiveRecord::StatementInvalid
604 @logger.warn "#{name} database doesn't exist." if @logger
610 # Returns the list of all tables in the schema search path or a specified schema.
611 def tables(name = nil)
612 schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
613 query(<<-SQL, name).map { |row| row[0] }
616 WHERE schemaname IN (#{schemas})
620 # Returns the list of all indexes for a table.
621 def indexes(table_name
, name
= nil)
622 schemas
= schema_search_path
.split(/,/).map
{ |p
| quote(p
) }.join(',')
623 result
= query(<<-SQL, name)
624 SELECT distinct i.relname, d.indisunique, a.attname
625 FROM pg_class t, pg_class i, pg_index d, pg_attribute a
626 WHERE i.relkind = 'i'
627 AND d.indexrelid = i.oid
628 AND d.indisprimary = 'f'
629 AND t.oid = d.indrelid
630 AND t.relname = '#{table_name}'
631 AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN (#{schemas}) )
632 AND a.attrelid = t.oid
633 AND ( d.indkey[0]=a.attnum OR d.indkey[1]=a.attnum
634 OR d.indkey[2]=a.attnum OR d.indkey[3]=a.attnum
635 OR d.indkey[4]=a.attnum OR d.indkey[5]=a.attnum
636 OR d.indkey[6]=a.attnum OR d.indkey[7]=a.attnum
637 OR d.indkey[8]=a.attnum OR d.indkey[9]=a.attnum )
645 if current_index
!= row
[0]
646 indexes
<< IndexDefinition
.new(table_name
, row
[0], row
[1] == "t", [])
647 current_index
= row
[0]
650 indexes
.last
.columns
<< row
[2]
656 # Returns the list of all column definitions for a table.
657 def columns(table_name
, name
= nil)
658 # Limit, precision, and scale are all handled by the superclass.
659 column_definitions(table_name
).collect
do |name
, type
, default
, notnull
|
660 PostgreSQLColumn
.new(name
, default
, type
, notnull
== 'f')
664 # Returns the current database name.
666 query('select current_database()')[0][0]
669 # Returns the current database encoding format.
671 query(<<-end_sql)[0][0]
672 SELECT pg_encoding_to_char(pg_database.encoding) FROM pg_database
673 WHERE pg_database.datname LIKE '#{current_database}'
677 # Sets the schema search path to a string of comma-separated schema names.
678 # Names beginning with $ have to be quoted (e.g. $user => '$user').
679 # See: http://www.postgresql.org/docs/current/static/ddl-schemas.html
681 # This should be not be called manually but set in database.yml.
682 def schema_search_path
=(schema_csv
)
684 execute
"SET search_path TO #{schema_csv}"
685 @schema_search_path = schema_csv
689 # Returns the active schema search path.
690 def schema_search_path
691 @schema_search_path ||= query('SHOW search_path')[0][0]
694 # Returns the current client message level.
695 def client_min_messages
696 query('SHOW client_min_messages')[0][0]
699 # Set the client message level.
700 def client_min_messages
=(level
)
701 execute("SET client_min_messages TO '#{level}'")
704 # Returns the sequence name for a table's primary key or some other specified key.
705 def default_sequence_name(table_name
, pk
= nil) #:nodoc:
706 default_pk
, default_seq
= pk_and_sequence_for(table_name
)
707 default_seq
|| "#{table_name}_#{pk || default_pk || 'id'}_seq"
710 # Resets the sequence of a table's primary key to the maximum value.
711 def reset_pk_sequence
!(table
, pk
= nil, sequence
= nil) #:nodoc:
712 unless pk
and sequence
713 default_pk
, default_sequence
= pk_and_sequence_for(table
)
715 sequence
||= default_sequence
719 quoted_sequence
= quote_column_name(sequence
)
721 select_value
<<-end_sql, 'Reset sequence'
722 SELECT setval('#{quoted_sequence}', (SELECT COALESCE(MAX(#{quote_column_name pk})+(SELECT increment_by FROM #{quoted_sequence}), (SELECT min_value FROM #{quoted_sequence})) FROM #{quote_table_name(table)}), false)
725 @logger.warn
"#{table} has primary key #{pk} with no default sequence" if @logger
730 # Returns a table's primary key and belonging sequence.
731 def pk_and_sequence_for(table
) #:nodoc:
732 # First try looking for a sequence with a dependency on the
733 # given table's primary key.
734 result
= query(<<-end_sql, 'PK and serial sequence')[0]
735 SELECT attr.attname, seq.relname
741 WHERE seq.oid = dep.objid
742 AND seq.relkind = 'S'
743 AND attr.attrelid = dep.refobjid
744 AND attr.attnum = dep.refobjsubid
745 AND attr.attrelid = cons.conrelid
746 AND attr.attnum = cons.conkey[1]
747 AND cons.contype = 'p'
748 AND dep.refobjid = '#{table}'::regclass
751 if result
.nil? or result
.empty
?
752 # If that fails, try parsing the primary key's default value.
753 # Support the 7.x and 8.0 nextval('foo'::text) as well as
754 # the 8.1+ nextval('foo'::regclass).
755 result
= query(<<-end_sql, 'PK and custom sequence')[0]
758 WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
759 substr(split_part(def.adsrc, '''', 2),
760 strpos(split_part(def.adsrc, '''', 2), '.')+1)
761 ELSE split_part(def.adsrc, '''', 2)
764 JOIN pg_attribute attr ON (t.oid = attrelid)
765 JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
766 JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
767 WHERE t.oid = '#{table}'::regclass
768 AND cons.contype = 'p'
769 AND def.adsrc ~* 'nextval'
773 # [primary_key, sequence]
774 [result
.first
, result
.last
]
780 def rename_table(name
, new_name
)
781 execute
"ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
784 # Adds a new column to the named table.
785 # See TableDefinition#column for details of the options you can use.
786 def add_column(table_name
, column_name
, type
, options
= {})
787 default
= options
[:default]
788 notnull
= options
[:null] == false
791 execute("ALTER TABLE #{quote_table_name(table_name)} ADD COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}")
793 change_column_default(table_name
, column_name
, default
) if options_include_default
?(options
)
794 change_column_null(table_name
, column_name
, false, default
) if notnull
797 # Changes the column of a table.
798 def change_column(table_name
, column_name
, type
, options
= {})
799 quoted_table_name
= quote_table_name(table_name
)
802 execute
"ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
803 rescue ActiveRecord
::StatementInvalid => e
804 raise e
if postgresql_version
> 80000
805 # This is PostgreSQL 7.x, so we have to use a more arcane way of doing it.
808 tmp_column_name
= "#{column_name}_ar_tmp"
809 add_column(table_name
, tmp_column_name
, type
, options
)
810 execute
"UPDATE #{quoted_table_name} SET #{quote_column_name(tmp_column_name)} = CAST(#{quote_column_name(column_name)} AS #{type_to_sql(type, options[:limit], options[:precision], options[:scale])})"
811 remove_column(table_name
, column_name
)
812 rename_column(table_name
, tmp_column_name
, column_name
)
813 commit_db_transaction
815 rollback_db_transaction
819 change_column_default(table_name
, column_name
, options
[:default]) if options_include_default
?(options
)
820 change_column_null(table_name
, column_name
, options
[:null], options
[:default]) if options
.key
?(:null)
823 # Changes the default value of a table column.
824 def change_column_default(table_name
, column_name
, default
)
825 execute
"ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}"
828 def change_column_null(table_name
, column_name
, null
, default
= nil)
829 unless null
|| default
.nil?
830 execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
832 execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
835 # Renames a column in a table.
836 def rename_column(table_name
, column_name
, new_column_name
)
837 execute
"ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
840 # Drops an index from a table.
841 def remove_index(table_name
, options
= {})
842 execute
"DROP INDEX #{index_name(table_name, options)}"
845 # Maps logical Rails types to PostgreSQL-specific data types.
846 def type_to_sql(type
, limit
= nil, precision
= nil, scale
= nil)
847 return super unless type
.to_s
== 'integer'
850 when 1..2; 'smallint'
851 when 3..4, nil; 'integer'
853 else raise(ActiveRecordError
, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
857 # Returns a SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
859 # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
860 # requires that the ORDER BY include the distinct column.
862 # distinct("posts.id", "posts.created_at desc")
863 def distinct(columns
, order_by
) #:nodoc:
864 return "DISTINCT #{columns}" if order_by
.blank
?
866 # Construct a clean list of column names from the ORDER BY clause, removing
867 # any ASC/DESC modifiers
868 order_columns
= order_by
.split(',').collect
{ |s
| s
.split
.first
}
869 order_columns
.delete_if
&:blank?
870 order_columns
= order_columns
.zip((0...order_columns
.size
).to_a
).map
{ |s
,i
| "#{s} AS alias_#{i}" }
872 # Return a DISTINCT ON() clause that's distinct on the columns we want but includes
873 # all the required columns for the ORDER BY to work properly.
874 sql
= "DISTINCT ON (#{columns}) #{columns}, "
875 sql
<< order_columns
* ', '
878 # Returns an ORDER BY clause for the passed order option.
880 # PostgreSQL does not allow arbitrary ordering when using DISTINCT ON, so we work around this
881 # by wrapping the +sql+ string as a sub-select and ordering in that query.
882 def add_order_by_for_association_limiting
!(sql
, options
) #:nodoc:
883 return sql
if options
[:order].blank
?
885 order
= options
[:order].split(',').collect
{ |s
| s
.strip
}.reject(&:blank?)
886 order
.map
! { |s
| 'DESC' if s
=~
/\bdesc$/i
}
887 order
= order
.zip((0...order
.size
).to_a
).map
{ |s
,i
| "id_list.alias_#{i} #{s}" }.join(', ')
889 sql
.replace
"SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}"
893 # Returns the version of the connected PostgreSQL version.
894 def postgresql_version
895 @postgresql_version ||=
896 if @connection.respond_to
?(:server_version)
897 @connection.server_version
899 # Mimic PGconn.server_version behavior
901 query('SELECT version()')[0][0] =~
/PostgreSQL (\d+)\.(\d+)\.(\d+)/
902 ($1.to_i
* 10000) + ($2.to_i
* 100) + $3.to_i
910 # The internal PostgreSQL identifier of the money data type.
911 MONEY_COLUMN_TYPE_OID
= 790 #:nodoc:
913 # Connects to a PostgreSQL server and sets up the adapter depending on the
914 # connected server's characteristics.
916 @connection = PGconn
.connect(*@connection_parameters)
917 PGconn
.translate_results
= false if PGconn
.respond_to
?(:translate_results=)
919 # Ignore async_exec and async_query when using postgres-pr.
920 @async = @config[:allow_concurrency] && @connection.respond_to
?(:async_exec)
922 # Use escape string syntax if available. We cannot do this lazily when encountering
923 # the first string, because that could then break any transactions in progress.
924 # See: http://www.postgresql.org/docs/current/static/runtime-config-compatible.html
925 # If PostgreSQL doesn't know the standard_conforming_strings parameter then it doesn't
926 # support escape string syntax. Don't override the inherited quoted_string_prefix.
927 if supports_standard_conforming_strings
?
928 self.class.instance_eval
do
929 define_method(:quoted_string_prefix) { 'E' }
933 # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
934 # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
935 # should know about this but can't detect it there, so deal with it here.
936 money_precision
= (postgresql_version
>= 80300) ? 19 : 10
937 PostgreSQLColumn
.module_eval(<<-end_eval)
938 def extract_precision(sql_type) # def extract_precision(sql_type)
939 if sql_type =~ /^money$/ # if sql_type =~ /^money$/
940 #{money_precision} # 19
950 # Configures the encoding, verbosity, and schema search path of the connection.
951 # This is called by #connect and should not be called manually.
952 def configure_connection
953 if @config[:encoding]
954 if @connection.respond_to
?(:set_client_encoding)
955 @connection.set_client_encoding(@config[:encoding])
957 execute("SET client_encoding TO '#{@config[:encoding]}'")
960 self.client_min_messages
= @config[:min_messages] if @config[:min_messages]
961 self.schema_search_path
= @config[:schema_search_path] || @config[:schema_order]
964 # Returns the current ID of a table's sequence.
965 def last_insert_id(table
, sequence_name
) #:nodoc:
966 Integer(select_value("SELECT currval('#{sequence_name}')"))
969 # Executes a SELECT query and returns the results, performing any data type
970 # conversions that are required to be performed here instead of in PostgreSQLColumn.
971 def select(sql
, name
= nil)
972 fields
, rows
= select_raw(sql
, name
)
976 fields
.each_with_index
do |f
, i
|
984 def select_raw(sql
, name
= nil)
985 res
= execute(sql
, name
)
986 results
= result_as_array(res
)
991 results
.each
do |row
|
993 row
.each_index
do |cell_index
|
994 # If this is a money type column and there are any currency symbols,
995 # then strip them off. Indeed it would be prettier to do this in
996 # PostgreSQLColumn.string_to_decimal but would break form input
997 # fields that call value_before_type_cast.
998 if res
.ftype(cell_index
) == MONEY_COLUMN_TYPE_OID
999 # Because money output is formatted according to the locale, there are two
1000 # cases to consider (note the decimal separators):
1001 # (1) $12,345,678.12
1002 # (2) $12.345.678,12
1003 case column
= row
[cell_index
]
1004 when /^-?\D+[\d,]+\.\d{2}$/ # (1)
1005 row
[cell_index
] = column
.gsub(/[^-\d\.]/, '')
1006 when /^-?\D+[\d\.]+,\d{2}$/ # (2)
1007 row
[cell_index
] = column
.gsub(/[^-\d,]/, '').sub(/,/, '.')
1011 hashed_row
[fields
[cell_index
]] = column
1020 # Returns the list of a table's column names, data types, and default values.
1022 # The underlying query is roughly:
1023 # SELECT column.name, column.type, default.value
1024 # FROM column LEFT JOIN default
1025 # ON column.table_id = default.table_id
1026 # AND column.num = default.column_num
1027 # WHERE column.table_id = get_table_id('table_name')
1028 # AND column.num > 0
1029 # AND NOT column.is_dropped
1030 # ORDER BY column.num
1032 # If the table name is not prefixed with a schema, the database will
1033 # take the first match from the schema search path.
1035 # Query implementation notes:
1036 # - format_type includes the column size constraint, e.g. varchar(50)
1037 # - ::regclass is a function that gives the id for a table name
1038 def column_definitions(table_name
) #:nodoc:
1040 SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull
1041 FROM pg_attribute a LEFT JOIN pg_attrdef d
1042 ON a.attrelid = d.adrelid AND a.attnum = d.adnum
1043 WHERE a.attrelid = '#{table_name}'::regclass
1044 AND a.attnum > 0 AND NOT a.attisdropped