📄 querytranslator.cs
字号:
/// <summary>
/// WARNING: side-effecty
/// </summary>
private SqlString RenderScalarSelect()
{
bool isSubselect = superQuery != null;
SqlStringBuilder buf = new SqlStringBuilder();
if (scalarTypes.Count == 0)
{
//ie. no select clause
int size = returnedTypes.Count;
for (int k = 0; k < size; k++)
{
scalarTypes.Add(NHibernateUtil.Entity(persisters[k].EntityName));
string[] _names = persisters[k].IdentifierColumnNames;
for (int i = 0; i < _names.Length; i++)
{
buf.Add(returnedTypes[k].ToString()).Add(StringHelper.Dot.ToString()).Add(_names[i]);
if (!isSubselect)
{
buf.Add(" as ").Add(ScalarName(k, i));
}
if (i != _names.Length - 1 || k != size - 1)
{
buf.Add(StringHelper.CommaSpace);
}
}
}
}
else
{
//there _was_ a select clause
int c = 0;
bool nolast = false; //real hacky...
int parenCount = 0; // used to count the nesting of parentheses
for (int tokenIdx = 0; tokenIdx < scalarSelectTokens.Count; tokenIdx++)
{
SqlString next = scalarSelectTokens[tokenIdx];
if (next.Count == 1)
{
string token = next.ToString();
string lc = token.ToLowerInvariant();
ISQLFunction func = Factory.SQLFunctionRegistry.FindSQLFunction(lc);
if (func != null)
{
// Render the HQL function
SqlString renderedFunction = RenderFunctionClause(func, scalarSelectTokens, ref tokenIdx);
buf.Add(renderedFunction);
}
else
{
if (StringHelper.OpenParen.Equals(token))
{
parenCount++;
}
else if (StringHelper.ClosedParen.Equals(token))
{
parenCount--;
}
else if (lc.Equals(StringHelper.CommaSpace))
{
if (nolast)
{
nolast = false;
}
else
{
if (!isSubselect && parenCount == 0)
{
buf.Add(" as ").Add(ScalarName(c++, 0));
}
}
}
buf.Add(token);
if (lc.Equals("distinct") || lc.Equals("all"))
{
buf.Add(" ");
}
}
}
else
{
nolast = true;
int i = 0;
foreach (object token in next.Parts)
{
buf.AddObject(token);
if (!isSubselect)
{
buf.Add(" as ").Add(ScalarName(c, i));
}
if (i != next.Count - 1)
{
buf.Add(StringHelper.CommaSpace);
}
i++;
}
c++;
}
}
if (!isSubselect && !nolast)
{
buf.Add(" as ").Add(ScalarName(c++, 0));
}
}
return buf.ToSqlString();
}
private void RenderFunctions(IList<SqlString> tokens)
{
for (int tokenIdx = 0; tokenIdx < tokens.Count; tokenIdx++)
{
string token = tokens[tokenIdx].ToString().ToLowerInvariant();
ISQLFunction func = Factory.SQLFunctionRegistry.FindSQLFunction(token);
if (func != null)
{
int flTokenIdx = tokenIdx;
SqlString renderedFunction = RenderFunctionClause(func, tokens, ref flTokenIdx);
// At this point we have the trunk that represents the function with its
// arguments enclosed in parens. Now all token in the tokens list will be
// removed from the original list and replaced with the rendered function.
for (int i = 0; i < flTokenIdx - tokenIdx; i++)
{
tokens.RemoveAt(tokenIdx + 1);
}
tokens[tokenIdx] = new SqlString(renderedFunction);
}
}
}
/// <summary>
/// Extract the complete clause of function.
/// </summary>
/// <param name="tokens">The list of tokens</param>
/// <param name="tokenIdx">The index of the list that represent the founded function.</param>
/// <returns>String trepresentation of each token.</returns>
/// <remarks>Each token can be string or SqlString </remarks>
private IList<SqlString> ExtractFunctionClause(IList<SqlString> tokens, ref int tokenIdx)
{
SqlString funcName = tokens[tokenIdx];
IList<SqlString> functionTokens = new List<SqlString>();
functionTokens.Add(funcName);
tokenIdx++;
if (tokenIdx >= tokens.Count ||
!StringHelper.OpenParen.Equals(tokens[tokenIdx].ToString()))
{
// All function with arguments have the syntax
// <function name> <left paren> <arguments> <right paren>
throw new QueryException("'(' expected after function " + funcName);
}
functionTokens.Add(new SqlString(StringHelper.OpenParen));
tokenIdx++;
int parenCount = 1;
for (; tokenIdx < tokens.Count && parenCount > 0; tokenIdx++)
{
if (tokens[tokenIdx].StartsWithCaseInsensitive(ParserHelper.HqlVariablePrefix) || tokens[tokenIdx].ToString().Equals(StringHelper.SqlParameter))
{
functionTokens.Add(SqlString.Parameter);
}
else
{
functionTokens.Add(tokens[tokenIdx]);
}
if (StringHelper.OpenParen.Equals(tokens[tokenIdx].ToString()))
{
parenCount++;
}
else if (StringHelper.ClosedParen.Equals(tokens[tokenIdx].ToString()))
{
parenCount--;
}
}
tokenIdx--; // position of the last managed token
if (parenCount > 0)
{
throw new QueryException("')' expected for function " + funcName);
}
return functionTokens;
}
private SqlString RenderFunctionClause(ISQLFunction func, IList<SqlString> tokens, ref int tokenIdx)
{
IList<SqlString> functionTokens;
if (!func.HasArguments)
{
// The function doesn't work with arguments.
if (func.HasParenthesesIfNoArguments)
ExtractFunctionClause(tokens, ref tokenIdx);
// The function render simply translate its name for a specific dialect.
return func.Render(new ArrayList(), Factory);
}
functionTokens = ExtractFunctionClause(tokens, ref tokenIdx);
IFunctionGrammar fg = func as IFunctionGrammar;
if (fg == null)
fg = new CommonGrammar();
IList args = new ArrayList();
SqlStringBuilder argBuf = new SqlStringBuilder();
// Extract args spliting first 2 token because are: FuncName(
// last token is ')'
// To allow expressions like arg (ex:5+5) all tokens between 'argument separator' or
// a 'know argument' are compacted in a string,
// because many HQL function expect IList<string> like args in Render method.
// This solution give us the ability to use math expression in common function.
// Ex: sum(a.Prop+10), cast(yesterday-1 as date)
for (int argIdx = 2; argIdx < functionTokens.Count - 1; argIdx++)
{
object token = functionTokens[argIdx];
if (fg.IsKnownArgument(token.ToString()))
{
if (argBuf.Count > 0)
{
// end of the previous argument
args.Add(argBuf.ToSqlString());
argBuf = new SqlStringBuilder();
}
args.Add(token);
}
else if (fg.IsSeparator(token.ToString()))
{
// argument end
if (argBuf.Count > 0)
{
args.Add(argBuf.ToSqlString());
argBuf = new SqlStringBuilder();
}
}
else
{
ISQLFunction nfunc = Factory.SQLFunctionRegistry.FindSQLFunction(token.ToString().ToLowerInvariant());
if (nfunc != null)
{
// the token is a nested function call
argBuf.Add(RenderFunctionClause(nfunc, functionTokens, ref argIdx));
}
else
{
// the token is a part of an argument (every thing else)
argBuf.AddObject(token);
}
}
}
// Add the last arg
if (argBuf.Count > 0)
args.Add(argBuf.ToSqlString());
return func.Render(args, Factory);
}
private class Selector : JoinSequence.ISelector
{
private QueryTranslator outer;
public Selector(QueryTranslator outer)
{
this.outer = outer;
}
public bool IncludeSubclasses(string alias)
{
bool include = outer.returnedTypes.Contains(alias) && !outer.IsShallowQuery;
return include;
}
}
private void MergeJoins(JoinFragment ojf)
{
foreach (KeyValuePair<string, JoinSequence> de in joins)
{
string name = de.Key;
JoinSequence join = de.Value;
join.SetSelector(new Selector(this));
if (typeMap.ContainsKey(name))
{
ojf.AddFragment(join.ToJoinFragment(enabledFilters, true));
}
else if (collections.ContainsKey(name))
{
ojf.AddFragment(join.ToJoinFragment(enabledFilters, true));
}
else
{
//name from a super query (a bit inelegant that it shows up here)
}
}
}
public ISet<string> QuerySpaces
{
get { return querySpaces; }
}
/// <summary>
/// Is this query called by Scroll() or Iterate()?
/// </summary>
/// <value>true if it is, false if it is called by find() or list()</value>
public bool IsShallowQuery
{
get { return shallowQuery; }
}
internal bool Distinct
{
set { distinct = value; }
}
/// <summary></summary>
public bool IsSubquery
{
get { return superQuery != null; }
}
protected override ICollectionPersister[] CollectionPersisters
{
get { return fetchedCollections.CollectionPersisters; }
}
protected override string[] CollectionSuffixes
{
get { return fetchedCollections.CollectionSuffixes; }
set { throw new InvalidOperationException("QueryTranslator.CollectionSuffixes_set"); }
}
public void AddCollectionToFetch(string role, string name, string ownerName, string entityName)
{
IQueryableCollection persister = GetCollectionPersister(role);
fetchedCollections.Add(name, persister, ownerName);
if (persister.ElementType.IsEntityType)
{
AddEntityToFetch(entityName);
}
}
protected override string[] Suffixes
{
get { return suffixes; }
set { suffixes = value; }
}
/// <remarks>Used for collection filters</remarks>
protected void AddFromAssociation(string elementName, string collectionRole)
{
//q.addCollection(collectionName, collectionRole);
IType collectionElementType = GetCollectionPersister(collectionRole).ElementType;
if (!collectionElementType.IsEntityType)
{
throw new QueryException("collection of values in filter: " + elementName);
}
IQueryableCollection persister = GetCollectionPersister(collectionRole);
string[] keyColumnNames = persister.KeyColumnNames;
//if (keyColumnNames.Length!=1) throw new QueryException("composite-key collecion in filter: " + collectionRole);
string collectionName;
JoinSequence join = new JoinSequence(Factory);
collectionName = persister.IsOneToMany ? elementName : CreateNameForCollection(collectionRole);
join.SetRoot(persister, collectionName);
if (!persister.IsOneToMany)
{
//many-to-many
AddCollection(collectionName, collectionRole);
try
{
join.AddJoin(
(IAssociationType) persister.ElementType,
elementName,
JoinType.InnerJoin,
persister.GetElementColumnNames(collectionName));
}
catch (MappingException me)
{
throw new QueryException(me);
}
}
join.AddCondition(collectionName, keyColumnNames, " = ", true);
EntityType elmType = (EntityType) collectionElementType;
AddFrom(elementName, elmType.GetAssociatedEntityName(), join);
}
internal string GetPathAlias(string path)
{
string result;
pathAliases.TryGetValue(path, out result);
return result;
}
internal JoinSequence GetPathJoin(string path)
{
JoinSequence result;
pathJoins.TryGetValue(path, out result);
return result;
}
internal void AddPathAliasAndJoin(string path, string alias, JoinSequence joinSequence)
{
pathAliases.Add(path, alias);
pathJoins.Add(path, joinSequence);
}
public IList List(ISessionImplementor session, QueryParameters queryParameters)
{
return List(session, queryParameters, QuerySpaces, actualReturnTypes);
}
public IEnumerable GetEnumerable(QueryParameters parameters, ISessionImplementor session)
{
bool stats = session.Factory.Statistics.IsStatisticsEnabled;
long startTime = 0;
if (stats)
startTime = DateTime.Now.Ticks;
IDbCommand cmd = PrepareQueryCommand(parameters, false, session);
// This IDataReader is disposed of in EnumerableImpl.Dispose
IDataReader rs = GetResultSet(cmd, parameters.HasAutoDiscoverScalarTypes, false, parameters.RowSelection, session);
HolderInstantiator hi =
HolderInstantiator.CreateClassicHolderInstantiator(holderConstructor, parameters.ResultTransformer);
IEnumerable result =
new EnumerableImpl(rs, cmd, session, ReturnTypes, ScalarColumnNames, parameters.RowSelection, hi);
if (stats)
{
session.Factory.StatisticsImplementor.QueryExecuted("HQL: " + queryString, 0, (DateTime.Now.Ticks - startTime));
// NH: Different behavior (H3.2 use QueryLoader in AST parser) we need statistic for orginal query too.
// probably we have a bug some where else for statistic RowCount
session.Factory.StatisticsImplementor.QueryExecuted(QueryIdentifier, 0, (DateTime.Now.Ticks - startTime));
}
return result;
}
public static string[] ConcreteQueries(string query, ISessionFactoryImplementor factory)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -