Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quote postgres table and column names which use reserved keywords #155

Merged
merged 1 commit into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/Weasel.Postgresql.Tests/Tables/creating_tables_in_database.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,4 +210,27 @@ bool withDistinctNulls
(await table.ExistsInDatabaseAsync(theConnection))
.ShouldBeTrue();
}

[Fact]
public async Task create_table_with_name_and_column_using_reserved_keyword()
{
await theConnection.OpenAsync();

await theConnection.ResetSchemaAsync("tables");

var table = new Table("order");
table.AddColumn<int>("id").AsPrimaryKey();
table.AddColumn<string>("first_name");
table.AddColumn<string>("last_name");
table.AddColumn<int>("order");

await CreateSchemaObjectInDatabase(table);

(await table.ExistsInDatabaseAsync(theConnection))
.ShouldBeTrue();

await theConnection.CreateCommand(
"insert into \"order\" (id, first_name, last_name, \"order\") values (1, 'Elton', 'John',1)")
.ExecuteNonQueryAsync();
}
}
2 changes: 2 additions & 0 deletions src/Weasel.Postgresql/PostgresqlObjectName.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ namespace Weasel.Postgresql;

public class PostgresqlObjectName: DbObjectName
{
protected override string QuotedQualifiedName => $"{SchemaUtils.QuoteName(Schema)}.{SchemaUtils.QuoteName(Name)}";

public PostgresqlObjectName(string schema, string name)
: base(schema, name, PostgresqlProvider.Instance.ToQualifiedName(schema, name))
{
Expand Down
20 changes: 20 additions & 0 deletions src/Weasel.Postgresql/SchemaUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,25 @@ private static async Task<bool> dropSchema(string connectionString, string schem
throw;
}
}

public static string QuoteName(string name)
{
return ReservedKeywords.Contains(name, StringComparer.InvariantCultureIgnoreCase) ? $"\"{name}\"" : name;
}

private static readonly string[] ReservedKeywords =
[
"ALL", "ANALYSE", "ANALYZE", "AND", "ANY", "ARRAY", "AS", "ASC", "ASYMMETRIC", "AUTHORIZATION",
"BINARY", "BOTH", "CASE", "CAST", "CHECK", "COLLATE", "COLUMN", "CONCURRENTLY", "CONSTRAINT",
"CREATE", "CROSS", "CURRENT_CATALOG", "CURRENT_DATE", "CURRENT_ROLE", "CURRENT_SCHEMA",
"CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "DEFAULT", "DEFERRABLE", "DESC", "DISTINCT",
"DO", "ELSE", "END", "EXCEPT", "FALSE", "FETCH", "FOR", "FOREIGN", "FREEZE", "FROM", "FULL",
"GRANT", "GROUP", "HAVING", "ILIKE", "IN", "INITIALLY", "INNER", "INTERSECT", "INTO", "IS",
"ISNULL", "JOIN", "LATERAL", "LEADING", "LEFT", "LIKE", "LIMIT", "LOCALTIME", "LOCALTIMESTAMP",
"NATURAL", "NOT", "NOTNULL", "NULL", "OFFSET", "ON", "ONLY", "OR", "ORDER", "OUTER", "OVERLAPS",
"PLACING", "PRIMARY", "REFERENCES", "RETURNING", "RIGHT", "SELECT", "SESSION_USER", "SIMILAR",
"SOME", "SYMMETRIC", "TABLE", "THEN", "TO", "TRAILING", "TRUE", "UNION", "UNIQUE", "USER",
"USING", "VARIADIC", "VERBOSE", "WHEN", "WHERE", "WINDOW", "WITH"
];
}

4 changes: 2 additions & 2 deletions src/Weasel.Postgresql/Tables/Table.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,11 @@ public void WriteCreateStatement(Migrator migrator, TextWriter writer)

if (migrator.Formatting == SqlFormatting.Pretty)
{
var columnLength = Columns.Max(x => x.Name.Length) + 4;
var columnLength = Columns.Max(x => x.QuotedName.Length) + 4;
var typeLength = Columns.Max(x => x.Type.Length) + 4;

var lines = Columns.Select(column =>
$" {column.Name.PadRight(columnLength)}{column.Type.PadRight(typeLength)}{column.Declaration()}")
$" {column.QuotedName.PadRight(columnLength)}{column.Type.PadRight(typeLength)}{column.Declaration()}")
.ToList();

if (PrimaryKeyColumns.Any())
Expand Down
12 changes: 7 additions & 5 deletions src/Weasel.Postgresql/Tables/TableColumn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ public TableColumn(string name, string type)

public string Name { get; }

public string QuotedName => SchemaUtils.QuoteName(Name);

public string RawType()
{
return Type.Split('(')[0].Trim();
Expand All @@ -59,7 +61,7 @@ public string Declaration()

protected bool Equals(TableColumn other)
{
return string.Equals(Name, other.Name) &&
return string.Equals(QuotedName, other.QuotedName) &&
string.Equals(PostgresqlProvider.Instance.ConvertSynonyms(RawType()),
PostgresqlProvider.Instance.ConvertSynonyms(other.RawType()));
}
Expand Down Expand Up @@ -97,8 +99,8 @@ public string ToDeclaration()
var declaration = Declaration();

return declaration.IsEmpty()
? $"{Name} {Type}"
: $"{Name} {Type} {declaration}";
? $"{QuotedName} {Type}"
: $"{QuotedName} {Type} {declaration}";
}

public override string ToString()
Expand All @@ -109,12 +111,12 @@ public override string ToString()

public virtual string AlterColumnTypeSql(Table table, TableColumn changeActual)
{
return $"alter table {table.Identifier} alter column {Name.PadRight(Name.Length)} type {Type};";
return $"alter table {table.Identifier} alter column {QuotedName.PadRight(QuotedName.Length)} type {Type};";
}

public string DropColumnSql(Table table)
{
return $"alter table {table.Identifier} drop column {Name};";
return $"alter table {table.Identifier} drop column {QuotedName};";
}


Expand Down
Loading