Ticket #8049: postgresql_adapter.diff
| File postgresql_adapter.diff, 38.6 kB (added by FooBarWidget, 1 year ago) |
|---|
-
activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
old new 2 2 3 3 module ActiveRecord 4 4 class Base 5 # Establishes a connection to the database that's used by all Active Record objects 6 def self.postgresql_connection(config) # :nodoc:7 require_library_or_gem 'postgres'unless self.class.const_defined?(:PGconn)5 # Establishes a connection to the database that's used by all Active Record objects. 6 def self.postgresql_connection(config) 7 require_library_or_gem('postgres') unless self.class.const_defined?(:PGconn) 8 8 9 9 config = config.symbolize_keys 10 10 host = config[:host] … … 12 12 username = config[:username].to_s 13 13 password = config[:password].to_s 14 14 15 min_messages = config[:min_messages]16 17 15 if config.has_key?(:database) 18 16 database = config[:database] 19 17 else 20 raise ArgumentError, "No database specified. Missing argument: database."18 raise(ArgumentError, 'No database specified. Missing argument: database.') 21 19 end 22 20 23 pga = ConnectionAdapters::PostgreSQLAdapter.new( 24 PGconn.connect(host, port, "", "", database, username, password), logger, config 25 ) 21 # The postgres drivers don't allow to create an unconnected PGconn object, 22 # so just pass a nil connection object for the time being. 23 ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, [host, port, nil, nil, database, username, password], config) 24 end 25 end 26 26 27 PGconn.translate_results = false if PGconn.respond_to? :translate_results= 27 module ConnectionAdapters 28 # PostgreSQL-specific extensions to column definitions in a table. 29 class PostgreSQLColumn < Column #:nodoc: 30 # Instantiates a new PostgreSQL column definition in a table. 31 def initialize(name, default, sql_type = nil, null = true) 32 super(name, self.class.extract_value_from_default(default), sql_type, null) 33 end 28 34 29 pga.schema_search_path = config[:schema_search_path] || config[:schema_order] 35 private 36 # Extracts the scale from PostgreSQL-specific data types. 37 def extract_scale(sql_type) 38 # Money type has a fixed scale of 2. 39 if sql_type =~ /^money/ 40 2 41 else 42 super 43 end 44 end 30 45 31 pga 46 # Extracts the precision from PostgreSQL-specific data types. 47 def extract_precision(sql_type) 48 # Actual code is defined dynamically in PostgreSQLAdapter.connect 49 super 50 end 51 52 # Escapes binary strings for bytea input to the database. 53 def self.string_to_binary(value) 54 if PGconn.respond_to?(:escape_bytea) 55 self.class.module_eval do 56 define_method(:string_to_binary) do |value| 57 PGconn.escape_bytea(value) if value 58 end 59 end 60 else 61 self.class.module_eval do 62 define_method(:string_to_binary) do |value| 63 if value 64 result = '' 65 value.each_byte { |c| result << sprintf('\\\\%03o', c) } 66 result 67 end 68 end 69 end 70 end 71 self.class.string_to_binary(value) 72 end 73 74 # Unescapes bytea output from a database to the binary string it represents. 75 def self.binary_to_string(value) 76 # In each case, check if the value actually is escaped PostgresSQL bytea output 77 # or an unescaped Active Record attribute that was just written. 78 if PGconn.respond_to?(:unescape_bytea) 79 self.class.module_eval do 80 define_method(:binary_to_string) do |value| 81 if value =~ /\\\\\d{3}/ 82 PGconn.unescape_bytea(value) 83 else 84 value 85 end 86 end 87 end 88 else 89 self.class.module_eval do 90 define_method(:binary_to_string) do |value| 91 if value =~ /\\\\\d{3}/ 92 result = '' 93 i, max = 0, value.size 94 while i < max 95 char = value[i] 96 if char == ?\\ 97 if value[i+1] == ?\\ 98 char = ?\\ 99 i += 1 100 else 101 char = value[i+1..i+3].oct 102 i += 3 103 end 104 end 105 result << char 106 i += 1 107 end 108 result 109 else 110 value 111 end 112 end 113 end 114 end 115 self.class.binary_to_string(value) 116 end 117 118 # Maps PostgreSQL-specific data types to logical Rails types. 119 def simplified_type(field_type) 120 case field_type 121 # Numeric and monetary types 122 when /^(?:real|double precision)$/ 123 :float 124 # Monetary types 125 when /^money$/ 126 :decimal 127 # Character types 128 when /^(?:character varying|bpchar)(?:\(\d+\))?$/ 129 :string 130 # Binary data types 131 when /^bytea$/ 132 :binary 133 # Date/time types 134 when /^timestamp with(?:out)? time zone$/ 135 :datetime 136 when /^interval$/ 137 :string 138 # Geometric types 139 when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/ 140 :string 141 # Network address types 142 when /^(?:cidr|inet|macaddr)$/ 143 :string 144 # Bit strings 145 when /^bit(?: varying)?(?:\(\d+\))?$/ 146 :string 147 # XML type 148 when /^xml$/ 149 :string 150 # Arrays 151 when /^\D+\[\]$/ 152 :string 153 # Object identifier types 154 when /^oid$/ 155 :integer 156 # Pass through all types that are not specific to PostgreSQL. 157 else 158 super 159 end 160 end 161 162 # Extracts the value from a PostgreSQL column default definition. 163 def self.extract_value_from_default(default) 164 case default 165 # Numeric types 166 when /^-?\d+(\.\d*)?$/ 167 default 168 # Character types 169 when /^'(.*)'::(?:character varying|bpchar|text)$/ 170 $1 171 # Binary data types 172 when /^'(.*)'::bytea$/ 173 $1 174 # Date/time types 175 when /^'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)$/ 176 $1 177 when /^'(.*)'::interval$/ 178 $1 179 # Boolean type 180 when /^true$/ 181 true 182 when /^false$/ 183 false 184 # Geometric types 185 when /^'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)$/ 186 $1 187 # Network address types 188 when /^'(.*)'::(?:cidr|inet|macaddr)$/ 189 $1 190 # Bit string types 191 when /^B'(.*)'::"?bit(?: varying)?"?$/ 192 $1 193 # XML type 194 when /^'(.*)'::xml$/ 195 $1 196 # Arrays 197 when /^'(.*)'::"?\D+"?\[\]$/ 198 $1 199 # Object identifier types 200 when /^-?\d+$/ 201 $1 202 else 203 # Anything else is blank, some user type, or some function 204 # and we can't know the value of that, so return nil. 205 nil 206 end 207 end 32 208 end 33 end34 209 35 module ConnectionAdapters 36 # The PostgreSQL adapter works both with the C-based (http://www.postgresql.jp/interfaces/ruby/) and the Ruby-base 37 # (available both as gem and from http://rubyforge.org/frs/?group_id=234&release_id=1145) drivers. 210 # The PostgreSQL adapter works both with the native C (http://ruby.scripting.ca/postgres/) and the pure 211 # Ruby (available both as gem and from http://rubyforge.org/frs/?group_id=234&release_id=1944) drivers. 38 212 # 39 213 # Options: 40 214 # … … 48 222 # * <tt>:min_messages</tt> -- An optional client min messages that is using in a SET client_min_messages TO <min_messages> call on connection. 49 223 # * <tt>:allow_concurrency</tt> -- If true, use async query methods so Ruby threads don't deadlock; otherwise, use blocking query methods. 50 224 class PostgreSQLAdapter < AbstractAdapter 225 # Returns 'PostgreSQL' as adapter name for identification purposes. 51 226 def adapter_name 52 227 'PostgreSQL' 53 228 end 54 229 55 def initialize(connection, logger, config = {}) 230 # Initializes and connects a PostgreSQL adapter. 231 def initialize(connection, logger, connection_parameters, config) 56 232 super(connection, logger) 57 @con fig =config233 @connection_parameters, @config = connection_parameters, config 58 234 59 # Ignore async_exec and async_query with the postgres-pr client lib. 60 @async = config[:allow_concurrency] && @connection.respond_to?(:async_exec) 61 62 configure_connection 235 connect 63 236 end 64 237 65 # Is this connection alive and ready for queries?238 # Returns if this connection is alive and ready for queries. 66 239 def active? 67 240 if @connection.respond_to?(:status) 68 241 @connection.status == PGconn::CONNECTION_OK 69 242 else 70 @connection.query 'SELECT 1' 243 # We're asking the driver, not ActiveRecord, so use @connection.query instead of #query 244 @connection.query('SELECT 1') 71 245 true 72 246 end 73 # postgres-pr raises a NoMethodError when querying if no conn is available247 # postgres-pr raises a NoMethodError when querying if no connection is available. 74 248 rescue PGError, NoMethodError 75 249 false 76 250 end 77 251 78 # Close then reopenthe connection.252 # Closes and then reopens the connection. 79 253 def reconnect! 80 # TODO: postgres-pr doesn't have PGconn#reset.81 254 if @connection.respond_to?(:reset) 82 255 @connection.reset 83 256 configure_connection 257 else 258 disconnect! 259 connect 84 260 end 85 261 end 86 262 263 # Closes the connection. 87 264 def disconnect! 88 # Both postgres and postgres-pr respond to :close89 265 @connection.close rescue nil 90 266 end 91 267 92 def native_database_types 268 def native_database_types #:nodoc: 93 269 { 94 :primary_key => "serial primary key",95 :string => { :name => "character varying", :limit => 255 },96 :text => { :name => "text"},97 :integer => { :name => "integer"},98 :float => { :name => "float"},99 :decimal => { :name => "decimal"},100 :datetime => { :name => "timestamp"},101 :timestamp => { :name => "timestamp"},102 :time => { :name => "time"},103 :date => { :name => "date"},104 :binary => { :name => "bytea"},105 :boolean => { :name => "boolean"}270 :primary_key => 'serial PRIMARY KEY', 271 :string => { :name => 'character varying', :limit => 255 }, 272 :text => { :name => 'text' }, 273 :integer => { :name => 'integer' }, 274 :float => { :name => 'float' }, 275 :decimal => { :name => 'decimal' }, 276 :datetime => { :name => 'timestamp' }, 277 :timestamp => { :name => 'timestamp' }, 278 :time => { :name => 'time' }, 279 :date => { :name => 'date' }, 280 :binary => { :name => 'bytea' }, 281 :boolean => { :name => 'boolean' } 106 282 } 107 283 end 108 284 285 # Returns true to indicate that PostgreSQL supports migrations. 109 286 def supports_migrations? 110 287 true 111 288 end 112 289 290 # Returns the configured supported identifier length supported by PostgreSQL, 291 # or report the default of 63 on PostgreSQL 7. 113 292 def table_alias_length 114 63293 @table_alias_length ||= (postgresql_version >= 80000 ? query('SHOW max_identifier_length')[0][0].to_i : 63) 115 294 end 116 295 117 296 # QUOTING ================================================== 118 297 119 def quote(value, column = nil) 298 # Quotes PostgreSQL-specific data types for SQL input. 299 def quote(value, column = nil) #:nodoc: 120 300 if value.kind_of?(String) && column && column.type == :binary 121 "'#{escape_bytea(value)}'" 301 "#{quoted_string_prefix}'#{column.class.string_to_binary(value)}'" 302 elsif value.kind_of?(String) && column && column.sql_type =~ /^xml$/ 303 "xml '#{quote_string(value)}'" 304 elsif value.kind_of?(Numeric) && column && column.sql_type =~ /^money$/ 305 # Not truly string input, so doesn't require (or allow) escape string syntax. 306 "'#{value.to_s}'" 307 elsif value.kind_of?(String) && column && column.sql_type =~ /^bit/ 308 case value 309 when /^[01]*$/ 310 "B'#{value}'" # Bit-string notation 311 when /^[0-9A-F]*$/i 312 "X'#{value}'" # Hexadecimal notation 313 end 122 314 else 123 315 super 124 316 end 125 317 end 126 318 127 def quote_column_name(name) 319 # Quotes strings for use in SQL input in the postgres driver for better performance. 320 def quote_string(s) #:nodoc: 321 if PGconn.respond_to?(:escape) 322 self.class.instance_eval do 323 define_method(:quote_string) do |s| 324 PGconn.escape(s) 325 end 326 end 327 else 328 # There are some incorrectly compiled postgres drivers out there 329 # that don't define PGconn.escape. 330 self.class.instance_eval do 331 undef_method(:quote_string) 332 end 333 end 334 quote_string(s) 335 end 336 337 # Quotes column names for use in SQL queries. 338 def quote_column_name(name) #:nodoc: 128 339 %("#{name}") 129 340 end 130 341 131 # Include microseconds if the value is a Time responding to usec. 132 def quoted_date(value) 342 # Quote date/time values for use in SQL input. Includes microseconds 343 # if the value is a Time responding to usec. 344 def quoted_date(value) #:nodoc: 133 345 if value.acts_like?(:time) && value.respond_to?(:usec) 134 346 "#{super}.#{sprintf("%06d", value.usec)}" 135 347 else … … 137 349 end 138 350 end 139 351 140 141 352 # DATABASE STATEMENTS ====================================== 142 353 143 def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc: 354 # Executes an INSERT query and returns the new record's ID 355 def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) 144 356 execute(sql, name) 145 table = sql.split( " ", 4)[2]357 table = sql.split(' ', 4)[2] 146 358 id_value || last_insert_id(table, sequence_name || default_sequence_name(table, pk)) 147 359 end 148 360 361 # Queries the database and returns the results in an Array or nil otherwise. 149 362 def query(sql, name = nil) #:nodoc: 150 363 log(sql, name) do 151 364 if @async … … 156 369 end 157 370 end 158 371 159 def execute(sql, name = nil) #:nodoc: 372 # Executes a SQL statement, returning a PGresult object on success 373 # or raising a PGError exception otherwise. 374 def execute(sql, name = nil) 160 375 log(sql, name) do 161 376 if @async 162 377 @connection.async_exec(sql) … … 166 381 end 167 382 end 168 383 169 def update(sql, name = nil) #:nodoc: 384 # Executes an UPDATE query and returns the number of affected tuples. 385 def update(sql, name = nil) 170 386 execute(sql, name).cmdtuples 171 387 end 172 388 173 def begin_db_transaction #:nodoc: 174 execute "BEGIN" 389 # Begins a transaction. 390 def begin_db_transaction 391 execute('BEGIN') 175 392 end 176 393 177 def commit_db_transaction #:nodoc: 178 execute "COMMIT" 394 # Commits a transaction. 395 def commit_db_transaction 396 execute('COMMIT') 179 397 end 180 398 181 def rollback_db_transaction #:nodoc: 182 execute "ROLLBACK" 399 # Aborts a transaction. 400 def rollback_db_transaction 401 execute('ROLLBACK') 183 402 end 184 403 185 404 # SCHEMA STATEMENTS ======================================== 186 405 187 # Return the list of all tables in the schema search path.188 def tables(name = nil) #:nodoc:406 # Returns the list of all tables in the schema search path or a specified schema. 407 def tables(name = nil) 189 408 schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',') 190 409 query(<<-SQL, name).map { |row| row[0] } 191 410 SELECT tablename … … 194 413 SQL 195 414 end 196 415 197 def indexes(table_name, name = nil) #:nodoc: 416 # Returns the list of all indexes for a table. 417 def indexes(table_name, name = nil) 198 418 result = query(<<-SQL, name) 199 419 SELECT i.relname, d.indisunique, a.attname 200 420 FROM pg_class t, pg_class i, pg_index d, pg_attribute a … … 217 437 218 438 result.each do |row| 219 439 if current_index != row[0] 220 indexes << IndexDefinition.new(table_name, row[0], row[1] == "t", [])440 indexes << IndexDefinition.new(table_name, row[0], row[1] == 't', []) 221 441 current_index = row[0] 222 442 end 223 443 … … 227 447 indexes 228 448 end 229 449 230 def columns(table_name, name = nil) #:nodoc: 231 column_definitions(table_name).collect do |name, type, default, notnull, typmod| 232 # typmod now unused as limit, precision, scale all handled by superclass 233 Column.new(name, default_value(default), translate_field_type(type), notnull == "f") 450 # Returns the list of all column definitions for a table. 451 def columns(table_name, name = nil) 452 # Limit, precision, and scale are all handled by superclass. 453 column_definitions(table_name).collect do |name, type, default, notnull| 454 PostgreSQLColumn.new(name, default, type, notnull == 'f') 234 455 end 235 456 end 236 457 237 # Set the schema search path to a string of comma-separated schema names. 238 # Names beginning with $ are quoted (e.g. $user => '$user') 239 # See http://www.postgresql.org/docs/8.0/interactive/ddl-schemas.html 240 def schema_search_path=(schema_csv) #:nodoc: 458 # Sets the schema search path to a string of comma-separated schema names. 459 # Names beginning with $ have to be quoted (e.g. $user => '$user'). 460 # See: http://www.postgresql.org/docs/current/static/ddl-schemas.html 461 # 462 # This should be not be called manually but set in database.yml. 463 def schema_search_path=(schema_csv) 241 464 if schema_csv 242 execute "SET search_path TO #{schema_csv}"243 @schema_search_path = nil465 execute("SET search_path TO #{schema_csv}") 466 @schema_search_path = schema_csv 244 467 end 245 468 end 246 469 247 def schema_search_path #:nodoc: 470 # Returns the active schema search path. 471 def schema_search_path 248 472 @schema_search_path ||= query('SHOW search_path')[0][0] 249 473 end 250 474 251 def default_sequence_name(table_name, pk = nil) 475 # Returns the sequence name for a table's primary key or some other specified key. 476 def default_sequence_name(table_name, pk = nil) #:nodoc: 252 477 default_pk, default_seq = pk_and_sequence_for(table_name) 253 478 default_seq || "#{table_name}_#{pk || default_pk || 'id'}_seq" 254 479 end 255 480 256 # Resets sequence to the max value of the table's pk if present.257 def reset_pk_sequence!(table, pk = nil, sequence = nil) 258 unless pk andsequence481 # Resets the sequence of a table's primary key to the maximum value. 482 def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc: 483 unless pk && sequence 259 484 default_pk, default_sequence = pk_and_sequence_for(table) 260 485 pk ||= default_pk 261 486 sequence ||= default_sequence 262 487 end 263 488 if pk 264 489 if sequence 265 select_value <<-end_sql, 'Reset sequence'490 select_value(<<-end_sql, 'Reset sequence') 266 491 SELECT setval('#{sequence}', (SELECT COALESCE(MAX(#{pk})+(SELECT increment_by FROM #{sequence}), (SELECT min_value FROM #{sequence})) FROM #{table}), false) 267 492 end_sql 268 493 else 269 @logger.warn "#{table} has primary key #{pk} with no default sequence"if @logger494 @logger.warn("#{table} has primary key #{pk} with no default sequence") if @logger 270 495 end 271 496 end 272 497 end 273 498 274 # Find a table's primary key andsequence.275 def pk_and_sequence_for(table) 499 # Returns a table's primary key and belonging sequence. 500 def pk_and_sequence_for(table) #:nodoc: 276 501 # First try looking for a sequence with a dependency on the 277 502 # given table's primary key. 278 result = query(<<-end_sql, 'P Kand serial sequence')[0]279 SELECT attr.attname, name.nspname,seq.relname503 result = query(<<-end_sql, 'Primary key and serial sequence')[0] 504 SELECT attr.attname, seq.relname 280 505 FROM pg_class seq, 281 506 pg_attribute attr, 282 507 pg_depend dep, 283 508 pg_namespace name, 284 509 pg_constraint cons 285 510 WHERE seq.oid = dep.objid 286 AND seq.relnamespace = name.oid287 511 AND seq.relkind = 'S' 288 512 AND attr.attrelid = dep.refobjid 289 513 AND attr.attnum = dep.refobjsubid … … 293 517 AND dep.refobjid = '#{table}'::regclass 294 518 end_sql 295 519 296 if result.nil? orresult.empty?520 if result.nil? || result.empty? 297 521 # If that fails, try parsing the primary key's default value. 298 522 # Support the 7.x and 8.0 nextval('foo'::text) as well as 299 523 # the 8.1+ nextval('foo'::regclass). 300 # TODO: assumes sequence is in same schema as table. 301 result = query(<<-end_sql, 'PK and custom sequence')[0] 302 SELECT attr.attname, name.nspname, split_part(def.adsrc, '''', 2) 524 result = query(<<-end_sql, 'Primary key and custom sequence')[0] 525 SELECT attr.attname, split_part(def.adsrc, '''', 2) 303 526 FROM pg_class t 304 JOIN pg_namespace name ON (t.relnamespace = name.oid)305 527 JOIN pg_attribute attr ON (t.oid = attrelid) 306 528 JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum) 307 529 JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1]) … … 310 532 AND def.adsrc ~* 'nextval' 311 533 end_sql 312 534 end 313 # check for existence of . in sequence name as in public.foo_sequence. if it does not exist, return unqualified sequence 314 # We cannot qualify unqualified sequences, as rails doesn't qualify any table access, using the search path535 536 # [primary_key, sequence] 315 537 [result.first, result.last] 316 538 rescue 317 539 nil 318 540 end 319 541 542 # Renames a table. 320 543 def rename_table(name, new_name) 321 execute "ALTER TABLE #{name} RENAME TO #{new_name}"544 execute("ALTER TABLE #{name} RENAME TO #{new_name}") 322 545 end 323 546 547 # Adds a column to a table. 324 548 def add_column(table_name, column_name, type, options = {}) 325 549 default = options[:default] 326 550 notnull = options[:null] == false … … 341 565 end 342 566 end 343 567 344 def change_column(table_name, column_name, type, options = {}) #:nodoc: 568 # Changes the column of a table. 569 def change_column(table_name, column_name, type, options = {}) 345 570 begin 346 execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"571 execute("ALTER TABLE #{table_name} ALTER COLUMN #{column_name} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}") 347 572 rescue ActiveRecord::StatementInvalid 348 # This is P G7, so weuse a more arcane way of doing it.573 # This is PostgreSQL 7, so we have to use a more arcane way of doing it. 349 574 begin_db_transaction 350 575 add_column(table_name, "#{column_name}_ar_tmp", type, options) 351 execute "UPDATE #{table_name} SET #{column_name}_ar_tmp = CAST(#{column_name} AS #{type_to_sql(type, options[:limit], options[:precision], options[:scale])})"576 execute("UPDATE #{table_name} SET #{column_name}_ar_tmp = CAST(#{column_name} AS #{type_to_sql(type, options[:limit], options[:precision], options[:scale])})") 352 577 remove_column(table_name, column_name) 353 578 rename_column(table_name, "#{column_name}_ar_tmp", column_name) 354 579 commit_db_transaction … … 359 584 end 360 585 end 361 586 362 def change_column_default(table_name, column_name, default) #:nodoc: 363 execute "ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}" 587 # Changes the default value of a table column. 588 def change_column_default(table_name, column_name, default) 589 execute("ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}") 364 590 end 365 591 366 def rename_column(table_name, column_name, new_column_name) #:nodoc: 367 execute "ALTER TABLE #{table_name} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}" 592 # Renames a column in a table. 593 def rename_column(table_name, column_name, new_column_name) 594 execute("ALTER TABLE #{table_name} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}") 368 595 end 369 596 370 def remove_index(table_name, options) #:nodoc: 371 execute "DROP INDEX #{index_name(table_name, options)}" 597 # Drops an index from a table. 598 def remove_index(table_name, options = {}) 599 execute("DROP INDEX #{index_name(table_name, options)}") 372 600 end 373 601 374 def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc: 602 # Maps logical Rails types to PostgreSQL-specific data types. 603 def type_to_sql(type, limit = nil, precision = nil, scale = nil) 375 604 return super unless type.to_s == 'integer' 376 605 377 606 if limit.nil? || limit == 4 … … 382 611 'bigint' 383 612 end 384 613 end 385 386 # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.614 615 # Returns a SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause. 387 616 # 388 617 # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and 389 618 # requires that the ORDER BY include the distinct column. 390 619 # 391 620 # distinct("posts.id", "posts.created_at desc") 392 def distinct(columns, order_by) 621 def distinct(columns, order_by) #:nodoc: 393 622 return "DISTINCT #{columns}" if order_by.blank? 394 623 395 # construct a clean list of column names from the ORDER BY clause, removing396 # any asc/desc modifiers624 # Construct a clean list of column names from the ORDER BY clause, removing 625 # any ASC/DESC modifiers. 397 626 order_columns = order_by.split(',').collect { |s| s.split.first } 398 order_columns.delete_if &:blank?627 order_columns.delete_if(&:blank?) 399 628 order_columns = order_columns.zip((0...order_columns.size).to_a).map { |s,i| "#{s} AS alias_#{i}" } 400 629 401 # return a DISTINCT ON() clause that's distinct on the columns we want but includes402 # all the required columns for the ORDER BY to work properly 630 # Return a DISTINCT ON() clause that's distinct on the columns we want but includes 631 # all the required columns for the ORDER BY to work properly. 403 632 sql = "DISTINCT ON (#{columns}) #{columns}, " 404 633 sql << order_columns * ', ' 405 634 end 406 407 # ORDER BY clause for the passed order option.408 # 635 636 # Returns a ORDER BY clause for the passed order option. 637 # 409 638 # PostgreSQL does not allow arbitrary ordering when using DISTINCT ON, so we work around this 410 639 # by wrapping the sql as a sub-select and ordering in that query. 411 def add_order_by_for_association_limiting!(sql, options) 640 def add_order_by_for_association_limiting!(sql, options) #:nodoc: 412 641 return sql if options[:order].blank? 413 642 414 643 order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?) 415 644 order.map! { |s| 'DESC' if s =~ /\bdesc$/i } 416 645 order = order.zip((0...order.size).to_a).map { |s,i| "id_list.alias_#{i} #{s}" }.join(', ') 417 418 sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}"646 647 sql.replace("SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}") 419 648 end 420 649 650 protected 651 # Returns the version of the connected PostgreSQL version. 652 def postgresql_version 653 @postgresql_version ||= 654 if @connection.respond_to?(:server_version) 655 @connection.server_version 656 else 657 # Mimic PGconn.server_version behavior 658 begin 659 query('SELECT version()')[0][0] =~ /PostgreSQL (\d+)\.(\d+)\.(\d+)/ 660 ($1.to_i * 10000) + ($2.to_i * 100) + $3.to_i 661 rescue 662 0 663 end 664 end 665 end 666 421 667 private 422 BYTEA_COLUMN_TYPE_OID = 17 423 NUMERIC_COLUMN_TYPE_OID = 1700 424 TIMESTAMPOID = 1114 425 TIMESTAMPTZOID = 1184 668 # The internal PostgreSQL identifer of the money data type. 669 MONEY_COLUMN_TYPE_OID = 790 #:nodoc: 426 670 671 # Connects to a PostgreSQL server and sets up the adapter depending on the 672 # connected server's characteristics. 673 def connect 674 @connection = PGconn.connect(*@connection_parameters) 675 PGconn.translate_results = false if PGconn.respond_to?(:translate_results=) 676 677 # Ignore async_exec and async_query when using postgres-pr. 678 @async = @config[:allow_concurrency] && @connection.respond_to?(:async_exec) 679 680 # Use escape string syntax if available. We cannot do this lazily when encountering 681 # the first string, because that could then break any transactions in progress. 682 # See: http://www.postgresql.org/docs/current/static/runtime-config-compatible.html 683 begin 684 execute('SHOW standard_conforming_strings') 685 self.class.instance_eval do 686 define_method(:quoted_string_prefix) { 'E' } 687 end 688 rescue ActiveRecord::StatementInvalid 689 # If PostgreSQL doesn't know the standard_conforming_strings parameter then it doesn't 690 # support escape string syntax. Don't override the inherited quoted_string_prefix. 691 end 692 693 # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of 694 # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision 695 # should know about this but can't detect it there, so deal with it here. 696 money_precision = (postgresql_version >= 80300) ? 19 : 10 697 PostgreSQLColumn.module_eval(<<-end_eval) 698 def extract_precision(sql_type) 699 if sql_type =~ /^money$/ 700 #{money_precision} 701 else 702 super 703 end 704 end 705 end_eval 706 707 configure_connection 708 end 709 710 # Configures the encoding, verbosity, and schema search path of the connection. 711 # This is called by #connect and should not be called manually. 427 712 def configure_connection 428 713 if @config[:encoding] 429 execute("SET client_encoding TO '#{@config[:encoding]}'") 714 if @connection.respond_to?(:set_client_encoding) 715 @connection.set_client_encoding(@config[:encoding]) 716 else 717 execute("SET client_encoding TO '#{@config[:encoding]}'") 718 end 430 719 end 431 720 if @config[:min_messages] 432 721 execute("SET client_min_messages TO '#{@config[:min_messages]}'") 433 722 end 723 self.schema_search_path = @config[:schema_search_path] || @config[:schema_order] 434 724 end 435 725 436 def last_insert_id(table, sequence_name) 437 Integer(select_value("SELECT currval('#{sequence_name}')")) 726 # Returns the current ID of a table's sequence. 727 def last_insert_id(table, sequence_name) #:nodoc: 728 select_value("SELECT currval('#{sequence_name}')").to_i 438 729 end 439 730 731 # Executes a SELECT query and returns the results, performing any data type 732 # conversions that require to be performed here instead of in PostgreSQLColumn. 440 733 def select(sql, name = nil) 441 734 res = execute(sql, name) 442 735 results = res.result … … 445 738 fields = res.fields 446 739 results.each do |row| 447 740 hashed_row = {} 448 row.each_index do |cel _index|449 column = row[cel _index]741 row.each_index do |cell_index| 742 column = row[cell_index] 450 743 451 case res.type(cel_index) 452 when BYTEA_COLUMN_TYPE_OID 453 column = unescape_bytea(column) 454 when NUMERIC_COLUMN_TYPE_OID 455 column = column.to_d if column.respond_to?(:to_d) 744 # If this is a money type column and there are any currency symbols, 745 # then strip them off. Indeed it would be prettier to do this in 746 # PostgresSQLColumn.string_to_decimal but would break form input 747 # fields that call value_before_type_cast. 748 if res.type(cell_index) == MONEY_COLUMN_TYPE_OID 749 # Because money output is formatted according to the locale, there are two 750 # cases to consider (note the decimal seperators): 751 # (1) $12,345,678.12 752 # (2) $12.345.678,12 753 case column 754 when /^-?\D+[\d,]+\.\d{2}$/ # (1) 755 column = column.gsub(/[^-\d\.]/, '') 756 when /^-?\D+[\d\.]+,\d{2}$/ # (2) 757 column = column.gsub(/[^-\d,]/, '').sub(/,/, '.') 758 end 456 759 end 457 760 458 hashed_row[fields[cel _index]] = column761 hashed_row[fields[cell_index]] = column 459 762 end 460 763 rows << hashed_row 461 764 end … … 464 767 return rows 465 768 end 466 769 467 def escape_bytea(s) 468 if PGconn.respond_to? :escape_bytea 469 self.class.send(:define_method, :escape_bytea) do |s| 470 PGconn.escape_bytea(s) if s 471 end 472 else 473 self.class.send(:define_method, :escape_bytea) do |s| 474 if s 475 result = '' 476 s.each_byte { |c| result << sprintf('\\\\%03o', c) } 477 result 478 end 479 end 480 end 481 escape_bytea(s) 482 end 483 484 def unescape_bytea(s) 485 if PGconn.respond_to? :unescape_bytea 486 self.class.send(:define_method, :unescape_bytea) do |s| 487 PGconn.unescape_bytea(s) if s 488 end 489 else 490 self.class.send(:define_method, :unescape_bytea) do |s| 491 if s 492 result = '' 493 i, max = 0, s.size 494 while i < max 495 char = s[i] 496 if char == ?\\ 497 if s[i+1] == ?\\ 498 char = ?\\ 499 i += 1 500 else 501 char = s[i+1..i+3].oct 502 i += 3 503 end 504 end 505 result << char 506 i += 1 507 end 508 result 509 end 510 end 511 end 512 unescape_bytea(s) 513 end 514 515 # Query a table's column names, default values, and types. 770 # Returns the list of a table's column names, data types, and default values. 516 771 # 517 772 # The underlying query is roughly: 518 773 # SELECT column.name, column.type, default.value … … 530 785 # Query implementation notes: 531 786 # - format_type includes the column size constraint, e.g. varchar(50) 532 787 # - ::regclass is a function that gives the id for a table name 533 def column_definitions(table_name) 534 query <<-end_sql788 def column_definitions(table_name) #:nodoc: 789 query(<<-end_sql) 535 790 SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull 536 791 FROM pg_attribute a LEFT JOIN pg_attrdef d 537 ON a.attrelid = d.adrelid AND a.attnum = d.adnum 792 ON a.attrelid = d.adrelid 793 AND a.attnum = d.adnum 538 794 WHERE a.attrelid = '#{table_name}'::regclass 539 AND a.attnum > 0 AND NOT a.attisdropped 795 AND a.attnum > 0 796 AND NOT a.attisdropped 540 797 ORDER BY a.attnum 541 798 end_sql 542 799 end 543 544 # Translate PostgreSQL-specific types into simplified SQL types.545 # These are special cases; standard types are handled by546 # ConnectionAdapters::Column#simplified_type.547 def translate_field_type(field_type)548 # Match the beginning of field_type since it may have a size constraint on the end.549 case field_type550 # PostgreSQL array data types.551 when /\[\]$/i then 'string'552 when /^timestamp/i then 'datetime'