-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Repositories: Quote table, column and alias names (closes #21451) #21577
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
Merged
AndyButland
merged 23 commits into
umbraco:main
from
idseefeld:v173/21451-quote-raw-sql-names-with-SqlSyntaxProvider-methods
Feb 3, 2026
Merged
Changes from 7 commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
028b07f
quote table, column and alias names with SqlSyntaxProvider methods in…
idseefeld e406c32
refactoring private methods into new file as internal methods,
idseefeld 8344bfb
refactor GetAlias method
idseefeld 6511dea
Double check the change
idseefeld 12072a1
improve code health
idseefeld 95388db
change new static classes into public static partial class NPocoSqlEx…
idseefeld c090be3
Merge branch 'main' into v173/21451-quote-raw-sql-names-with-SqlSynta…
idseefeld 4d610a6
Merge branch 'main' into v173/21451-quote-raw-sql-names-with-SqlSynta…
idseefeld b6dfd5b
Merge branch 'v173/21451-quote-raw-sql-names-with-SqlSyntaxProvider-m…
idseefeld a88479f
resolve some Copilot review suggestions
idseefeld f140a57
revert Copilot suggestion because it decreases code health
idseefeld c9deac7
revert test
idseefeld 72c0ca7
revert refactoring for CodeScene
idseefeld 100b472
delete obsolete Test
idseefeld a6608c8
remove new methods and updates, which are not relevat for this PR
idseefeld e549394
prepare for additional states in the future
idseefeld 968a7a6
don't mix string building methods
idseefeld 3eddfbd
fix SQL injection danger
idseefeld e44abf8
Merge branch 'main' into v173/21451-quote-raw-sql-names-with-SqlSynta…
idseefeld a4addf9
fix test for reverted methods
idseefeld 6ece7b9
Merge branch 'main' into v173/21451-quote-raw-sql-names-with-SqlSynta…
idseefeld 6ba7569
another SqlSyntax issue
idseefeld 3a264f5
Add additional unit and integration tests verifying the refactorings …
AndyButland File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
78 changes: 78 additions & 0 deletions
78
src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensionsInternal.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| using System.Linq.Expressions; | ||
| using System.Reflection; | ||
| using System.Text.RegularExpressions; | ||
| using NPoco; | ||
| using Umbraco.Cms.Core; | ||
| using Umbraco.Cms.Infrastructure.Persistence; | ||
|
|
||
| namespace Umbraco.Extensions | ||
| { | ||
| public static partial class NPocoSqlExtensions | ||
| { | ||
| internal static string GetAliasedField(this Sql<ISqlContext> sql, string field) | ||
| { | ||
| // get alias, if aliased | ||
| // | ||
| // regex looks for pattern "([\w+].[\w+]) AS ([\w+])" ie "(field) AS (alias)" | ||
| // and, if found & a group's field matches the field name, returns the alias | ||
| // | ||
| // so... if query contains "[umbracoNode].[nodeId] AS [umbracoNode__nodeId]" | ||
| // then GetAliased for "[umbracoNode].[nodeId]" returns "[umbracoNode__nodeId]" | ||
|
|
||
| MatchCollection matches = sql.SqlContext.SqlSyntax.AliasRegex.Matches(sql.SQL); | ||
| Match? match = matches.Cast<Match>().FirstOrDefault(m => m.Groups[1].Value.InvariantEquals(field)); | ||
| return match == null ? field : match.Groups[2].Value; | ||
| } | ||
|
|
||
| internal static string[] GetColumns<TDto>(this Sql<ISqlContext> sql, string? tableAlias = null, string? referenceName = null, Expression<Func<TDto, object?>>[]? columnExpressions = null, bool withAlias = true, bool forInsert = false) | ||
| { | ||
| PocoData? pd = sql.SqlContext.PocoDataFactory.ForType(typeof(TDto)); | ||
| var tableName = tableAlias ?? pd.TableInfo.TableName; | ||
| var queryColumns = pd.QueryColumns.ToList(); | ||
|
|
||
| Dictionary<string, string>? aliases = null; | ||
|
|
||
| if (columnExpressions != null && columnExpressions.Length > 0) | ||
| { | ||
| var names = columnExpressions.Select(x => | ||
| { | ||
| (MemberInfo member, var alias) = ExpressionHelper.FindProperty(x); | ||
| var field = member as PropertyInfo; | ||
| var fieldName = field?.GetColumnName(); | ||
| if (alias != null && fieldName is not null) | ||
| { | ||
| aliases ??= new Dictionary<string, string>(); | ||
| aliases[fieldName] = alias; | ||
| } | ||
| return fieldName; | ||
| }).ToArray(); | ||
|
|
||
| //only get the columns that exist in the selected names | ||
| queryColumns = queryColumns.Where(x => names.Contains(x.Key)).ToList(); | ||
|
|
||
| //ensure the order of the columns in the expressions is the order in the result | ||
| queryColumns.Sort((a, b) => names.IndexOf(a.Key).CompareTo(names.IndexOf(b.Key))); | ||
| } | ||
|
|
||
| string? GetAlias(PocoColumn column) | ||
| { | ||
| if (aliases != null && aliases.TryGetValue(column.ColumnName, out var alias)) | ||
| { | ||
| return alias; | ||
| } | ||
|
|
||
| return withAlias ? (string.IsNullOrEmpty(column.ColumnAlias) ? column.MemberInfoKey : column.ColumnAlias) : null; | ||
| } | ||
|
|
||
| return queryColumns | ||
| .Select(x => sql.SqlContext.SqlSyntax.GetColumn( | ||
| sql.SqlContext.DatabaseType, | ||
| tableName, | ||
| x.Value.ColumnName, | ||
| GetAlias(x.Value), | ||
| referenceName, | ||
| forInsert: forInsert)) | ||
| .ToArray(); | ||
| } | ||
| } | ||
| } |
37 changes: 37 additions & 0 deletions
37
src/Umbraco.Infrastructure/Persistence/NPocoSqlSelectExtensions.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| using System.Collections; | ||
| using System.Linq.Expressions; | ||
| using System.Reflection; | ||
| using System.Text; | ||
| using System.Text.RegularExpressions; | ||
| using NPoco; | ||
| using Umbraco.Cms.Core; | ||
| using Umbraco.Cms.Infrastructure.Persistence; | ||
| using Umbraco.Cms.Infrastructure.Persistence.Querying; | ||
| using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; | ||
|
|
||
| namespace Umbraco.Extensions | ||
| { | ||
| public static partial class NPocoSqlExtensions | ||
| { | ||
| /// <summary> | ||
| /// Adds a SQL SELECT statement to retrieve the maximum value of the specified field from the table associated | ||
| /// with the specified DTO type. | ||
| /// </summary> | ||
| /// <typeparam name="TDto">The type of the Data Transfer Object (DTO) that represents the table from which the maximum value will be | ||
| /// selected.</typeparam> | ||
| /// <param name="sql">The SQL query builder to which the SELECT statement will be appended. Cannot be <see langword="null"/>.</param> | ||
| /// <param name="field">An expression specifying the field for which the maximum value will be calculated. Cannot be <see | ||
| /// langword="null"/>.</param> | ||
| /// <param name="coalesceValue">COALESCE string value.</param> | ||
| /// <returns>A modified SQL query builder that includes the SELECT statement for the maximum value of the specified | ||
| /// field or the coalesceValue.</returns> | ||
| public static Sql<ISqlContext> SelectMax<TDto>(this Sql<ISqlContext> sql, Expression<Func<TDto, object?>> field, string coalesceValue) | ||
| { | ||
| ArgumentNullException.ThrowIfNull(sql); | ||
| ArgumentNullException.ThrowIfNull(field); | ||
|
|
||
| return sql.Select($"COALESCE(MAX {sql.SqlContext.SqlSyntax.GetFieldName(field)}), '{coalesceValue}')"); | ||
AndyButland marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| } | ||
| } | ||
22 changes: 22 additions & 0 deletions
22
src/Umbraco.Infrastructure/Persistence/NPocoSqlWhereExtensions.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| using System.Collections; | ||
| using System.Linq.Expressions; | ||
| using System.Reflection; | ||
| using System.Text; | ||
| using System.Text.RegularExpressions; | ||
| using NPoco; | ||
| using Umbraco.Cms.Core; | ||
| using Umbraco.Cms.Infrastructure.Persistence; | ||
| using Umbraco.Cms.Infrastructure.Persistence.Querying; | ||
| using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; | ||
|
|
||
| namespace Umbraco.Extensions | ||
| { | ||
| public static partial class NPocoSqlExtensions | ||
| { | ||
| public static Sql<ISqlContext> WhereParam<TDto>(this Sql<ISqlContext> sql, Expression<Func<TDto, object?>> field, string param) | ||
| { | ||
| string s = $"{sql.GetColumns(columnExpressions: [field], withAlias: false).FirstOrDefault()} = {param}"; | ||
| return sql.Where(s, []); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.