using System.Globalization;
using System.Collections.Generic;
public static class ColumnNameUtilsV2
private const string Escape = "x_";
public static string EncodeColumnName(string input)
var data = input.ToLowerInvariant();
if (Reserved.Contains(data))
return Escape + ((int)(byte)data[0]).ToString("X2") + data.Substring(1);
data = data.Replace(Escape, Escape + ((int)(byte)Escape[0]).ToString("X2") + Escape.Substring(1), StringComparison.InvariantCultureIgnoreCase);
data = Escape + ((int)(byte)data[0]).ToString("X2") + data.Substring(1);
var utf8Bytes = Encoding.UTF8.GetBytes(data);
var result = new StringBuilder(data.Length);
foreach (var b in utf8Bytes)
if (c is >= 'a' and <= 'z' or >= 'A' and <= 'Z' or >= '0' and <= '9' or '_')
result.Append(Escape).Append(((int)b).ToString("X2"));
return result.ToString();
public static string DecodeColumnName(string input)
var data = Encoding.UTF8.GetBytes(input.ToLowerInvariant());
var result = new byte[data.Length];
for (int i = 0; i < data.Length - 1; i++)
if ((char)data[i] == char.ToLower(Escape[0]) && (char)data[i + 1] == char.ToLower(Escape[1]))
_ = byte.TryParse(input.AsSpan(i + 2, 2), NumberStyles.HexNumber, null, out var byteValue);
return Encoding.UTF8.GetString(result.AsSpan(0, j));
public static void Main()
var encoded = ColumnNameUtilsV2.EncodeColumnName(str);
var decoded = ColumnNameUtilsV2.DecodeColumnName(encoded);
Console.WriteLine("escape: " + Escape);
Console.WriteLine("input: " + str);
Console.WriteLine("encoded: " + encoded);
Console.WriteLine("decoded: " + decoded);
private static readonly HashSet<string> Reserved = new(){
"add", "all", "alter", "and", "any", "array", "as", "asc", "between", "by", "called", "case", "cast", "column",
"constraint", "costs", "create", "cross", "current_date", "current_schema", "current_time", "current_timestamp",
"current_user", "default", "delete", "deny", "desc", "describe", "directory", "distinct", "drop", "else", "end",
"escape", "except", "exists", "extract", "false", "first", "for", "from", "full", "function", "grant", "group",
"having", "if", "in", "index", "inner", "input", "insert", "intersect", "into", "is", "join", "last", "left",
"like", "limit", "match", "natural", "not", "null", "nulls", "object", "offset", "on", "or", "order", "outer",
"persistent", "recursive", "reset", "returns", "revoke", "right", "select", "session_user", "set", "some",
"stratify", "table", "then", "transient", "true", "try_cast", "unbounded", "union", "update", "user", "using",