Skip to content

Instantly share code, notes, and snippets.

@stevehodgkiss
Created May 15, 2014 10:57
Show Gist options
  • Save stevehodgkiss/e4ca45b2bf2570ddb135 to your computer and use it in GitHub Desktop.
Save stevehodgkiss/e4ca45b2bf2570ddb135 to your computer and use it in GitHub Desktop.
# encoding: utf-8
require 'active_record'
ActiveRecord::Base.establish_connection(
:adapter => 'mysql2',
:host => '127.0.0.1',
:port => 13306,
:username => 'root',
:database => 'encoding_test',
:encoding => 'latin1')
conn = ActiveRecord::Base.connection
conn.execute('set sql_mode="STRICT_TRANS_TABLES"')
def create_table(name, charset)
ActiveRecord::Base.connection.create_table :test, :options => "ENGINE=InnoDB DEFAULT CHARSET=#{charset}" do |t|
yield t
end
end
def recreate_test_table(encoding)
conn.drop_table('test') if conn.table_exists?('test')
create_table('test', encoding) do |t|
t.string :name
end
end
def set_client_encoding(encoding)
conn.execute("set names #{encoding}")
end
def insert_name
conn.execute("insert into test (`name`) values ('#{utf8_name}')")
end
describe 'mysql encoding' do
let(:utf8_name) { 'testş' }
let(:conn) { ActiveRecord::Base.connection }
subject(:name) { conn.select_value('select name from test limit 1') }
before { conn.execute('truncate table test') }
context 'latin1 table' do
before do
recreate_test_table('latin1')
end
context 'latin1 client' do
before { set_client_encoding('latin1') }
it 'stores utf8 data unchanged' do
insert_name
name.bytes.should eq utf8_name.bytes
name.should_not eq utf8_name
end
it 'comes back marked as latin1' do
insert_name
name.encoding.should eq Encoding::ISO_8859_1
end
context 'when marked as utf8' do
before do
insert_name
name.force_encoding('utf-8')
end
it { should eq utf8_name }
end
context 'when client connection changes to utf8 client' do
before do
insert_name
set_client_encoding('utf8')
end
it 'comes back double encoded' do
name.should eq "testÅŸ"
name.should eq conn.select_value("SELECT CAST(_latin1'testş' AS CHAR CHARACTER SET utf8)")
end
it 'can be recovered' do
recovered = conn.select_value("SELECT CONVERT(CAST(CONVERT('#{name}' USING latin1) AS BINARY) USING utf8)")
recovered.should eq utf8_name
end
end
context 'after a mysqldump and restore' do
# its the same
before do
insert_name
`mysqldump -u root --default-character-set=latin1 encoding_test > /tmp/encoding_test.sql`
`mysql -u root encoding_test < /tmp/encoding_test.sql`
end
it 'stores utf8 data unchanged' do
name.bytes.should eq utf8_name.bytes
name.should_not eq utf8_name
end
context 'and utf8 client' do
before { set_client_encoding('utf8') }
it 'is double encoded' do
name.should eq "testÅŸ"
end
end
context 'when marked as utf8' do
before do
name.force_encoding('utf-8')
end
it { should eq utf8_name }
end
end
end
context 'utf8 client' do
before { set_client_encoding('utf8') }
context 'with strict_trans_tables' do
before do
conn.execute('set sql_mode="STRICT_TRANS_TABLES"')
end
it 'comes back double encoded' do
expect {
insert_name
}.to raise_error(ActiveRecord::StatementInvalid)
end
end
context 'without strict_trans_tables' do
before do
conn.execute('set sql_mode=""')
insert_name
end
it 'replaces unknown chars with ?' do
name.should eq "test?"
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment