forked from nhibernate/nhibernate-core
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathQuerySplitter.cs
120 lines (108 loc) · 3.73 KB
/
QuerySplitter.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
using System;
using System.Collections.Generic;
using System.Text;
using NHibernate.Engine;
using NHibernate.Hql.Util;
using NHibernate.Util;
namespace NHibernate.Hql
{
public class QuerySplitter
{
private static readonly INHibernateLogger log = NHibernateLogger.For(typeof(QuerySplitter));
private static readonly HashSet<string> beforeClassTokens = new HashSet<string>();
private static readonly HashSet<string> notAfterClassTokens = new HashSet<string>();
static QuerySplitter()
{
beforeClassTokens.Add("from");
beforeClassTokens.Add("delete");
beforeClassTokens.Add("update");
//beforeClassTokens.Add("new"); DEFINITELY DON'T HAVE THIS!! (form H3.2)
beforeClassTokens.Add(",");
beforeClassTokens.Add("join");
notAfterClassTokens.Add("in");
//notAfterClassTokens.Add(",");
notAfterClassTokens.Add("from");
notAfterClassTokens.Add(")");
}
/// <summary>
/// Handle Hibernate "implicit" polymorphism, by translating the query string into
/// several "concrete" queries against mapped classes.
/// </summary>
/// <param name="query"></param>
/// <param name="factory"></param>
/// <returns></returns>
/// <exception cref="NHibernate.MappingException"/>
public static string[] ConcreteQueries(string query, ISessionFactoryImplementor factory)
{
//scan the query string for class names appearing in the from clause and replace
//with all persistent implementors of the class/interface, returning multiple
//query strings (make sure we don't pick up a class in the select clause!)
//TODO: this is one of the ugliest and most fragile pieces of code in Hibernate....
SessionFactoryHelper helper = new SessionFactoryHelper(factory);
string[] tokens = StringHelper.Split(StringHelper.WhiteSpace + "(),", query, true);
if (tokens.Length == 0)
{
return new String[] {query}; // just especially for the trivial collection filter
}
var placeholders = new List<object>();
var replacements = new List<object>();
StringBuilder templateQuery = new StringBuilder(40);
int count = 0;
string last = null;
int nextIndex = 0;
string next = null;
templateQuery.Append(tokens[0]);
for (int i = 1; i < tokens.Length; i++)
{
//update last non-whitespace token, if necessary
if (!ParserHelper.IsWhitespace(tokens[i - 1]))
{
last = tokens[i - 1].ToLowerInvariant();
}
string token = tokens[i];
if (!ParserHelper.IsWhitespace(token) || last == null)
{
//scan for next non-whitespace token
if (nextIndex <= i)
{
for (nextIndex = i + 1; nextIndex < tokens.Length; nextIndex++)
{
next = tokens[nextIndex].ToLowerInvariant();
if (!ParserHelper.IsWhitespace(next))
{
break;
}
}
}
// TODO H3.2 Different behavior
// NHb: This block is not an exactly port from H3.2 but a port from previous implementation of QueryTranslator
if (((last != null && beforeClassTokens.Contains(last)) &&
(next == null || !notAfterClassTokens.Contains(next))) ||
ParserHelper.EntityClass.Equals(last))
{
System.Type clazz = helper.GetImportedClass(token);
if (clazz != null)
{
string[] implementors = factory.GetImplementors(clazz.FullName);
string placeholder = "$clazz" + count++ + "$";
if (implementors != null)
{
placeholders.Add(placeholder);
replacements.Add(implementors);
}
token = placeholder; //Note this!!
}
}
}
templateQuery.Append(token);
}
string[] results =
StringHelper.Multiply(templateQuery.ToString(), placeholders, replacements);
if (results.Length == 0)
{
log.Warn("no persistent classes found for query class: {0}", query);
}
return results;
}
}
}