Skip to content
Browse files

Merge pull request #32 from skandragon/fix_string_array

String encoding fix, part 2
  • Loading branch information...
2 parents 429d447 + e4d4aec commit 483a823b17c7e5a50cb97c41eaf32aad4b6171ee @danmcclain danmcclain committed Nov 2, 2012
View
27 lib/postgres_ext/active_record/connection_adapters/postgres_adapter.rb
@@ -259,8 +259,10 @@ def type_cast_with_extended_types(value, column, part_array = false)
alias_method_chain :type_cast, :extended_types
def quote_with_extended_types(value, column = nil)
- if [Array, IPAddr].include? value.class
+ if value.is_a? IPAddr
"'#{type_cast(value, column)}'"
+ elsif value.is_a? Array
+ "'#{array_to_string(value, column, true)}'"
else
quote_without_extended_types(value, column)
end
@@ -319,15 +321,28 @@ def ipaddr_to_string(value)
"#{value.to_s}/#{value.instance_variable_get(:@mask_addr).to_s(2).count('1')}"
end
- def array_to_string(value, column)
- "{#{value.map { |val| item_to_string(val, column) }.join(',')}}"
+ def array_to_string(value, column, encode_single_quotes = false)
+ "{#{value.map { |val| item_to_string(val, column, encode_single_quotes) }.join(',')}}"
end
- def item_to_string(value, column)
+ def item_to_string(value, column, encode_single_quotes = false)
if value.nil?
'NULL'
- elsif value.is_a?String
- '"' + type_cast(value, column, true).gsub('"', '\\"') + '"'
+ elsif value.is_a? String
+ value = type_cast(value, column, true).dup
+ # Encode backslashes. One backslash becomes 4 in the resulting SQL.
+ # (why 4, and not 2? Trial and error shows 4 works, 2 fails to parse.)
+ value.gsub!('\\', '\\\\\\\\')
+ # Encode a bare " in the string as \"
+ value.gsub!('"', '\\"')
+ # PostgreSQL parses the string values differently if they are quoted for
+ # use in a statement, or if it will be used as part of a bound argument.
+ # For directly-inserted values (UPDATE foo SET bar='{"array"}') we need to
+ # escape ' as ''. For bound arguments, do not escape them.
+ if encode_single_quotes
+ value.gsub!("'", "''")
+ end
+ "\"#{value}\""
else
type_cast(value, column, true)
end
View
6 lib/postgres_ext/arel/visitors/to_sql.rb
@@ -22,11 +22,7 @@ def visit_IPAddr value
def change_string value
return value unless value.is_a?(String)
- if value.match /"|,|\{/
- value.gsub(/"/, "\"").gsub(/'/,'"')
- else
- value.gsub(/'/,'')
- end
+ value.gsub(/^\'/, '"').gsub(/\'$/, '"')
end
end
end
View
2 spec/arel/array_spec.rb
@@ -23,7 +23,7 @@ class ArelArray < ActiveRecord::Base
it 'converts Arel array_overlap statment' do
arel_table = ArelArray.arel_table
- arel_table.where(arel_table[:tags].array_overlap(['tag','tag 2'])).to_sql.should match /&& '\{tag,tag 2\}'/
+ arel_table.where(arel_table[:tags].array_overlap(['tag','tag 2'])).to_sql.should match /&& '\{"tag","tag 2"\}'/
end
it 'converts Arel array_overlap statment' do
View
99 spec/models/array_spec.rb
@@ -87,6 +87,105 @@ class User < ActiveRecord::Base
end
end
end
+
+ describe 'strings contain special characters' do
+ context '#save' do
+ it 'contains: \'' do
+ data = ['some\'thing']
+ u = User.create
+ u.nick_names = data
+ u.save!
+ u.reload
+ u.nick_names.should eq data
+ end
+
+ it 'contains: {' do
+ data = ['some{thing']
+ u = User.create
+ u.nick_names = data
+ u.save!
+ u.reload
+ u.nick_names.should eq data
+ end
+
+ it 'contains: }' do
+ data = ['some}thing']
+ u = User.create
+ u.nick_names = data
+ u.save!
+ u.reload
+ u.nick_names.should eq data
+ end
+
+ it 'contains: backslash' do
+ data = ['some\\thing']
+ u = User.create
+ u.nick_names = data
+ u.save!
+ u.reload
+ u.nick_names.should eq data
+ end
+
+ it 'contains: "' do
+ data = ['some"thing']
+ u = User.create
+ u.nick_names = data
+ u.save!
+ u.reload
+ u.nick_names.should eq data
+ end
+ end
+
+ context '#create' do
+ it 'contains: \'' do
+ data = ['some\'thing']
+ u = User.create(:nick_names => data)
+ u.reload
+ u.nick_names.should eq data
+ end
+
+ it 'contains: {' do
+ data = ['some{thing']
+ u = User.create(:nick_names => data)
+ u.reload
+ u.nick_names.should eq data
+ end
+
+ it 'contains: }' do
+ data = ['some}thing']
+ u = User.create(:nick_names => data)
+ u.reload
+ u.nick_names.should eq data
+ end
+
+ it 'contains: backslash' do
+ data = ['some\\thing']
+ u = User.create(:nick_names => data)
+ u.reload
+ u.nick_names.should eq data
+ end
+
+ it 'contains: "' do
+ data = ['some"thing']
+ u = User.create(:nick_names => data)
+ u.reload
+ u.nick_names.should eq data
+ end
+ end
+ end
+
+ describe 'array_overlap' do
+ it "works" do
+ arel = User.arel_table
+ User.create(:nick_names => ['this'])
+ x = User.create
+ x.nick_names = ["s'o{m}e", 'thing']
+ x.save
+ u = User.where(arel[:nick_names].array_overlap(["s'o{m}e"]))
+ u.first.should_not be_nil
+ u.first.nick_names.should eq ["s'o{m}e", 'thing']
+ end
+ end
end
context 'default values' do

0 comments on commit 483a823

Please sign in to comment.
Something went wrong with that request. Please try again.