Skip to content

Commit

Permalink
fix(high): fix a bug of generating sql for enum
Browse files Browse the repository at this point in the history
  • Loading branch information
Basim108 committed Mar 9, 2021
1 parent 9d4d4f9 commit 6953ddb
Show file tree
Hide file tree
Showing 11 changed files with 240 additions and 110 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ public static class PropertyProfileExtensions
/// <returns>Returns True if it will use dynamic invoke, otherwise returns False.</returns>
public static bool IsDynamicallyInvoked(this PropertyProfile profile)
{
if (profile.PropertyExpression.Body is UnaryExpression) {
return true;
}
switch (profile.DbColumnType)
{
case NpgsqlDbType.Bigint:
Expand Down Expand Up @@ -148,65 +151,9 @@ public static string GetPropertyValueAsString<TEntity>(this PropertyProfile prof
public static object GetPropertyValue<TEntity>(this PropertyProfile profile, TEntity item)
where TEntity : class
{
if (profile == null)
if(profile == null)
throw new ArgumentNullException(nameof(profile));

var propertyName = "";

if (profile.PropertyExpression.Body is MemberExpression memberExpression)
propertyName = memberExpression.Member.Name;
else if (profile.PropertyExpression.Body is ParameterExpression parameterExpression)
propertyName = parameterExpression.Name;
else if (profile.PropertyExpression.Body is UnaryExpression unaryExpression &&
unaryExpression.Operand is MemberExpression operand)
propertyName = operand.Member.Name;
if (!string.IsNullOrEmpty(propertyName))
{
var propInfo = typeof(TEntity).GetProperty(propertyName);
if (propInfo == null)
throw new ArgumentException($"Entity: {typeof(TEntity).FullName} doesn't have property: '{propertyName}'");

switch (profile.DbColumnType)
{
case NpgsqlDbType.Text:
case NpgsqlDbType.Varchar:
var strGetter = (Func<TEntity, string>) Delegate.CreateDelegate(typeof(Func<TEntity, string>), propInfo.GetGetMethod());
return strGetter(item);
case NpgsqlDbType.Timestamp:
if (profile.IsNullable)
{
var dateTimeNullableGetter = (Func<TEntity, DateTime?>) Delegate.CreateDelegate(typeof(Func<TEntity, DateTime?>), propInfo.GetGetMethod());
return dateTimeNullableGetter(item);
}
var dateTimeGetter = (Func<TEntity, DateTime>) Delegate.CreateDelegate(typeof(Func<TEntity, DateTime>), propInfo.GetGetMethod());
return dateTimeGetter(item);
case NpgsqlDbType.TimestampTz:
if (profile.IsNullable)
{
var dateTimeOffsetNullableGetter = (Func<TEntity, DateTimeOffset?>) Delegate.CreateDelegate(typeof(Func<TEntity, DateTimeOffset?>), propInfo.GetGetMethod());
return dateTimeOffsetNullableGetter(item);
}
var dateTimeOffsetGetter = (Func<TEntity, DateTimeOffset>) Delegate.CreateDelegate(typeof(Func<TEntity, DateTimeOffset>), propInfo.GetGetMethod());
return dateTimeOffsetGetter(item);
}
}
var value = profile.PropertyExpressionCompiled.DynamicInvoke(item);
if (value != null && value.GetType().IsEnum)
{
switch (profile.DbColumnType)
{
case NpgsqlDbType.Bigint:
value = (long) value;
break;
case NpgsqlDbType.Integer:
value = (int) value;
break;
case NpgsqlDbType.Varchar:
value = value.ToString();
break;
}
value = (int) value;
}
return value;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,7 @@ public IList<SqlCommandBuilderResult> Generate<TEntity>(ICollection<TEntity> ele
if (!propInfo.IsPrivateKey)
continue;
var whereDelimiter = firstWhereExpression ? "" : " and ";
var propValue = propInfo.GetPropertyValueAsString(item);
if (propValue == null) {
if (propInfo.IsDynamicallyInvoked()) {
commandBuilder.Append($"{whereDelimiter}\"{propInfo.DbColumnName}\"={paramName}");
parameters.Add(new NpgsqlParameter(paramName, propInfo.DbColumnType)
{
Expand All @@ -159,6 +158,7 @@ public IList<SqlCommandBuilderResult> Generate<TEntity>(ICollection<TEntity> ele
});
}
else {
var propValue = propInfo.GetPropertyValueAsString(item);
commandBuilder.Append($"{whereDelimiter}\"{propInfo.DbColumnName}\"={propValue}");
}
firstWhereExpression = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,7 @@ public IList<SqlCommandBuilderResult> Generate<TEntity>(ICollection<TEntity> ele
cmdBuilder.Append(commandHeader);
whereDelimiter = "";
}
var propValue = privateKeys[0].GetPropertyValueAsString(elementsEnumerator.Current);
if (sqlParameters != null) {
if (privateKeys[0].IsDynamicallyInvoked()) {
var paramName = $"@param_{privateKeys[0].DbColumnName}_{elementIndex}";
cmdBuilder.Append($"{whereDelimiter}{paramName}");
var value = privateKeys[0].GetPropertyValue(elementsEnumerator.Current);
Expand All @@ -122,6 +121,7 @@ public IList<SqlCommandBuilderResult> Generate<TEntity>(ICollection<TEntity> ele
});
}
else {
var propValue = privateKeys[0].GetPropertyValueAsString(elementsEnumerator.Current);
cmdBuilder.Append($"{whereDelimiter}{propValue}");
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
using Npgsql;
using NpgsqlTypes;

namespace Hrimsoft.SqlBulk.PostgreSql
{
Expand Down Expand Up @@ -79,15 +75,16 @@ public IList<SqlCommandBuilderResult> Generate<TEntity>(
cancellationToken.ThrowIfCancellationRequested();
commandBuilder.Append('(');
var firstPropertyValue = true;
foreach (var propInfo in entityProfile.Properties.Values) {
if (propInfo.IsAutoGenerated)
continue;
var delimiter = firstPropertyValue ? "" : ", ";
commandBuilder.Append(delimiter);
var propValue = propInfo.GetPropertyValueAsString(item);
if (propValue == null) {
var paramName = $"@param_{propInfo.DbColumnName}_{elementIndex}";
try {
foreach (var pair in entityProfile.Properties) {
try {
var propInfo = pair.Value;
if (propInfo.IsAutoGenerated)
continue;
var delimiter = firstPropertyValue ? "" : ", ";
commandBuilder.Append(delimiter);
if (propInfo.IsDynamicallyInvoked()) {
var paramName = $"@param_{propInfo.DbColumnName}_{elementIndex}";

var value = propInfo.GetPropertyValue(item);
if (value == null) {
// as count of parameters are limited, it's better to save params for non null values
Expand All @@ -101,15 +98,16 @@ public IList<SqlCommandBuilderResult> Generate<TEntity>(
commandBuilder.Append(paramName);
}
}
catch (Exception ex) {
var message = $"an error occurred while calculating {paramName}";
throw new SqlGenerationException(SqlOperation.Insert, message, ex);
else {
var value = propInfo.GetPropertyValueAsString(item);
commandBuilder.Append(value);
}
firstPropertyValue = false;
}
else {
commandBuilder.Append(propValue);
catch (Exception ex) {
var message = $"an error occurred while processing a property {pair.Key} of entity {entityProfile.EntityType.Namespace} entity, item idx: {elementAbsIndex}";
throw new SqlGenerationException(SqlOperation.Insert, message, ex);
}
firstPropertyValue = false;
}
commandBuilder.Append(')');
if (sqlParameters.Count + entityProfile.MaxPossibleSqlParameters > MAX_PARAMS_PER_CMD) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,22 @@ public IList<SqlCommandBuilderResult> Generate<TEntity>(ICollection<TEntity> ele

var isThereReturningClause = false;
var allElementsAreNull = true;
var elementIndex = -1;
var elementIndex = 0;
var elementAbsIndex = 0;
using (var elementsEnumerator = elements.GetEnumerator()) {
var thereIsMoreElements = true;

// I'd like to build the first update command, so I can estimate an approximate size of all commands
// ignore all null items until find the first not null item
while (elementsEnumerator.Current == null && thereIsMoreElements) {
thereIsMoreElements = elementsEnumerator.MoveNext();
elementAbsIndex++;
}
if (thereIsMoreElements) {
allElementsAreNull = false;

var (commandForOneItem, itemParameters, hasReturningClause)
= GenerateForItem(entityProfile, elementsEnumerator.Current, null, 0);
= GenerateForItem(entityProfile, elementsEnumerator.Current, null, 0, elementAbsIndex);
isThereReturningClause = hasReturningClause;
sqlParameters.AddRange(itemParameters);

Expand All @@ -87,6 +90,7 @@ public IList<SqlCommandBuilderResult> Generate<TEntity>(ICollection<TEntity> ele
var commandBuilder = new StringBuilder(entireCommandLength);
commandBuilder.AppendLine(commandForOneItem);
while (elementsEnumerator.MoveNext()) {
elementAbsIndex++;
// ignore all null items
if (elementsEnumerator.Current == null)
continue;
Expand All @@ -107,7 +111,7 @@ public IList<SqlCommandBuilderResult> Generate<TEntity>(ICollection<TEntity> ele
elementIndex = 0;
}
(commandForOneItem, itemParameters, _)
= GenerateForItem(entityProfile, elementsEnumerator.Current, commandBuilder, elementIndex);
= GenerateForItem(entityProfile, elementsEnumerator.Current, commandBuilder, elementIndex, elementAbsIndex);
sqlParameters.AddRange(itemParameters);
commandBuilder.AppendLine(commandForOneItem);
}
Expand Down Expand Up @@ -135,12 +139,14 @@ public IList<SqlCommandBuilderResult> Generate<TEntity>(ICollection<TEntity> ele
/// <param name="item">an instance that has to be updated to the database</param>
/// <param name="externalBuilder">Builder to which the generated for an item command will be appended</param>
/// <param name="elementIndex">As this method is called for each item, this value will be added to the sql parameter name</param>
/// <param name="elementAbsIndex"></param>
/// <returns> Returns named tuple with generated command and list of db parameters. </returns>
public (string Command, ICollection<NpgsqlParameter> Parameters, bool hasReturningClause)
private (string Command, ICollection<NpgsqlParameter> Parameters, bool hasReturningClause)
GenerateForItem<TEntity>(EntityProfile entityProfile,
TEntity item,
StringBuilder externalBuilder,
int elementIndex)
int elementIndex,
int elementAbsIndex)
where TEntity : class
{
if (item == null)
Expand All @@ -158,13 +164,13 @@ public IList<SqlCommandBuilderResult> Generate<TEntity>(ICollection<TEntity> ele
var firstWhereExpression = true;
var firstReturningColumn = true;

foreach (var propInfo in entityProfile.Properties.Values) {
var paramName = $"@param_{propInfo.DbColumnName}_{elementIndex}";
foreach (var pair in entityProfile.Properties) {
try {
var propInfo = pair.Value;
var paramName = $"@param_{propInfo.DbColumnName}_{elementIndex}";
if (propInfo.IsPrivateKey) {
var whereDelimiter = firstWhereExpression ? "" : ",";
var keyValue = propInfo.GetPropertyValueAsString(item);
if (keyValue == null) {
if (propInfo.IsDynamicallyInvoked()) {
whereClause += $"{whereDelimiter}\"{propInfo.DbColumnName}\"={paramName}";
parameters.Add(new NpgsqlParameter(paramName, propInfo.DbColumnType)
{
Expand All @@ -173,6 +179,7 @@ public IList<SqlCommandBuilderResult> Generate<TEntity>(ICollection<TEntity> ele
});
}
else {
var keyValue = propInfo.GetPropertyValueAsString(item);
whereClause += $"{whereDelimiter}\"{propInfo.DbColumnName}\"={keyValue}";
}
firstWhereExpression = false;
Expand All @@ -185,8 +192,8 @@ public IList<SqlCommandBuilderResult> Generate<TEntity>(ICollection<TEntity> ele
if (propInfo.IsAutoGenerated)
continue;
var setClauseDelimiter = firstSetExpression ? "" : ",";
var propValue = propInfo.GetPropertyValueAsString(item);
if (propValue == null) {

if (propInfo.IsDynamicallyInvoked()) {
commandBuilder.Append($"{setClauseDelimiter}\"{propInfo.DbColumnName}\"=");
var value = propInfo.GetPropertyValue(item);
if (value == null) {
Expand All @@ -201,12 +208,13 @@ public IList<SqlCommandBuilderResult> Generate<TEntity>(ICollection<TEntity> ele
}
}
else {
var propValue = propInfo.GetPropertyValueAsString(item);
commandBuilder.Append($"{setClauseDelimiter}\"{propInfo.DbColumnName}\"={propValue}");
}
firstSetExpression = false;
}
catch (Exception ex) {
var message = $"an error occurred while calculating {paramName}";
var message = $"an error occurred while processing a property {pair.Key} of {entityProfile.EntityType.Name} entity, item idx: {elementAbsIndex}";
throw new SqlGenerationException(SqlOperation.Update, message, ex);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,16 +85,17 @@ public IList<SqlCommandBuilderResult> Generate<TEntity>(ICollection<TEntity> ele

commandBuilder.Append('(');
var firstPropertyValue = true;
foreach (var propInfo in entityProfile.Properties.Values) {
if (propInfo.IsAutoGenerated)
continue;
var delimiter = firstPropertyValue ? "" : ", ";
commandBuilder.Append(delimiter);
var propValue = propInfo.GetPropertyValueAsString(item);
if (propValue == null) {
var paramName = $"@param_{propInfo.DbColumnName}_{elementIndex}";
try {
var value = propInfo.GetPropertyValue(item);
foreach (var pair in entityProfile.Properties) {
try {
var propInfo = pair.Value;
if (propInfo.IsAutoGenerated)
continue;
var delimiter = firstPropertyValue ? "" : ", ";
commandBuilder.Append(delimiter);

if (propInfo.IsDynamicallyInvoked()) {
var paramName = $"@param_{propInfo.DbColumnName}_{elementIndex}";
var value = propInfo.GetPropertyValue(item);
if (value == null) {
// as count of parameters are limited, it's better to save params for non null values
commandBuilder.Append("null");
Expand All @@ -107,15 +108,16 @@ public IList<SqlCommandBuilderResult> Generate<TEntity>(ICollection<TEntity> ele
commandBuilder.Append(paramName);
}
}
catch (Exception ex) {
var message = $"an error occurred while calculating a {paramName}";
throw new SqlGenerationException(SqlOperation.Upsert, message, ex);
else {
var propValue = propInfo.GetPropertyValueAsString(item);
commandBuilder.Append(propValue);
}
firstPropertyValue = false;
}
else {
commandBuilder.Append(propValue);
catch (Exception ex) {
var message = $"an error occurred while processing a property {pair.Key} of {entityProfile.EntityType.Name} entity, item idx: {elementAbsIndex}";
throw new SqlGenerationException(SqlOperation.Upsert, message, ex);
}
firstPropertyValue = false;
}
commandBuilder.Append(')');
if (sqlParameters.Count + entityProfile.MaxPossibleSqlParameters > MAX_PARAMS_PER_CMD) {
Expand Down
Loading

0 comments on commit 6953ddb

Please sign in to comment.