Skip to content

Commit 75cf89d

Browse files
committed
Escape NUL byte as '\0'. Fixes #1121
1 parent d2ad209 commit 75cf89d

File tree

2 files changed

+58
-17
lines changed

2 files changed

+58
-17
lines changed

src/MySqlConnector/MySqlParameter.cs

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -226,16 +226,16 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
226226
writer.Write((byte) '\'');
227227
switch (charValue)
228228
{
229-
case '\'':
230-
writer.Write((byte) '\'');
231-
writer.Write((byte) charValue);
229+
case '\0' when !noBackslashEscapes:
230+
writer.Write((ushort) 0x305C); // \0
232231
break;
233232

234-
case '\\':
235-
if (!noBackslashEscapes)
236-
writer.Write((byte) '\\');
233+
case '\'':
234+
writer.Write((ushort) 0x2727); // ''
235+
break;
237236

238-
writer.Write((byte) charValue);
237+
case '\\' when !noBackslashEscapes:
238+
writer.Write((ushort) 0x5C5C); // \\
239239
break;
240240

241241
default:
@@ -288,7 +288,7 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
288288
var length = inputSpan.Length + BinaryBytes.Length + 1;
289289
foreach (var by in inputSpan)
290290
{
291-
if (by is 0x27 || by is 0x5C && !noBackslashEscapes)
291+
if (by is 0x27 || by is 0x00 or 0x5C && !noBackslashEscapes)
292292
length++;
293293
}
294294

@@ -297,9 +297,18 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
297297
var index = BinaryBytes.Length;
298298
foreach (var by in inputSpan)
299299
{
300-
if (by is 0x27 || by is 0x5C && !noBackslashEscapes)
300+
if (by == 0x00 && !noBackslashEscapes)
301+
{
302+
// \0
303+
outputSpan[index++] = 0x5C;
304+
outputSpan[index++] = 0x30;
305+
}
306+
else
307+
{
308+
if (by is 0x27 || by is 0x5C && !noBackslashEscapes)
309+
outputSpan[index++] = by;
301310
outputSpan[index++] = by;
302-
outputSpan[index++] = by;
311+
}
303312
}
304313
outputSpan[index++] = 0x27;
305314
Debug.Assert(index == length, "index == length");
@@ -394,9 +403,9 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
394403
writer.Write(BinaryBytes);
395404
foreach (var by in bytes)
396405
{
397-
if (by is 0x27 or 0x5C)
406+
if (by is 0x00 or 0x27 or 0x5C)
398407
writer.Write((byte) 0x5C);
399-
writer.Write(by);
408+
writer.Write(by == 0 ? (byte) 0x30 : by);
400409
}
401410
writer.Write((byte) '\'');
402411
}
@@ -471,7 +480,7 @@ static void WriteString(ByteBufferWriter writer, bool noBackslashEscapes, string
471480
if (noBackslashEscapes)
472481
writer.Write(value.Replace("'", "''"));
473482
else
474-
writer.Write(value.Replace("\\", "\\\\").Replace("'", "''"));
483+
writer.Write(value.Replace("\\", "\\\\").Replace("'", "''").Replace("\0", "\\0"));
475484

476485
writer.Write((byte) '\'');
477486
}
@@ -485,7 +494,7 @@ static void WriteString(ByteBufferWriter writer, bool noBackslashEscapes, bool w
485494
while (charsWritten < value.Length)
486495
{
487496
var remainingValue = value.Slice(charsWritten);
488-
var nextDelimiterIndex = remainingValue.IndexOfAny('\'', '\\');
497+
var nextDelimiterIndex = remainingValue.IndexOfAny('\0', '\'', '\\');
489498
if (nextDelimiterIndex == -1)
490499
{
491500
// write the rest of the string
@@ -495,11 +504,17 @@ static void WriteString(ByteBufferWriter writer, bool noBackslashEscapes, bool w
495504
else
496505
{
497506
// write up to (and including) the delimiter, then double it
498-
writer.Write(remainingValue.Slice(0, nextDelimiterIndex + 1), flush: true);
507+
writer.Write(remainingValue.Slice(0, nextDelimiterIndex), flush: true);
499508
if (remainingValue[nextDelimiterIndex] == '\\' && !noBackslashEscapes)
500-
writer.Write((byte) '\\');
509+
writer.Write((ushort) 0x5C5C); // \\
510+
else if (remainingValue[nextDelimiterIndex] == '\\' && noBackslashEscapes)
511+
writer.Write((byte) 0x5C); // \
501512
else if (remainingValue[nextDelimiterIndex] == '\'')
502-
writer.Write((byte) '\'');
513+
writer.Write((ushort) 0x2727); // ''
514+
else if (remainingValue[nextDelimiterIndex] == '\0' && !noBackslashEscapes)
515+
writer.Write((ushort) 0x305C); // \0
516+
else if (remainingValue[nextDelimiterIndex] == '\0' && noBackslashEscapes)
517+
writer.Write((byte) 0x00); // (nul)
503518
charsWritten += nextDelimiterIndex + 1;
504519
}
505520
}

tests/SideBySide/InsertTests.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,32 @@ value set('one', 'two', 'four', 'eight') null
680680
Assert.Equal(new[] { "one", "one,two", "one,four", "one,two,four" }, m_database.Connection.Query<string>(@"select value from insert_mysql_set where find_in_set('one', value) order by rowid"));
681681
}
682682

683+
[Theory]
684+
[MemberData(nameof(GetChars))]
685+
public void InsertChar(char ch, bool prepare)
686+
{
687+
using var connection = new MySqlConnection(AppConfig.ConnectionString);
688+
connection.Open();
689+
connection.Execute(@"drop table if exists insert_char;
690+
create table insert_char(
691+
rowid integer not null primary key auto_increment,
692+
value tinytext null
693+
);");
694+
695+
using (var cmd = new MySqlCommand("insert into insert_char(value) values(@data);", connection))
696+
{
697+
cmd.Parameters.AddWithValue("@data", ch);
698+
if (prepare)
699+
cmd.Prepare();
700+
cmd.ExecuteNonQuery();
701+
}
702+
Assert.Equal(ch, connection.Query<char>(@"select value from insert_char;").Single());
703+
}
704+
705+
public static IEnumerable<object[]> GetChars() =>
706+
new[] { '\0', 'a', '\'', '\"', '\\', 'A', '\b', '\n', '\r', '\t', '\x1A' }
707+
.SelectMany(x => new[] { false, true }.Select(y => new object[] { x, y }));
708+
683709

684710
#if !BASELINE
685711
[Theory]

0 commit comments

Comments
 (0)