1
1
using System ;
2
+ using System . Collections . Generic ;
2
3
using Antlr . Runtime ;
3
4
using NHibernate . Type ;
4
5
@@ -39,9 +40,10 @@ public override void Initialize()
39
40
// some form of property ref and that the children of the in-list represent
40
41
// one-or-more params.
41
42
var lhsNode = lhs as SqlNode ;
43
+ IType lhsType = null ;
42
44
if ( lhsNode != null )
43
45
{
44
- IType lhsType = lhsNode . DataType ;
46
+ lhsType = lhsNode . DataType ;
45
47
IASTNode inListChild = inList . GetChild ( 0 ) ;
46
48
while ( inListChild != null )
47
49
{
@@ -53,6 +55,84 @@ public override void Initialize()
53
55
inListChild = inListChild . NextSibling ;
54
56
}
55
57
}
58
+
59
+ var sessionFactory = SessionFactoryHelper . Factory ;
60
+ if ( sessionFactory . Dialect . SupportsRowValueConstructorSyntaxInInList )
61
+ return ;
62
+
63
+ lhsType = lhsType ?? ExtractDataType ( lhs ) ;
64
+ if ( lhsType == null )
65
+ return ;
66
+
67
+ var rhsNode = inList . GetFirstChild ( ) ;
68
+ if ( rhsNode == null || ! IsNodeAcceptable ( rhsNode ) )
69
+ return ;
70
+
71
+ var lhsColumnSpan = lhsType . GetColumnSpan ( sessionFactory ) ;
72
+ var rhsColumnSpan = rhsNode . Type == HqlSqlWalker . VECTOR_EXPR
73
+ ? rhsNode . ChildCount
74
+ : ExtractDataType ( rhsNode ) ? . GetColumnSpan ( sessionFactory ) ?? 0 ;
75
+
76
+ if ( lhsColumnSpan > 1 && rhsColumnSpan > 1 )
77
+ {
78
+ MutateRowValueConstructorSyntaxInInListSyntax ( lhs , lhsColumnSpan , rhsNode , rhsColumnSpan ) ;
79
+ }
80
+ }
81
+
82
+ /// <summary>
83
+ /// this is possible for parameter lists and explicit lists. It is completely unreasonable for sub-queries.
84
+ /// </summary>
85
+ private static bool IsNodeAcceptable ( IASTNode rhsNode )
86
+ {
87
+ return rhsNode == null /* empty IN list */
88
+ || rhsNode is LiteralNode
89
+ || rhsNode is ParameterNode
90
+ || rhsNode . Type == HqlSqlWalker . VECTOR_EXPR ;
91
+ }
92
+
93
+ /// <summary>
94
+ /// Mutate the subtree relating to a row-value-constructor in "in" list to instead use
95
+ /// a series of ORen and ANDed predicates. This allows multi-column type comparisons
96
+ /// and explicit row-value-constructor in "in" list syntax even on databases which do
97
+ /// not support row-value-constructor in "in" list.
98
+ ///
99
+ /// For example, here we'd mutate "... where (col1, col2) in ( ('val1', 'val2'), ('val3', 'val4') ) ..." to
100
+ /// "... where (col1 = 'val1' and col2 = 'val2') or (col1 = 'val3' and val2 = 'val4') ..."
101
+ /// </summary>
102
+ private void MutateRowValueConstructorSyntaxInInListSyntax ( IASTNode lhsNode , int lhsColumnSpan , IASTNode rhsNode , int rhsColumnSpan )
103
+ {
104
+ //NHibenate specific: In hibernate they recreate new tree in HQL. In NHibernate we just replace node with generated SQL
105
+ // (same as it's done in BinaryLogicOperatorNode)
106
+
107
+ string [ ] lhsElementTexts = ExtractMutationTexts ( lhsNode , lhsColumnSpan ) ;
108
+
109
+ if ( lhsNode is ParameterNode lhsParam && lhsParam . HqlParameterSpecification != null )
110
+ {
111
+ AddEmbeddedParameter ( lhsParam . HqlParameterSpecification ) ;
112
+ }
113
+
114
+ var negated = Type == HqlSqlWalker . NOT_IN ;
115
+
116
+ var andElementsNodeList = new List < string > ( ) ;
117
+
118
+ while ( rhsNode != null )
119
+ {
120
+ string [ ] rhsElementTexts = ExtractMutationTexts ( rhsNode , rhsColumnSpan ) ;
121
+ if ( rhsNode is ParameterNode rhsParam && rhsParam . HqlParameterSpecification != null )
122
+ {
123
+ AddEmbeddedParameter ( rhsParam . HqlParameterSpecification ) ;
124
+ }
125
+
126
+ var text = Translate ( lhsColumnSpan , "=" , lhsElementTexts , rhsElementTexts ) ;
127
+
128
+ andElementsNodeList . Add ( negated ? string . Concat ( "( not " , text , ")" ) : text ) ;
129
+ rhsNode = rhsNode . NextSibling ;
130
+ }
131
+
132
+ ClearChildren ( ) ;
133
+ Type = HqlSqlWalker . SQL_TOKEN ;
134
+ var sqlToken = string . Join ( negated ? " and " : " or " , andElementsNodeList ) ;
135
+ Text = andElementsNodeList . Count > 1 ? string . Concat ( "(" , sqlToken , ")" ) : sqlToken ;
56
136
}
57
137
}
58
138
}
0 commit comments