sqlqueryparser.cs
来自「NHibernate NET开发者所需的」· CS 代码 · 共 343 行
CS
343 行
using System.Collections;
using System.Text;
using NHibernate.Engine.Query;
using NHibernate.Persister.Collection;
using NHibernate.Persister.Entity;
using NHibernate.SqlCommand;
namespace NHibernate.Loader.Custom.Sql
{
public class SQLQueryParser
{
public interface IParserContext
{
bool IsEntityAlias(string aliasName);
ISqlLoadable GetEntityPersisterByAlias(string alias);
string GetEntitySuffixByAlias(string alias);
bool IsCollectionAlias(string aliasName);
ISqlLoadableCollection GetCollectionPersisterByAlias(string alias);
string GetCollectionSuffixByAlias(string alias);
IDictionary GetPropertyResultsMapByAlias(string alias);
}
private readonly string sqlQuery;
private IParserContext context;
private readonly IDictionary namedParameters = new Hashtable();
private long aliasesFound = 0;
public SQLQueryParser(
string sqlQuery,
IParserContext context)
{
this.sqlQuery = sqlQuery;
this.context = context;
}
public IDictionary NamedParameters
{
get { return namedParameters; }
}
public bool QueryHasAliases
{
get { return aliasesFound > 0; }
}
public SqlString Process()
{
return substituteParams(SubstituteBrackets());
}
// TODO: should "record" how many properties we have reffered to - and if we
// don't get'em'all we throw an exception! Way better than trial and error ;)
private string SubstituteBrackets()
{
StringBuilder result = new StringBuilder(sqlQuery.Length + 20);
int left, right;
// replace {....} with corresponding column aliases
for (int curr = 0; curr < sqlQuery.Length; curr = right + 1)
{
if ((left = sqlQuery.IndexOf('{', curr)) < 0)
{
// No additional open braces found in the string, Append the
// rest of the string in its entirty and quit this loop
result.Append(sqlQuery.Substring(curr));
break;
}
// apend everything up until the next encountered open brace
result.Append(sqlQuery.Substring(curr, left - curr));
if ((right = sqlQuery.IndexOf('}', left + 1)) < 0)
{
throw new QueryException("Unmatched braces for alias path", sqlQuery);
}
string aliasPath = sqlQuery.Substring(left + 1, right - (left + 1));
int firstDot = aliasPath.IndexOf('.');
if (firstDot == -1)
{
if (context.IsEntityAlias(aliasPath))
{
// it is a simple table alias {foo}
result.Append(aliasPath);
aliasesFound++;
}
else
{
// passing through anything we do not know to support jdbc escape sequences HB-898
result.Append('{').Append(aliasPath).Append('}');
}
}
else
{
string aliasName = aliasPath.Substring(0, firstDot);
bool isCollection = context.IsCollectionAlias(aliasName);
bool isEntity = context.IsEntityAlias(aliasName);
if (isCollection)
{
// The current alias is referencing the collection to be eagerly fetched
string propertyName = aliasPath.Substring(firstDot + 1);
result.Append(
ResolveCollectionProperties(
aliasName,
propertyName,
context.GetPropertyResultsMapByAlias(aliasName),
context.GetEntityPersisterByAlias(aliasName),
context.GetCollectionPersisterByAlias(aliasName),
context.GetCollectionSuffixByAlias(aliasName),
context.GetEntitySuffixByAlias(aliasName)
)
);
aliasesFound++;
}
else if (isEntity)
{
// it is a property reference {foo.bar}
string propertyName = aliasPath.Substring(firstDot + 1);
result.Append(
ResolveProperties(
aliasName,
propertyName,
context.GetPropertyResultsMapByAlias(aliasName),
context.GetEntityPersisterByAlias(aliasName),
context.GetEntitySuffixByAlias(aliasName)
)
);
aliasesFound++;
}
if (!isEntity && !isCollection)
{
// passing through anything we do not know to support jdbc escape sequences HB-898
result.Append('{').Append(aliasPath).Append('}');
}
}
}
// Possibly handle :something parameters for the query ?
return result.ToString();
}
private string ResolveCollectionProperties(
string aliasName,
string propertyName,
IDictionary fieldResults,
ISqlLoadable elementPersister,
ISqlLoadableCollection currentPersister,
string suffix,
string persisterSuffix)
{
if ("*".Equals(propertyName))
{
if (fieldResults.Count > 0)
{
throw new QueryException("Using return-propertys together with * syntax is not supported.");
}
string selectFragment = currentPersister.SelectFragment(aliasName, suffix);
aliasesFound++;
return selectFragment
+ ", "
+ ResolveProperties(aliasName, propertyName, fieldResults, elementPersister, persisterSuffix);
}
else if ("element.*".Equals(propertyName))
{
return ResolveProperties(aliasName, "*", fieldResults, elementPersister, persisterSuffix);
}
else
{
string[] columnAliases;
// Let return-propertys override whatever the persister has for aliases.
columnAliases = (string[]) fieldResults[propertyName];
if (columnAliases == null)
{
columnAliases = currentPersister.GetCollectionPropertyColumnAliases(propertyName, suffix);
}
if (columnAliases == null || columnAliases.Length == 0)
{
throw new QueryException("No column name found for property [" +
propertyName +
"] for alias [" + aliasName + "]",
sqlQuery);
}
if (columnAliases.Length != 1)
{
// TODO: better error message since we actually support composites if names are explicitly listed.
throw new QueryException("SQL queries only support properties mapped to a single column - property [" +
propertyName +
"] is mapped to " +
columnAliases.Length +
" columns.",
sqlQuery);
}
aliasesFound++;
return columnAliases[0];
}
}
private string ResolveProperties(
string aliasName,
string propertyName,
IDictionary fieldResults,
ISqlLoadable currentPersister,
string suffix)
{
/*int currentPersisterIndex = getPersisterIndex( aliasName );
if ( !aliasName.Equals( aliases[currentPersisterIndex] ) ) {
throw new QueryException( "Alias [" +
aliasName +
"] does not correspond to return alias " +
aliases[currentPersisterIndex],
sqlQuery );
}*/
if ("*".Equals(propertyName))
{
if (fieldResults.Count > 0)
{
throw new QueryException("Using return-propertys together with * syntax is not supported.");
}
aliasesFound++;
return currentPersister.SelectFragment(aliasName, suffix);
}
else
{
string[] columnAliases;
// Let return-propertys override whatever the persister has for aliases.
columnAliases = (string[]) fieldResults[propertyName];
if (columnAliases == null)
{
columnAliases = currentPersister.GetSubclassPropertyColumnAliases(propertyName, suffix);
}
if (columnAliases == null || columnAliases.Length == 0)
{
throw new QueryException("No column name found for property [" +
propertyName +
"] for alias [" + aliasName + "]",
sqlQuery);
}
if (columnAliases.Length != 1)
{
// TODO: better error message since we actually support composites if names are explicitly listed.
throw new QueryException("SQL queries only support properties mapped to a single column - property [" +
propertyName +
"] is mapped to " +
columnAliases.Length +
" columns.",
sqlQuery);
}
aliasesFound++;
return columnAliases[0];
}
}
/**
* Substitues JDBC parameter placeholders (?) for all encountered
* parameter specifications. It also tracks the positions of these
* parameter specifications within the query string. This accounts for
* ordinal-params, named-params, and ejb3-positional-params.
*
* @param sqlString The query string.
* @return The SQL query with parameter substitution complete.
*/
private SqlString substituteParams(string sqlString)
{
ParameterSubstitutionRecognizer recognizer = new ParameterSubstitutionRecognizer();
ParameterParser.Parse(sqlString, recognizer);
namedParameters.Clear();
foreach (DictionaryEntry de in recognizer.namedParameterBindPoints)
{
namedParameters.Add(de.Key, de.Value);
}
return recognizer.result.ToSqlString();
}
public class ParameterSubstitutionRecognizer : ParameterParser.IRecognizer
{
internal SqlStringBuilder result = new SqlStringBuilder();
internal IDictionary namedParameterBindPoints = new Hashtable();
internal int parameterCount = 0;
public void OutParameter(int position)
{
result.Add(Parameter.Placeholder);
}
public void OrdinalParameter(int position)
{
result.Add(Parameter.Placeholder);
}
public void NamedParameter(string name, int position)
{
AddNamedParameter(name);
result.Add(Parameter.Placeholder);
}
public void Ejb3PositionalParameter(string name, int position)
{
NamedParameter(name, position);
}
public void Other(char character)
{
result.Add(new string(character, 1));
}
private void AddNamedParameter(string name)
{
int loc = parameterCount++;
object o = namedParameterBindPoints[name];
if (o == null)
{
namedParameterBindPoints[name] = loc;
}
else if (o is int)
{
ArrayList list = new ArrayList(4);
list.Add(o);
list.Add(loc);
namedParameterBindPoints[name] = list;
}
else
{
((IList) o).Add(loc);
}
}
}
}
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?