Skip to content
This repository
branch: fewer_strings
Fetching contributors…

Cannot retrieve contributors at this time

file 188 lines (175 sloc) 6.2 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
module ActiveRecord
  module ConnectionAdapters
    class PostgreSQLAdapter < AbstractAdapter
      module Quoting
        # Escapes binary strings for bytea input to the database.
        def escape_bytea(value)
          PGconn.escape_bytea(value) if value
        end

        # Unescapes bytea output from a database to the binary string it represents.
        # NOTE: This is NOT an inverse of escape_bytea! This is only to be used
        # on escaped binary output from database drive.
        def unescape_bytea(value)
          PGconn.unescape_bytea(value) if value
        end

        # Quotes PostgreSQL-specific data types for SQL input.
        def quote(value, column = nil) #:nodoc:
          return super unless column

          sql_type = type_to_sql(column.type, column.limit, column.precision, column.scale)

          case value
          when Range
            if /range$/ =~ sql_type
              "'#{PostgreSQLColumn.range_to_string(value)}'::#{sql_type}"
            else
              super
            end
          when Array
            case sql_type
            when 'point' then super(PostgreSQLColumn.point_to_string(value))
            when 'json' then super(PostgreSQLColumn.json_to_string(value))
            else
              if column.array
                "'#{PostgreSQLColumn.array_to_string(value, column, self).gsub(/'/, "''")}'"
              else
                super
              end
            end
          when Hash
            case sql_type
            when 'hstore' then super(PostgreSQLColumn.hstore_to_string(value), column)
            when 'json' then super(PostgreSQLColumn.json_to_string(value), column)
            else super
            end
          when IPAddr
            case sql_type
            when 'inet', 'cidr' then super(PostgreSQLColumn.cidr_to_string(value), column)
            else super
            end
          when Float
            if value.infinite? && column.type == :datetime
              "'#{value.to_s.downcase}'"
            elsif value.infinite? || value.nan?
              "'#{value.to_s}'"
            else
              super
            end
          when Numeric
            if sql_type == 'money' || [:string, :text].include?(column.type)
              # Not truly string input, so doesn't require (or allow) escape string syntax.
              "'#{value}'"
            else
              super
            end
          when String
            case sql_type
            when 'bytea' then "'#{escape_bytea(value)}'"
            when 'xml' then "xml '#{quote_string(value)}'"
            when /^bit/
              case value
              when /^[01]*$/ then "B'#{value}'" # Bit-string notation
              when /^[0-9A-F]*$/i then "X'#{value}'" # Hexadecimal notation
              end
            else
              super
            end
          else
            super
          end
        end

        def type_cast(value, column, array_member = false)
          return super(value, column) unless column

          case value
          when Range
            if /range$/ =~ column.sql_type
              PostgreSQLColumn.range_to_string(value)
            else
              super(value, column)
            end
          when NilClass
            if column.array && array_member
              'NULL'
            elsif column.array
              value
            else
              super(value, column)
            end
          when Array
            case column.sql_type
            when 'point' then PostgreSQLColumn.point_to_string(value)
            when 'json' then PostgreSQLColumn.json_to_string(value)
            else
              if column.array
                PostgreSQLColumn.array_to_string(value, column, self)
              else
                super(value, column)
              end
            end
          when String
            if 'bytea' == column.sql_type
              # Return a bind param hash with format as binary.
              # See http://deveiate.org/code/pg/PGconn.html#method-i-exec_prepared-doc
              # for more information
              { value: value, format: 1 }
            else
              super(value, column)
            end
          when Hash
            case column.sql_type
            when 'hstore' then PostgreSQLColumn.hstore_to_string(value)
            when 'json' then PostgreSQLColumn.json_to_string(value)
            else super(value, column)
            end
          when IPAddr
            if %w(inet cidr).include? column.sql_type
              PostgreSQLColumn.cidr_to_string(value)
            else
              super(value, column)
            end
          else
            super(value, column)
          end
        end

        # Quotes strings for use in SQL input.
        def quote_string(s) #:nodoc:
          @connection.escape(s)
        end

        # Checks the following cases:
        #
        # - table_name
        # - "table.name"
        # - schema_name.table_name
        # - schema_name."table.name"
        # - "schema.name".table_name
        # - "schema.name"."table.name"
        def quote_table_name(name)
          schema, name_part = extract_pg_identifier_from_name(name.to_s)

          unless name_part
            quote_column_name(schema)
          else
            table_name, name_part = extract_pg_identifier_from_name(name_part)
            "#{quote_column_name(schema)}.#{quote_column_name(table_name)}"
          end
        end

        def quote_table_name_for_assignment(table, attr)
          quote_column_name(attr)
        end

        # Quotes column names for use in SQL queries.
        def quote_column_name(name) #:nodoc:
          PGconn.quote_ident(name.to_s)
        end

        # Quote date/time values for use in SQL input. Includes microseconds
        # if the value is a Time responding to usec.
        def quoted_date(value) #:nodoc:
          result = super
          if value.acts_like?(:time) && value.respond_to?(:usec)
            result = "#{result}.#{sprintf("%06d", value.usec)}"
          end

          if value.year < 0
            result = result.sub(/^-/, "") + " BC"
          end
          result
        end
      end
    end
  end
end
Something went wrong with that request. Please try again.