This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'active_support/core_ext/big_decimal/conversions' | |
module ActiveRecord | |
module ConnectionAdapters # :nodoc: | |
module Quoting | |
NUM_REGEX = Regexp.new(/\A[+-]?\d+\.?\d*\Z/).freeze | |
# Quotes the column value to help prevent | |
# {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection]. | |
def quote(value, column = nil) | |
# records are quoted as their primary key | |
return value.quoted_id if value.respond_to?(:quoted_id) | |
case value | |
when String, ActiveSupport::Multibyte::Chars | |
value = value.to_s | |
return "'#{quote_string(value.to_s)}'" unless column || is_number?(value) | |
case column.type | |
when :integer then value.to_s.to_i.to_s | |
when :float then value.to_s.to_f.to_s | |
else | |
"'#{quote_string(value.to_s)}'" | |
end | |
when true, false | |
if column && column.type == :integer | |
value ? '1' : '0' | |
else | |
value ? quoted_true : quoted_false | |
end | |
# BigDecimals need to be put in a non-normalized form and quoted. | |
when nil then "NULL" | |
when BigDecimal then value.to_s('F') | |
when Numeric, ActiveSupport::Duration then value.to_s | |
when Date, Time then "'#{quoted_date(value)}'" | |
when Symbol then "'#{quote_string(value.to_s)}'" | |
when Class then "'#{value.to_s}'" | |
else | |
"'#{quote_string(YAML.dump(value))}'" | |
end | |
end | |
# Cast a +value+ to a type that the database understands. For example, | |
# SQLite does not understand dates, so this method will convert a Date | |
# to a String. | |
def type_cast(value, column) | |
if value.respond_to?(:quoted_id) && value.respond_to?(:id) | |
return value.id | |
end | |
case value | |
when String, ActiveSupport::Multibyte::Chars | |
value = value.to_s | |
return value unless column | |
case column.type | |
when :integer then value.to_i | |
when :float then value.to_f | |
else | |
value | |
end | |
when true, false | |
if column && column.type == :integer | |
value ? 1 : 0 | |
else | |
value ? 't' : 'f' | |
end | |
# BigDecimals need to be put in a non-normalized form and quoted. | |
when nil then nil | |
when BigDecimal then value.to_s('F') | |
when Numeric then value | |
when Date, Time then quoted_date(value) | |
when Symbol then value.to_s | |
else | |
to_type = column ? " to #{column.type}" : "" | |
raise TypeError, "can't cast #{value.class}#{to_type}" | |
end | |
end | |
# Quotes a string, escaping any ' (single quote) and \ (backslash) | |
# characters. | |
def quote_string(s) | |
s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode) | |
end | |
# Quotes the column name. Defaults to no quoting. | |
def quote_column_name(column_name) | |
column_name | |
end | |
# Quotes the table name. Defaults to column name quoting. | |
def quote_table_name(table_name) | |
quote_column_name(table_name) | |
end | |
# Override to return the quoted table name for assignment. Defaults to | |
# table quoting. | |
# | |
# This works for mysql and mysql2 where table.column can be used to | |
# resolve ambiguity. | |
# | |
# We override this in the sqlite and postgresql adapters to use only | |
# the column name (as per syntax requirements). | |
def quote_table_name_for_assignment(table, attr) | |
quote_table_name("#{table}.#{attr}") | |
end | |
def quoted_true | |
"'t'" | |
end | |
def quoted_false | |
"'f'" | |
end | |
def quoted_date(value) | |
if value.acts_like?(:time) | |
zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal | |
if value.respond_to?(zone_conversion_method) | |
value = value.send(zone_conversion_method) | |
end | |
end | |
value.to_s(:db) | |
end | |
private | |
def is_number?(number) | |
number =~ NUM_REGEX | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment