public static IDbSelect Map( IDbSelect dbSelect, Type returnType, IModelInfoProvider infoProvider, IDbObjectFactory dbFactory, UniqueNameGenerator nameGenerator) { // If the select returns specified columns, it means there is a constructor in Select, // e.g (.Select(d => new { A = d.ABC })) // In this case we do not need to add alias to the end result as where will be one in the select if (dbSelect.Selection.Any(s => !(s is IDbRefColumn))) { return(dbSelect); } var entityInfo = infoProvider.FindEntityInfo(returnType); if (!entityInfo.RequirePropertyNameMapping()) { return(dbSelect); } var alias = nameGenerator.GenerateAlias(dbSelect, TranslationConstants.SubSelectPrefix, true); var newSelectRef = dbFactory.BuildRef(dbSelect, alias); var newSelect = dbFactory.BuildSelect(newSelectRef); foreach (var fieldInfo in entityInfo.Columns) { var column = dbFactory.BuildColumn( newSelectRef, fieldInfo.DbName, fieldInfo.ValType, fieldInfo.PropertyName); newSelect.Selection.Add(column); } return(newSelect); }
public IDbSelect GetLastSelect() { IDbSelect dbSelect = null; var results = new Stack <IDbObject>(); while (ResultStack.Count > 0) { var dbObject = ResultStack.Pop(); results.Push(dbObject); dbSelect = dbObject as IDbSelect; if (dbSelect != null) { break; } } while (results.Count > 0) { ResultStack.Push(results.Pop()); } return(dbSelect); }
private static IDbSelectable CreateNewSelectableForWrappingSelect( IDbSelect dbSelect, IDbSelectable selectable, DbReference dbRef, Expression m, IDbObjectFactory dbFactory, UniqueNameGenerator nameGenerator) { if (dbRef == null) { return(selectable); } var oCol = selectable as IDbColumn; if (oCol != null) { return(dbFactory.BuildColumn(dbRef, oCol.GetAliasOrName(), oCol.ValType)); } var oRefCol = selectable as IDbRefColumn; if (oRefCol != null) { return(dbFactory.BuildRefColumn(dbRef, oRefCol.Alias, oRefCol)); } if (selectable is IDbFunc oDbFunc) { if (string.IsNullOrEmpty(oDbFunc.Alias)) { oDbFunc.Alias = nameGenerator.GenerateAlias(dbSelect, oDbFunc.Name, true); } return(dbFactory.BuildColumn(dbRef, oDbFunc.Alias, oDbFunc.ReturnType)); } return(dbFactory.BuildColumn(dbRef, selectable.Alias, typeof(string))); }
private void UpdateSelection(IDbSelect fromSelect, IDbObject selection, DbReference toSelectRef) { var dbList = selection as IEnumerable <DbKeyValue>; if (dbList != null) { foreach (var dbObj in dbList) { UpdateSelection(fromSelect, dbObj, toSelectRef); } return; } var keyValue = selection as DbKeyValue; selection = keyValue != null ? keyValue.Value : selection; var selectable = GetSelectable(fromSelect, selection, toSelectRef); if (keyValue != null) { selectable.Alias = keyValue.Key; } fromSelect.Selection.Add(selectable); }
protected void CreateAggregation( MethodCallExpression m, TranslationState state, UniqueNameGenerator nameGenerator, IDbSelect childSelect, IDbFunc dbFunc) { var dbSelect = (IDbSelect)state.ResultStack.Peek(); if (childSelect == null) { state.ResultStack.Push(dbFunc); return; } var alias = nameGenerator.GenerateAlias(dbSelect, dbFunc.Name, true); dbFunc.Alias = alias; childSelect.Selection.Add(dbFunc); var cRef = dbSelect.Joins.Single(j => ReferenceEquals(j.To.Referee, childSelect)).To; var column = _dbFactory.BuildColumn(cRef, alias, m.Method.ReturnType); var dbDefaultVal = _dbFactory.BuildConstant(Activator.CreateInstance(m.Method.ReturnType)); var dbIsNullFunc = _dbFactory.BuildNullCheckFunc(column, dbDefaultVal); state.ResultStack.Push(dbIsNullFunc); }
public static IDbSelect Optimize(IDbSelect dbSelect) { dbSelect = UnwrapUnneededSelect(dbSelect); RemoveUnneededSelectAllColumn(dbSelect); MergeChildJoins(dbSelect); return(dbSelect); }
public static void RemoveUnneededSelectAllColumn(IDbSelect dbSelect, bool subSelectOnly = true) { if (!subSelectOnly) { var selectAllColumns = dbSelect.Selection.OfType <IDbRefColumn>().ToArray(); foreach (var refColumn in selectAllColumns) { dbSelect.Selection.Remove(refColumn); } } var subSelect = dbSelect.From?.Referee as IDbSelect; if (subSelect != null) { RemoveUnneededSelectAllColumn(subSelect, false); } foreach (var dbJoin in dbSelect.Joins) { subSelect = dbJoin.To.Referee as IDbSelect; if (subSelect != null) { RemoveUnneededSelectAllColumn(subSelect, false); } } }
private static IDbJoin MakeJoin(IDbSelect ownerSelect, IDbObject tempSelect, IDbObjectFactory dbFactory, UniqueNameGenerator nameGenerator) { var joinAlias = nameGenerator.GenerateAlias(ownerSelect, TranslationConstants.SubSelectPrefix); var joinTo = dbFactory.BuildRef(tempSelect, joinAlias); return(dbFactory.BuildJoin(joinTo, ownerSelect)); }
public string GenerateAlias(IDbSelect dbSelect, string name, bool fullName = false) { name = name.StartsWith("#") ? name.Remove(0, 1) : name; var alias = fullName ? name : name.Substring(0, 1).ToLower(); int count; if (dbSelect == null) { count = _globalUniqueAliasNames.ContainsKey(alias) ? ++_globalUniqueAliasNames[alias] : _globalUniqueAliasNames[alias] = 0; } else { var uniqueNames = _uniqueAliasNames.ContainsKey(dbSelect) ? _uniqueAliasNames[dbSelect] : _uniqueAliasNames[dbSelect] = new Dictionary <string, int>(); count = uniqueNames.ContainsKey(alias) ? ++uniqueNames[alias] : uniqueNames[alias] = 0; } return($"{alias}{count}"); }
public static void UpdateWhereClause(this IDbSelect dbSelect, IDbBinary whereClause, IDbObjectFactory dbFactory) { if (whereClause == null) { return; } dbSelect.Where = dbSelect.Where.UpdateBinary(whereClause, dbFactory); }
public override IDbTempTable BuildTempTable(string tableName, IDbSelect sourceSelect = null) { return(new SqliteTempTable { TableName = tableName, SourceSelect = sourceSelect, OutputOption = OutputOption }); }
public virtual IDbTempTable BuildTempTable(string tableName, IDbSelect sourceSelect = null) { return(new SqlTempTable { TableName = tableName, SourceSelect = sourceSelect, OutputOption = OutputOption }); }
public static void MergeChildJoins(IDbSelect dbSelect) { var joins = dbSelect.Joins.Where(j => j.To.Referee is IDbSelect).ToArray(); if (joins.Length == 0) { return; } foreach (var dbJoin in joins) { MergeChildJoins((IDbSelect)dbJoin.To.Referee); } var colDict = dbSelect.Selection. SelectMany(s => s.GetDbObjects <IDbColumn>()). Distinct(). ToDictionary(s => s.GetAliasOrName(), s => s); var selectDict = new Dictionary <string, IDbJoin>(); foreach (var dbJoin in joins) { var childSelect = (IDbSelect)dbJoin.To.Referee; var hashKey = childSelect.ToMergeKey(); if (selectDict.ContainsKey(hashKey)) { var mergedJoin = selectDict[hashKey]; var mergedSelect = (IDbSelect)mergedJoin.To.Referee; var colLookUp = mergedSelect.Selection.ToLookup(s => s.GetAliasOrName()); var columns = childSelect.Selection.Where(s => !colLookUp.Contains(s.GetAliasOrName())); foreach (var selectable in columns) { var column = colDict[selectable.GetAliasOrName()]; column.Ref = mergedJoin.To; mergedSelect.Selection.Add(selectable); } } else { selectDict[hashKey] = dbJoin; } } var lookUp = selectDict.Values.ToLookup(v => v); foreach (var dbJoin in joins) { if (!lookUp.Contains(dbJoin)) { dbSelect.Joins.Remove(dbJoin); } } }
private void ReLinkToChildSelect(IDbSelect dbSelect, IDbSelect childSelect) { var joinToRelink = dbSelect.Joins.SingleOrDefault(j => ReferenceEquals(j.To.Referee, childSelect.From.Referee)); if (joinToRelink == null) { return; } joinToRelink.To = _dbFactory.BuildRef(childSelect, joinToRelink.To.Alias); }
public override IDbTempTable BuildTempTable(string tableName, IDbSelect sourceSelect = null) { var sqlTable = new MySqlTempTable { TableName = tableName, SourceSelect = sourceSelect, OutputOption = OutputOption }; return(sqlTable); }
private static void UpdateIncludeSelectAndProcessToNodes( IncludeNode graphNode, IDbSelect includedSelect, IModelInfoProvider infoProvider, IDbObjectFactory dbFactory, UniqueNameGenerator nameGenerator, AbstractMethodTranslator[] addons) { // create temp table var entityRef = includedSelect.GetReturnEntityRef(); var returnTable = (IDbTable)entityRef.Referee; var newIncludedSelect = dbFactory.BuildSelect(returnTable); newIncludedSelect.From.Alias = nameGenerator.GenerateAlias(newIncludedSelect, returnTable.TableName); var tempTableName = TranslationConstants.TempTablePreix + nameGenerator.GenerateAlias(null, returnTable.TableName, true); var tempTable = dbFactory.BuildTempTable(tempTableName, includedSelect); var tempSelect = dbFactory.BuildSelect(tempTable); tempSelect.From.Alias = nameGenerator.GenerateAlias(tempSelect, tempTable.TableName); var joinToTemp = MakeJoin(newIncludedSelect, tempSelect, dbFactory, nameGenerator); var joinTo = joinToTemp.To; newIncludedSelect.Joins.Add(joinToTemp); foreach (var pk in returnTable.PrimaryKeys) { var fromPkCol = dbFactory.BuildColumn(entityRef, pk.Name, pk.ValType); includedSelect.Selection.Add(fromPkCol); var toPkCol = dbFactory.BuildColumn(tempSelect.From, pk.Name, pk.ValType); tempSelect.Selection.Add(toPkCol); tempSelect.GroupBys.Add(toPkCol); toPkCol = dbFactory.BuildColumn(joinTo, pk.Name, pk.ValType); var binary = dbFactory.BuildBinary(fromPkCol, DbOperator.Equal, toPkCol); joinToTemp.Condition = joinToTemp.Condition.UpdateBinary(binary, dbFactory); } var orderCol = dbFactory.BuildColumn(tempSelect.From, tempTable.RowNumberColumnName, typeof(int)); tempSelect.Selection.Add(orderCol); orderCol = dbFactory.BuildColumn(joinTo, tempTable.RowNumberColumnName, typeof(int)); newIncludedSelect.OrderBys.Add(orderCol); graphNode.Select = newIncludedSelect; graphNode.TempTable = tempTable; foreach (var toNode in graphNode.ToNodes) { TranslateGraphNode(toNode, infoProvider, dbFactory, nameGenerator, addons); } }
/// <summary> Translate to SQL </summary> public override void Translate( MethodCallExpression m, TranslationState state, UniqueNameGenerator nameGenerator) { var dbExpression = GetAggregationTarget(m, state); IDbSelect childSelect = null; if (!m.GetCaller().Type.IsGrouping()) { childSelect = state.ResultStack.Pop() as IDbSelect; } // if the aggregation does not have expression, it means // the caller of the aggregation method must be a Select method call // In this case, the actual expression that we need to aggregate on, // will be inside the child select statement if (dbExpression == null && childSelect != null) { // to get the actual expression, we need to first unwrap the child select // because the translation of Select call always wrap the actual select childSelect = (IDbSelect)childSelect.From.Referee; dbExpression = childSelect.Selection.Last( s => string.IsNullOrEmpty(s.Alias) || !s.Alias.EndsWith(TranslationConstants.JoinKeySuffix)); childSelect.Selection.Remove(dbExpression); childSelect.GroupBys.Remove(dbExpression); } if (dbExpression == null) { throw new NotSupportedException("Can not aggregate."); } if (!(dbExpression is IDistinctable)) { throw new NotSupportedException("Expression must be Distinctable"); } var distinctable = (IDistinctable)dbExpression; distinctable.IsDistinct = true; var dbFunc = _dbFactory.BuildFunc("count", true, m.Method.ReturnType, dbExpression); CreateAggregation(m, state, nameGenerator, childSelect, dbFunc); }
public IDbJoin BuildJoin( DbReference joinTo, IDbSelect dbSelect, IDbBinary condition = null, DbJoinType dbJoinType = DbJoinType.Inner) { var dbJoin = new SqlJoin { To = joinTo, Condition = condition, Type = dbJoinType }; joinTo.OwnerSelect = dbSelect; joinTo.OwnerJoin = dbJoin; return(dbJoin); }
public override void Translate( MethodCallExpression m, TranslationState state, UniqueNameGenerator nameGenerator) { var predicate = BuildCondition(m, state); IDbSelect childSelect = null; if (!m.GetCaller().Type.IsGrouping()) { childSelect = state.ResultStack.Pop() as IDbSelect; } var dbCountFunc = _dbFactory.BuildFunc(m.Method.Name.ToLower(), true, m.Method.ReturnType, predicate); CreateAggregation(m, state, nameGenerator, childSelect, dbCountFunc); }
protected void CreateAggregation( MethodCallExpression m, TranslationState state, UniqueNameGenerator nameGenerator, IDbSelect childSelect, IDbFunc dbFunc) { var dbSelect = (IDbSelect)state.ResultStack.Peek(); if (childSelect == null) { state.ResultStack.Push(dbFunc); return; } var alias = nameGenerator.GenerateAlias(dbSelect, dbFunc.Name, true); dbFunc.Alias = alias; childSelect.Selection.Add(dbFunc); /** * Aggregation can happen after a method that generate new select. * In this case, join from the main select to the child select will not be * updated yet at this stage, so we need to correct the join to on the * correct child select statment. * For example: * var query = db.Blogs * .Where(b => b.BlogId > 0) * .Select(b => new * { * b.BlogId, * Cnt = b.Posts.Select(p => p.Title).Distinct().Count() * }); * `b.Posts.Select(p => p.Title).Distinct()` will create a new child select and * it will not be the one that the main select is currently targeting, so we * need to correct the join target. */ ReLinkToChildSelect(dbSelect, childSelect); var cRef = dbSelect.Joins.Single(j => ReferenceEquals(j.To.Referee, childSelect)).To; var column = _dbFactory.BuildColumn(cRef, alias, m.Method.ReturnType); var dbDefaultVal = _dbFactory.BuildConstant(Activator.CreateInstance(m.Method.ReturnType)); var dbIsNullFunc = _dbFactory.BuildNullCheckFunc(m.Method.ReturnType, column, dbDefaultVal); state.ResultStack.Push(dbIsNullFunc); }
private IDbSelectable GetSelectable(IDbSelect fromSelect, IDbObject selection, DbReference toSelectRef) { var dbRef = selection as DbReference; if (dbRef != null) { IDbRefColumn toRefCol = null; if (dbRef.OwnerSelect != fromSelect) { var toSelect = (IDbSelect)toSelectRef.Referee; toRefCol = _dbFactory.BuildRefColumn(dbRef); toSelect.Selection.Add(toRefCol); dbRef = toSelectRef; } var refColumn = _dbFactory.BuildRefColumn(dbRef); refColumn.RefTo = toRefCol; return(refColumn); } var column = selection as IDbColumn; if (column != null) { if (column.Ref.OwnerSelect != fromSelect) { var toSelect = (IDbSelect)toSelectRef.Referee; toSelect.Selection.Add(column); column = _dbFactory.BuildColumn(column); column.Ref = toSelectRef; } return(column); } throw new NotSupportedException(); }
public static IDbSelect UnwrapUnneededSelect(IDbSelect dbSelect) { while (true) { if (!CanUnwrapSelect(dbSelect)) { return(dbSelect); } var unwrapedSelect = (IDbSelect)dbSelect.From.Referee; if (!dbSelect.Selection.Any()) { dbSelect = unwrapedSelect; continue; } // re add columns with outer select's order // there are two ways to lookup the selectable in the unwraped select // for normally column, we can use the name the column to search aginst the // alais name in the unwrapped select // for RefColumn, we can use the RefTo property to look for the column it refers to var colDict = unwrapedSelect.Selection. Where(s => s.GetAliasOrName() != null). ToDictionary(s => s.GetAliasOrName(), s => s); unwrapedSelect.Selection.Clear(); foreach (var selectable in dbSelect.Selection) { var innnerCol = (selectable as IDbRefColumn)?.RefTo ?? colDict[selectable.GetNameOrAlias()]; innnerCol.Alias = selectable.GetAliasOrName(); unwrapedSelect.Selection.Add(innnerCol); } unwrapedSelect.Selection.IsDistinct = dbSelect.Selection.IsDistinct; dbSelect = unwrapedSelect; } }
private static void UpdateFromNodeTempTable( IncludeNode fromNode, IDbObject dbObject, IDbSelect includedSelect, IDbObjectFactory dbFactory, UniqueNameGenerator nameGenerator) { var fromSelect = fromNode.Select; var dbJoin = fromSelect.Joins.Single( j => dbObject is DbReference ? ReferenceEquals(j.To, dbObject) : ReferenceEquals(j.To.Referee, dbObject)); // remove the join to included relation from fromSelect fromSelect.Joins.Remove(dbJoin); if (dbObject is IDbSelect) { var refCol = includedSelect.Selection.Single(c => c is IDbRefColumn); includedSelect.Selection.Remove(refCol); } var keys = dbJoin.Condition.GetDbObjects <IDbColumn>().ToArray(); var fromKeys = keys.Where(c => !ReferenceEquals(c.Ref, dbJoin.To)).ToArray(); var toKeys = keys.Where(c => ReferenceEquals(c.Ref, dbJoin.To)).ToArray(); var tempTable = fromNode.TempTable; var sourceSelect = tempTable.SourceSelect; var fromRef = sourceSelect.GetReturnEntityRef(); var returnRef = includedSelect.GetReturnEntityRef(); var tempSelect = dbFactory.BuildSelect(tempTable); tempSelect.From.Alias = nameGenerator.GenerateAlias(tempSelect, tempTable.TableName); var joinToTemp = MakeJoin(includedSelect, tempSelect, dbFactory, nameGenerator); includedSelect.Joins.Add(joinToTemp); for (var i = 0; i < fromKeys.Length; i++) { var fromKey = fromKeys[i]; var toKey = toKeys[i]; var fromPkCol = dbFactory.BuildColumn(fromRef, fromKey.Name, fromKey.ValType); sourceSelect.Selection.Add(fromPkCol); var tempPkCol = dbFactory.BuildColumn(tempSelect.From, fromKey.Name, fromKey.ValType); tempSelect.Selection.Add(tempPkCol); tempSelect.GroupBys.Add(tempPkCol); if (dbObject is IDbSelect) { var key = toKey; toKey = (IDbColumn)includedSelect.Selection.Single(c => c.GetAliasOrName() == key.GetNameOrAlias()); includedSelect.Selection.Remove(toKey); includedSelect.GroupBys.Remove(toKey); } fromPkCol = dbFactory.BuildColumn(joinToTemp.To, fromKey.Name, fromKey.ValType); var toPkCol = dbFactory.BuildColumn(returnRef, toKey.Name, toKey.ValType); var binary = dbFactory.BuildBinary(toPkCol, DbOperator.Equal, fromPkCol); joinToTemp.Condition = joinToTemp.Condition.UpdateBinary(binary, dbFactory); } }
public static DbReference GetReturnEntityRef(this IDbSelect dbSelect) { var entityRefCol = dbSelect.Selection.SingleOrDefault(c => c is IDbRefColumn); return(entityRefCol != null ? entityRefCol.Ref : dbSelect.From); }
private static bool CanUnwrapSelect(IDbSelect dbSelect) { return(dbSelect.From.Referee is IDbSelect && dbSelect.Where == null && !dbSelect.Joins.Any() && !dbSelect.OrderBys.Any() && !dbSelect.GroupBys.Any() && dbSelect.Selection.All(s => s is IDbColumn || s is IDbRefColumn)); }
public DbSelectableCollection(IDbSelect owner) { _owner = owner; }
/// Create a join for the relation /// For parent relation, we create a join that joins to the parent table /// For child relation, we will create a sub select that returns the child table, /// and then joins to the sub select. /// The reason for joining to sub select for child relation, is that we want to be /// able to group on the join key, so that we will not repeat the parent row. private IDbJoin GetOrCreateJoin(EntityRelation relation, DbReference fromRef, IDbRefColumn refCol) { var dbSelect = fromRef.OwnerSelect; var tupleKey = Tuple.Create(dbSelect, relation); if (!relation.IsChildRelation && _state.CreatedJoins.ContainsKey(tupleKey)) { return(_state.CreatedJoins[tupleKey]); } var toEntity = relation.ToEntity; var dbTable = _dbFactory.BuildTable(toEntity); DbReference joinTo; DbReference childRef = null; IDbSelect childSelect = null; // Create the join. For parent join, we just need to join to a Ref to the table // For child relation, we will firstly create a sub select that return the child table // and then join to then sub select if (!relation.IsChildRelation) { var tableAlias = _nameGenerator.GenerateAlias(dbSelect, dbTable.TableName); joinTo = _dbFactory.BuildRef(dbTable, tableAlias); } else { childRef = _dbFactory.BuildRef(dbTable); childSelect = _dbFactory.BuildSelect(childRef); childRef.Alias = _nameGenerator.GenerateAlias(childSelect, dbTable.TableName); var tableAlias = _nameGenerator.GenerateAlias(dbSelect, TranslationConstants.SubSelectPrefix, true); joinTo = _dbFactory.BuildRef(childSelect, tableAlias); } var dbJoin = _dbFactory.BuildJoin(joinTo, dbSelect); dbSelect.Joins.Add(dbJoin); // build join condition IDbBinary condition = null; for (var i = 0; i < relation.FromKeys.Count; i++) { var fromKey = relation.FromKeys[i]; var toKey = relation.ToKeys[i]; var fromColumn = _dbFactory.BuildColumn(fromRef, fromKey.DbName, fromKey.ValType); var toColumn = _dbFactory.BuildColumn(joinTo, toKey.DbName, toKey.ValType); // If we have created a sub for child relation, we need to the columns // that are used in join condition selected from the sub select. if (childRef != null && childSelect != null) { var alias = _nameGenerator.GenerateAlias(childSelect, toKey.DbName + TranslationConstants.JoinKeySuffix, true); var childColumn = _dbFactory.BuildColumn(childRef, toKey.DbName, toKey.ValType, alias, true); /** * We need to also put the join key in the group of the sub select. * This is to make sure the sub select is grouped by the key so that * the parent (outer select) will not be repeated * This operation needs to happen here not in the aggregation method call. * The reason is that in aggregtion method calls we do not know which column * from the entity is used in relation, so they will not be able to create * the correct column */ childSelect.Selection.Add(_dbFactory.BuildRefColumn(childRef)); childSelect.Selection.Add(childColumn); childSelect.GroupBys.Add(childColumn); toColumn.Name = alias; toColumn.Alias = string.Empty; } // if the relation is found on a fromRef which is referring a sub-select, // it means the from key of the join is not on a table but a derived select. // In this case, we need to add the from key into the derived select, as we will // be using it in the join if (fromRef.Referee is IDbSelect) { var alias = _nameGenerator.GenerateAlias(dbSelect, toKey.DbName + TranslationConstants.JoinKeySuffix, true); fromColumn.Name = alias; fromColumn.Alias = string.Empty; // try to recursively add the join key to all connected sub select. refCol.RefTo?.AddToReferedSelect(_dbFactory, fromKey.DbName, fromKey.ValType, alias); } var binary = _dbFactory.BuildBinary(fromColumn, DbOperator.Equal, toColumn); condition = condition.UpdateBinary(binary, _dbFactory); } dbJoin.Condition = condition; // all relations need to follow the join type if (fromRef.OwnerJoin != null) { dbJoin.Type = fromRef.OwnerJoin.Type; } if (relation.IsChildRelation) { dbJoin.Type = DbJoinType.LeftOuter; } return(_state.CreatedJoins[tupleKey] = dbJoin); }