Skip to content

Commit

Permalink
[QP-9760] Replace DatePart expression in Date functions (#93)
Browse files Browse the repository at this point in the history
DATE 기반 함수 중에서 첫번째 파라미터를 DATEPART로 사용하는 함수가 column 으로 인식되어서 Reference
오류가 발생하던 문제를 해결합니다.

대상 함수는 아래와 같습니다.

DATE_BUCKET
DATEADD
DATEDIFF
DATEDIFF_BIG
DATENAME
DATEPART
DATETRUNC

만약 대상 함수들을 사용했을때 첫번째 파라미터가 1 Level Column 으로 인식된 경우
SqlServerDatePartExpression으로 치환합니다.
  • Loading branch information
tony-jang authored Jul 8, 2024
1 parent 030dc5a commit 33af7da
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 60 deletions.
3 changes: 3 additions & 0 deletions Qsi.SqlServer/Analyzers/SqlServerTableAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ protected override IEnumerable<QsiTableColumn> ResolveColumnsInExpression(TableC
{
case SqlServerPhyslocExpressionNode:
return Enumerable.Empty<QsiTableColumn>();

case SqlServerDatePartExpressionNode:
return Enumerable.Empty<QsiTableColumn>();
}

return base.ResolveColumnsInExpression(context, expression);
Expand Down
12 changes: 12 additions & 0 deletions Qsi.SqlServer/Tree/SqlServerDatePartExpressionNode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Collections.Generic;
using System.Linq;
using Qsi.Tree;

namespace Qsi.SqlServer.Tree;

public class SqlServerDatePartExpressionNode : QsiExpressionNode
{
public override IEnumerable<IQsiTreeNode> Children => Enumerable.Empty<IQsiTreeNode>();

public string DatePart { get; set; }
}
155 changes: 95 additions & 60 deletions Qsi.SqlServer/Tree/Visitors/ExpressionVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@ namespace Qsi.SqlServer.Tree;

internal sealed class ExpressionVisitor : VisitorBase
{
private static readonly string[] _datePartFunctions =
{
"DATE_BUCKET",
"DATEADD",
"DATEDIFF",
"DATEDIFF_BIG",
"DATENAME",
"DATEPART",
"DATETRUNC"
};

public List<Range> PhyslocRanges { get; } = new();

public ExpressionVisitor(IVisitorContext visitorContext) : base(visitorContext)
Expand Down Expand Up @@ -507,7 +518,7 @@ public QsiExpressionNode VisitPrimaryExpression(PrimaryExpression primaryExpress
{
MultiPartIdentifierCallTarget multiPartIdentifierCallTarget => multiPartIdentifierCallTarget.MultiPartIdentifier,
UserDefinedTypeCallTarget userDefinedTypeCallTarget => userDefinedTypeCallTarget.SchemaObjectName,
ExpressionCallTarget _ => throw TreeHelper.NotSupportedFeature("expression call target"),
ExpressionCallTarget => throw TreeHelper.NotSupportedFeature("expression call target"),
null => new MultiPartIdentifier(),
_ => throw TreeHelper.NotSupportedTree(functionCall.CallTarget)
};
Expand Down Expand Up @@ -856,71 +867,95 @@ public QsiInvokeExpressionNode CreateInvokeExpression(TSqlFragment fragment, Qsi
{
n.Member.SetValue(functionExpressionNode);
n.Parameters.AddRange(parameters
.Where(p => p != null)
.Select(p =>
if (functionExpressionNode.Identifier.Level is 1 &&
IsDatePartParameterFunction(functionExpressionNode.Identifier[^1].Value))
{
var parameter = parameters.FirstOrDefault();
// 첫번째 파라미터가 DATEPART에 해당하는 값인 경우에만 특수 처리
if (parameter is ColumnReferenceExpression { MultiPartIdentifier.Count: 1 } columnReferenceExpression)
{
switch (p)
n.Parameters.Add(TreeHelper.Create<SqlServerDatePartExpressionNode>(node =>
{
case BooleanExpression booleanExpression:
return VisitBooleanExpression(booleanExpression);
case ScalarExpression scalarExpression:
return VisitScalarExpression(scalarExpression);
case DataTypeReference dataTypeReference:
{
var node = new QsiTypeExpressionNode
{
Identifier = IdentifierVisitor.CreateQualifiedIdentifier(dataTypeReference.Name)
};
SqlServerTree.PutFragmentSpan(node, dataTypeReference);
return node;
}
case Identifier identifier:
{
return TreeHelper.Create<QsiColumnExpressionNode>(cn =>
{
cn.Column.SetValue(new QsiColumnReferenceNode
{
Name = new QsiQualifiedIdentifier(IdentifierVisitor.CreateIdentifier(identifier))
});
SqlServerTree.PutFragmentSpan(cn, identifier);
});
}
case MultiPartIdentifier multiPartIdentifier:
{
var node = TreeHelper.Create<QsiTableExpressionNode>(tn =>
{
tn.Table.SetValue(new QsiTableReferenceNode
{
Identifier = IdentifierVisitor.CreateQualifiedIdentifier(multiPartIdentifier)
});
});
SqlServerTree.PutFragmentSpan(node, multiPartIdentifier);
return node;
}
case OverClause _:
throw TreeHelper.NotSupportedFeature("over clause");
default:
throw new InvalidOperationException();
}
})
);
node.DatePart = columnReferenceExpression.MultiPartIdentifier[0].Value;
SqlServerTree.PutFragmentSpan(node, columnReferenceExpression);
}));
parameters = parameters.Skip(1);
}
}
n.Parameters.AddRange(parameters
.Where(p => p is not null)
.Select(CreateParameterExpression));
SqlServerTree.PutFragmentSpan(n, fragment);
});
}

private static bool IsDatePartParameterFunction(string value)
{
return _datePartFunctions.Contains(value, StringComparer.OrdinalIgnoreCase);
}

private QsiExpressionNode CreateParameterExpression(TSqlFragment fragment)
{
switch (fragment)
{
case BooleanExpression booleanExpression:
return VisitBooleanExpression(booleanExpression);

case ScalarExpression scalarExpression:
return VisitScalarExpression(scalarExpression);

case DataTypeReference dataTypeReference:
{
var node = new QsiTypeExpressionNode
{
Identifier = IdentifierVisitor.CreateQualifiedIdentifier(dataTypeReference.Name)
};

SqlServerTree.PutFragmentSpan(node, dataTypeReference);

return node;
}

case Identifier identifier:
{
return TreeHelper.Create<QsiColumnExpressionNode>(cn =>
{
cn.Column.SetValue(new QsiColumnReferenceNode
{
Name = new QsiQualifiedIdentifier(IdentifierVisitor.CreateIdentifier(identifier))
});
SqlServerTree.PutFragmentSpan(cn, identifier);
});
}

case MultiPartIdentifier multiPartIdentifier:
{
var node = TreeHelper.Create<QsiTableExpressionNode>(tn =>
{
tn.Table.SetValue(new QsiTableReferenceNode
{
Identifier = IdentifierVisitor.CreateQualifiedIdentifier(multiPartIdentifier)
});
});

SqlServerTree.PutFragmentSpan(node, multiPartIdentifier);

return node;
}

case OverClause:
throw TreeHelper.NotSupportedFeature("over clause");

default:
throw new InvalidOperationException();
}
}

public string ConvertBooleanComparisonType(BooleanComparisonType booleanComparisonType)
{
return booleanComparisonType switch
Expand All @@ -939,4 +974,4 @@ public string ConvertBooleanComparisonType(BooleanComparisonType booleanComparis
_ => throw new InvalidOperationException()
};
}
}
}

0 comments on commit 33af7da

Please sign in to comment.