public override WTableReference ToTableReference() { WSelectQueryBlock queryBlock = SubqueryContext.ToSelectQueryBlock(); queryBlock.SelectElements.Clear(); queryBlock.SelectElements.Add(SqlUtil.GetSelectScalarExpr(SqlUtil.GetFunctionCall(GremlinKeyword.func.Mean, SubqueryContext.PivotVariable.GetDefaultProjection().ToScalarExpression()), GremlinKeyword.TableDefaultColumnName)); return(SqlUtil.GetDerivedTable(queryBlock, GetVariableName())); }
internal static WQueryDerivedTable GetDerivedTable(WSelectQueryBlock selectQueryBlock, string alias) { return(new WQueryDerivedTable() { QueryExpr = selectQueryBlock, Alias = GetIdentifier(alias) }); }
public override WTableReference ToTableReference() { WSelectQueryBlock firstQueryExpr = new WSelectQueryBlock(); foreach (var projectProperty in ProjectedProperties) { if (projectProperty == GremlinKeyword.TableDefaultColumnName) { firstQueryExpr.SelectElements.Add(SqlUtil.GetSelectScalarExpr(InputVariable.GetDefaultProjection().ToScalarExpression(), GremlinKeyword.TableDefaultColumnName)); } else if (InputVariable.ProjectedProperties.Contains(projectProperty)) { firstQueryExpr.SelectElements.Add( SqlUtil.GetSelectScalarExpr( InputVariable.GetVariableProperty(projectProperty).ToScalarExpression(), projectProperty)); } else { firstQueryExpr.SelectElements.Add( SqlUtil.GetSelectScalarExpr(SqlUtil.GetValueExpr(null), projectProperty)); } } WSelectQueryBlock secondQueryExpr = OptionalContext.ToSelectQueryBlock(); bool HasAggregateFunctionAsChildren = false; foreach (var variable in OptionalContext.TableReferences) { if (variable is GremlinFoldVariable || variable is GremlinCountVariable || variable is GremlinMinVariable || variable is GremlinMaxVariable || variable is GremlinSumVariable || variable is GremlinMeanVariable || variable is GremlinTreeVariable) { HasAggregateFunctionAsChildren = true; } var group = variable as GremlinGroupVariable; if (group != null && group.SideEffectKey == null) { HasAggregateFunctionAsChildren = true; } } var WBinaryQueryExpression = SqlUtil.GetBinaryQueryExpr(firstQueryExpr, secondQueryExpr); List <WScalarExpression> parameters = new List <WScalarExpression>(); parameters.Add(SqlUtil.GetScalarSubquery(WBinaryQueryExpression)); var tableRef = SqlUtil.GetFunctionTableReference(GremlinKeyword.func.Optional, parameters, GetVariableName()); ((WOptionalTableReference)tableRef).HasAggregateFunctionAsChildren = HasAggregateFunctionAsChildren; return(SqlUtil.GetCrossApplyTableReference(tableRef)); }
internal static WSelectQueryBlock GetSimpleSelectQueryBlock(params GremlinVariableProperty[] projectProperties) { var queryBlock = new WSelectQueryBlock(); foreach (var property in projectProperties) { queryBlock.SelectElements.Add(GetSelectScalarExpr(property.DefaultVariableProperty().ToScalarExpression())); } return(queryBlock); }
/// <summary> /// Returns true if there are columns of a specific table referenced in a statement /// </summary> /// <param name="node">The query statement</param> /// <param name="tableName">the table for query</param> /// <param name="context">Sql context with table alias mapping</param> /// <param name="columnsOfNodeTables"></param> /// <param name="conn">A open Sql connection</param> /// <returns></returns> public bool Invoke(WSelectQueryBlock node, string tableName, WSqlTableContext context, Dictionary <Tuple <string, string>, Dictionary <string, NodeColumns> > columnsOfNodeTables) { _tableExists = false; _tableName = tableName; _tableRef = context[tableName] as WNamedTableReference; _columnTableMapping = context.GetColumnTableMapping(columnsOfNodeTables); node.Accept(this); return(_tableExists); }
/// <summary> /// Returns true if there are columns of a specific table referenced in a statement /// </summary> /// <param name="node">The query statement</param> /// <param name="tableName">the table for query</param> /// <param name="context">Sql context with table alias mapping</param> /// <param name="columnsOfNodeTables"></param> /// <param name="conn">A open Sql connection</param> /// <returns></returns> public bool Invoke(WSelectQueryBlock node, string tableName, WSqlTableContext context, Dictionary<Tuple<string, string>, Dictionary<string, NodeColumns>> columnsOfNodeTables) { _tableExists = false; _tableName = tableName; _tableRef = context[tableName] as WNamedTableReference; _columnTableMapping = context.GetColumnToAliasMapping(columnsOfNodeTables); node.Accept(this); return _tableExists; }
internal static WQueryDerivedTable GetDerivedTable(WSelectQueryBlock selectQueryBlock, string alias) { return(new WQueryDerivedTable() { QueryExpr = selectQueryBlock, Alias = GetIdentifier(alias), Low = Int32.MinValue, High = Int32.MaxValue }); }
public override void Visit(WSelectQueryBlock node) { if (node.SelectElements.Any(e => e is WSelectStarExpression)) { _tableExists = true; } else { base.Visit(node); } }
public override WTableReference ToTableReference() { List <WScalarExpression> parameters = new List <WScalarExpression>(); WSelectQueryBlock selectQueryBlock = ProjectContext.ToSelectQueryBlock(true); parameters.Add(SqlUtil.GetScalarSubquery(selectQueryBlock)); parameters.Add(SqlUtil.GetValueExpr(SideEffectKey)); var tableRef = SqlUtil.GetFunctionTableReference(GremlinKeyword.func.Store, parameters, GetVariableName()); return(SqlUtil.GetCrossApplyTableReference(tableRef)); }
public int Invoke( WSelectQueryBlock selectQueryBlock) { aggregateFunctionCount = 0; if (selectQueryBlock != null) { selectQueryBlock.Accept(this); } return(aggregateFunctionCount); }
public override WTableReference ToTableReference() { WSelectQueryBlock queryBlock = SubqueryContext.ToSelectQueryBlock(); queryBlock.SelectElements.Clear(); List <WScalarExpression> foldParameters = new List <WScalarExpression> { SubqueryContext.PivotVariable.ToCompose1() }; queryBlock.SelectElements.Add(SqlUtil.GetSelectScalarExpr(SqlUtil.GetFunctionCall(GremlinKeyword.func.Fold, foldParameters), GremlinKeyword.TableDefaultColumnName)); return(SqlUtil.GetDerivedTable(queryBlock, GetVariableName())); }
private WSelectQueryBlock GetSelectQueryBlock(GremlinToSqlContext context) { if (context == null) { var queryBlock = new WSelectQueryBlock(); queryBlock.SelectElements.Add(SqlUtil.GetSelectScalarExpr(InputVariable.GetDefaultProjection().ToScalarExpression())); return(queryBlock); } else { return(context.ToSelectQueryBlock()); } }
public override WTableReference ToTableReference() { List <WScalarExpression> parameters = new List <WScalarExpression>(); for (var i = 0; i < ProjectKeys.Count; i++) { WSelectQueryBlock selectBlock = ProjectContextList[i % ProjectContextList.Count].ToSelectQueryBlock(true); parameters.Add(SqlUtil.GetScalarSubquery(selectBlock)); parameters.Add(SqlUtil.GetValueExpr(ProjectKeys[i])); } var tableRef = SqlUtil.GetFunctionTableReference(GremlinKeyword.func.Project, parameters, GetVariableName()); return(SqlUtil.GetCrossApplyTableReference(tableRef)); }
public override WTableReference ToTableReference() { List <WScalarExpression> parameters = new List <WScalarExpression>(); //TODO: refactor WSelectQueryBlock selectQueryBlock = ProjectContext.ToSelectQueryBlock(); selectQueryBlock.SelectElements.Clear(); selectQueryBlock.SelectElements.Add(SqlUtil.GetSelectScalarExpr(ProjectContext.PivotVariable.ToCompose1(), GremlinKeyword.TableDefaultColumnName)); parameters.Add(SqlUtil.GetScalarSubquery(selectQueryBlock)); parameters.Add(SqlUtil.GetValueExpr(SideEffectKey)); var secondTableRef = SqlUtil.GetFunctionTableReference(GremlinKeyword.func.Store, parameters, this, GetVariableName()); return(SqlUtil.GetCrossApplyTableReference(null, secondTableRef)); }
public WDeleteEdgeSpecification(WSelectQueryBlock deleteSpec) { SelectDeleteExpr = deleteSpec; //FromClause = new WFromClause //{ // TableReferences = new List<WTableReference> // { // new WQueryDerivedTable // { // QueryExpr = deleteSpec // } // } //}; EdgeColumn = deleteSpec.MatchClause.Paths[0].PathEdgeList[0].Item2; }
public override WTableReference ToTableReference() { WSelectQueryBlock queryBlock = SubqueryContext.ToSelectQueryBlock(); queryBlock.SelectElements.Clear(); if (SubqueryContext.PivotVariable.ProjectedProperties.Count == 0) { SubqueryContext.PivotVariable.ProjectedProperties.Add(GetProjectKey()); } List <WScalarExpression> foldParameters = new List <WScalarExpression> { SubqueryContext.PivotVariable.ToCompose1() }; queryBlock.SelectElements.Add(SqlUtil.GetSelectScalarExpr(SqlUtil.GetFunctionCall(GremlinKeyword.func.Fold, foldParameters), GremlinKeyword.ScalarValue)); return(SqlUtil.GetDerivedTable(queryBlock, GetVariableName())); }
public override WTableReference ToTableReference() { List <WScalarExpression> parameters = new List <WScalarExpression>(); parameters.Add(SqlUtil.GetValueExpr(this.SideEffectKey)); WSelectQueryBlock groupBlock = this.GroupByContext.ToSelectQueryBlock(true); parameters.Add(SqlUtil.GetScalarSubquery(groupBlock)); WSelectQueryBlock projectBlock = this.ProjectByContext.ToSelectQueryBlock(true); parameters.Add(SqlUtil.GetScalarSubquery(projectBlock)); var tableRef = SqlUtil.GetFunctionTableReference(GremlinKeyword.func.Group, parameters, GetVariableName()); ((WGroupTableReference)tableRef).IsProjectingACollection = this.IsProjectingACollection; return(SqlUtil.GetCrossApplyTableReference(tableRef)); }
public override WTableReference ToTableReference() { List <WScalarExpression> parameters = new List <WScalarExpression>(); List <WSelectQueryBlock> queryBlocks = new List <WSelectQueryBlock>(); //Must toSelectQueryBlock before toCompose1 of variableList in order to populate needed columns foreach (var byContext in ByContexts) { //TODO: select compose1 WSelectQueryBlock queryBlock = byContext.ToSelectQueryBlock(); queryBlock.SelectElements.Clear(); queryBlock.SelectElements.Add(SqlUtil.GetSelectScalarExpr(byContext.PivotVariable.ToCompose1(), GremlinKeyword.TableDefaultColumnName)); queryBlocks.Add(queryBlock); } if (IsInRepeatContext) { //Must add as the first parameter parameters.Add(SqlUtil.GetColumnReferenceExpr(GremlinKeyword.RepeatInitalTableName, GremlinKeyword.Path)); } foreach (var path in PathList) { if (path.AttachedVariable != null) { parameters.Add(SqlUtil.GetColumnReferenceExpr(path.AttachedVariable.GetVariableName(), GremlinKeyword.Path)); } else { parameters.Add(path.StepVariable.First().ToCompose1()); } } foreach (var block in queryBlocks) { parameters.Add(SqlUtil.GetScalarSubquery(block)); } var tableRef = SqlUtil.GetFunctionTableReference(GremlinKeyword.func.Path2, parameters, GetVariableName()); return(SqlUtil.GetCrossApplyTableReference(tableRef)); }
public override WTableReference ToTableReference() { List <WScalarExpression> parameters = new List <WScalarExpression>(); foreach (var row in rows) { if (row is string || row is int) { var queryBlock = new WSelectQueryBlock(); queryBlock.SelectElements.Add(SqlUtil.GetSelectScalarExpr(SqlUtil.GetValueExpr(row), "_value")); parameters.Add(SqlUtil.GetScalarSubquery(queryBlock)); } else { throw new NotImplementedException(); } } var secondTableRef = SqlUtil.GetFunctionTableReference(GremlinKeyword.func.Inject, parameters, this, GetVariableName()); return(SqlUtil.GetCrossApplyTableReference(null, secondTableRef)); }
public override WTableReference ToTableReference() { WSelectQueryBlock firstQueryExpr = new WSelectQueryBlock(); foreach (var projectProperty in ProjectedProperties) { if (projectProperty == GremlinKeyword.TableDefaultColumnName) { firstQueryExpr.SelectElements.Add(SqlUtil.GetSelectScalarExpr(InputVariable.DefaultProjection().ToScalarExpression(), GremlinKeyword.TableDefaultColumnName)); } else if (InputVariable.ProjectedProperties.Contains(projectProperty)) { firstQueryExpr.SelectElements.Add( SqlUtil.GetSelectScalarExpr( InputVariable.GetVariableProperty(projectProperty).ToScalarExpression(), projectProperty)); } else { firstQueryExpr.SelectElements.Add( SqlUtil.GetSelectScalarExpr(SqlUtil.GetValueExpr(null), projectProperty)); } } if (OptionalContext.IsPopulateGremlinPath) { firstQueryExpr.SelectElements.Add(SqlUtil.GetSelectScalarExpr(SqlUtil.GetValueExpr(null), GremlinKeyword.Path)); } WSelectQueryBlock secondQueryExpr = OptionalContext.ToSelectQueryBlock(ProjectedProperties); var WBinaryQueryExpression = SqlUtil.GetBinaryQueryExpr(firstQueryExpr, secondQueryExpr); List <WScalarExpression> parameters = new List <WScalarExpression>(); parameters.Add(SqlUtil.GetScalarSubquery(WBinaryQueryExpression)); var tableRef = SqlUtil.GetFunctionTableReference(GremlinKeyword.func.Optional, parameters, GetVariableName()); return(SqlUtil.GetCrossApplyTableReference(tableRef)); }
public override WTableReference ToTableReference() { WSelectQueryBlock queryBlock = SubqueryContext.ToSelectQueryBlock(); queryBlock.SelectElements.Clear(); List <WValueExpression> columnListExpr = new List <WValueExpression>(); foreach (var projectProperty in ProjectedProperties) { columnListExpr.Add(SqlUtil.GetValueExpr(projectProperty)); } List <WScalarExpression> capParameters = new List <WScalarExpression>(); foreach (var sideEffectKey in SideEffectKeys) { capParameters.Add(new WColumnNameList(columnListExpr)); capParameters.Add(SqlUtil.GetValueExpr(sideEffectKey)); } queryBlock.SelectElements.Add(SqlUtil.GetSelectScalarExpr(SqlUtil.GetFunctionCall(GremlinKeyword.func.Cap, capParameters), GremlinKeyword.ScalarValue)); return(SqlUtil.GetDerivedTable(queryBlock, GetVariableName())); }
internal void Split(out WSelectQueryBlock contextSelect, out WSelectQueryBlock repeatSelectQuery) { WScalarSubquery repeatInput = Parameters[0] as WScalarSubquery; if (repeatInput == null) { throw new SyntaxErrorException("The input of a repeat table reference must be a scalar subquery."); } WBinaryQueryExpression binaryQuery = repeatInput.SubQueryExpr as WBinaryQueryExpression; if (binaryQuery == null || binaryQuery.BinaryQueryExprType != BinaryQueryExpressionType.Union || !binaryQuery.All) { throw new SyntaxErrorException("The input of a repeat table reference must be a UNION ALL binary query expression."); } contextSelect = binaryQuery.FirstQueryExpr as WSelectQueryBlock; repeatSelectQuery = binaryQuery.SecondQueryExpr as WSelectQueryBlock; if (contextSelect == null || repeatSelectQuery == null) { throw new SyntaxErrorException("The input of a repeat table reference must be a UNION ALL binary query and the two sub-queries must be a select query block."); } }
public override WTableReference ToTableReference() { List <WScalarExpression> parameters = new List <WScalarExpression>(); if (this.UnionContextList.Count == 0) { parameters.AddRange(this.ProjectedProperties.Select(SqlUtil.GetValueExpr)); } else { foreach (GremlinToSqlContext context in this.UnionContextList) { WSelectQueryBlock selectQueryBlock = context.ToSelectQueryBlock(); Dictionary <string, WSelectElement> projectionMap = new Dictionary <string, WSelectElement>(); WSelectElement value = selectQueryBlock.SelectElements[0]; foreach (WSelectElement selectElement in selectQueryBlock.SelectElements) { projectionMap[(selectElement as WSelectScalarExpression).ColumnName] = selectElement; } selectQueryBlock.SelectElements.Clear(); selectQueryBlock.SelectElements.Add(SqlUtil.GetSelectScalarExpr((value as WSelectScalarExpression).SelectExpr, this.DefaultProperty())); foreach (string property in this.ProjectedProperties) { selectQueryBlock.SelectElements.Add( projectionMap.TryGetValue(property, out value) ? value : SqlUtil.GetSelectScalarExpr(SqlUtil.GetValueExpr(null), property)); } parameters.Add(SqlUtil.GetScalarSubquery(selectQueryBlock)); } } var tableRef = SqlUtil.GetFunctionTableReference(GremlinKeyword.func.Union, parameters, GetVariableName()); return(SqlUtil.GetCrossApplyTableReference(tableRef)); }
public override void Visit(WSelectQueryBlock node) { UpdateMapping(node); }
/// <summary> /// Estimate number of rows of node table in graph. /// </summary> /// <param name="graph">Constructed node graph</param> private void EstimateRows(WSelectQueryBlock query, MatchGraph graph) { var declareParameter = ""; if (_variables != null) { foreach (var parameter in _variables) { declareParameter += "DECLARE " + parameter.VariableName.Value + " " + TsqlFragmentToString.DataType(parameter.DataType) + "\r\n"; } } var estimator = new TableSizeEstimator(Conn); bool first = true; var fromQuerySb = new StringBuilder(1024); foreach (var subGraph in graph.ConnectedSubGraphs) { foreach (var node in subGraph.Nodes) { if (first) first = false; else fromQuerySb.Append(", "); var currentNode = node.Value; fromQuerySb.AppendFormat("{0} AS [{1}] WITH (ForceScan)", currentNode.TableObjectName, currentNode.NodeAlias); } } // Attach proper parts of the where clause into the Estimiation Query var columnTableMapping = _context.GetColumnTableMapping(_columnsOfNodeTables); var attachWhereClauseVisiter = new AttachNodeEdgePredictesVisitor(); var nodeEstimationWhereClause = attachWhereClauseVisiter.Invoke(query.WhereClause, graph, columnTableMapping); string nodeEstimationQuery = string.Format("{0}\r\n SELECT * FROM {1}\r\n", declareParameter, fromQuerySb); if (nodeEstimationWhereClause.SearchCondition != null) nodeEstimationQuery += nodeEstimationWhereClause.ToString(); var estimateRows = estimator.GetQueryTableEstimatedRows(nodeEstimationQuery); foreach (var subGraph in graph.ConnectedSubGraphs) { // Update Row Estimation for nodes foreach (var node in subGraph.Nodes) { var currentNode = node.Value; currentNode.EstimatedRows = estimateRows[node.Key]; var tableSchema = currentNode.TableObjectName.SchemaIdentifier.Value; var tableName = currentNode.TableObjectName.BaseIdentifier.Value; currentNode.TableRowCount = estimator.GetTableRowCount(tableSchema, tableName); } } // Attach predicates to nodes and edges var attachPredicateVisitor = new AttachWhereClauseVisitor(); attachPredicateVisitor.Invoke(query.WhereClause, graph, columnTableMapping); }
public override WTableReference ToTableReference() { Dictionary <GremlinVariableProperty, string> map = new Dictionary <GremlinVariableProperty, string>(); Dictionary <GremlinVariableProperty, string> map2 = new Dictionary <GremlinVariableProperty, string>(); Dictionary <GremlinVariableProperty, string> map3 = new Dictionary <GremlinVariableProperty, string>(); Dictionary <GremlinVariableProperty, string> map4 = new Dictionary <GremlinVariableProperty, string>(); WRepeatConditionExpression conditionExpr = GetRepeatConditionExpression(); List <WSelectScalarExpression> inputSelectList = GetInputSelectList(ref map); List <WSelectScalarExpression> outerSelectList = GetOuterSelectList(ref map); List <WSelectScalarExpression> terminateSelectList = GetConditionSelectList(ref map2); List <WSelectScalarExpression> repeatPathOuterList = GetRepeatPathOuterVariableList(ref map3); List <WSelectScalarExpression> conditionPathOuterList = GetConditionPathOuterVariableList(ref map4); WSelectQueryBlock selectQueryBlock = RepeatContext.ToSelectQueryBlock(); selectQueryBlock.SelectElements.Clear(); foreach (var selectElement in inputSelectList) { selectQueryBlock.SelectElements.Add(selectElement); } foreach (var selectElement in outerSelectList) { selectQueryBlock.SelectElements.Add(selectElement); } foreach (var selectElement in terminateSelectList) { selectQueryBlock.SelectElements.Add(selectElement); } foreach (var selectElement in repeatPathOuterList) { selectQueryBlock.SelectElements.Add(selectElement); } foreach (var selectElement in conditionPathOuterList) { selectQueryBlock.SelectElements.Add(selectElement); } WSelectQueryBlock firstQueryExpr = new WSelectQueryBlock(); foreach (var item in map) { firstQueryExpr.SelectElements.Add(SqlUtil.GetSelectScalarExpr(item.Key.ToScalarExpression(), item.Value)); } foreach (var item in map2) { firstQueryExpr.SelectElements.Add(SqlUtil.GetSelectScalarExpr(SqlUtil.GetValueExpr(null), item.Value)); } foreach (var item in map3) { firstQueryExpr.SelectElements.Add(SqlUtil.GetSelectScalarExpr(item.Key.ToScalarExpression(), item.Value)); } foreach (var item in map4) { firstQueryExpr.SelectElements.Add(SqlUtil.GetSelectScalarExpr(item.Key.ToScalarExpression(), item.Value)); } //firstQueryExpr.SelectElements.Add(SqlUtil.GetSelectScalarExpr(FirstVariable.DefaultProjection().ToScalarExpression(), GremlinKeyword.TableDefaultColumnName)); //selectQueryBlock.SelectElements.Add(SqlUtil.GetSelectScalarExpr(RepeatContext.PivotVariable.DefaultProjection().ToScalarExpression(), GremlinKeyword.TableDefaultColumnName)); foreach (var property in ProjectedProperties) { if (InputVariable.ProjectedProperties.Contains(property)) { firstQueryExpr.SelectElements.Add( SqlUtil.GetSelectScalarExpr( InputVariable.GetVariableProperty(property).ToScalarExpression(), property)); } else { firstQueryExpr.SelectElements.Add( SqlUtil.GetSelectScalarExpr(SqlUtil.GetValueExpr(null), property)); } if (RepeatContext.PivotVariable.ProjectedProperties.Contains(property)) { selectQueryBlock.SelectElements.Add( SqlUtil.GetSelectScalarExpr( RepeatContext.PivotVariable.GetVariableProperty(property).ToScalarExpression(), property)); } else { selectQueryBlock.SelectElements.Add( SqlUtil.GetSelectScalarExpr(SqlUtil.GetValueExpr(null), property)); } } if (SelectedVariableList.Count != 0) { foreach (var selectedVariableTuple in SelectedVariableList) { var columnName = selectedVariableTuple.Item1; var selectedVariable = selectedVariableTuple.Item2; firstQueryExpr.SelectElements.Add(SqlUtil.GetSelectScalarExpr(SqlUtil.GetValueExpr(null), columnName)); List <WScalarExpression> compose2Paramters = new List <WScalarExpression>(); compose2Paramters.Add(selectedVariable.RealVariable.ToCompose1()); compose2Paramters.Add(SqlUtil.GetColumnReferenceExpr("R", columnName)); WFunctionCall compose2 = SqlUtil.GetFunctionCall(GremlinKeyword.func.Compose2, compose2Paramters); selectQueryBlock.SelectElements.Add(SqlUtil.GetSelectScalarExpr(compose2, columnName)); } } if (RepeatContext.IsPopulateGremlinPath) { var columnName = GremlinKeyword.Path; var pathVariable = RepeatContext.CurrentContextPath; firstQueryExpr.SelectElements.Add(SqlUtil.GetSelectScalarExpr(SqlUtil.GetValueExpr(null), columnName)); selectQueryBlock.SelectElements.Add(SqlUtil.GetSelectScalarExpr(pathVariable.DefaultProjection().ToScalarExpression(), columnName)); } var WBinaryQueryExpression = SqlUtil.GetBinaryQueryExpr(firstQueryExpr, selectQueryBlock); ModifyColumnNameVisitor newVisitor = new ModifyColumnNameVisitor(); newVisitor.Invoke(selectQueryBlock, map); newVisitor.Invoke(conditionExpr, map2); newVisitor.Invoke(selectQueryBlock, map3); newVisitor.Invoke(conditionExpr, map4); List <WScalarExpression> repeatParameters = new List <WScalarExpression>(); repeatParameters.Add(SqlUtil.GetScalarSubquery(WBinaryQueryExpression)); repeatParameters.Add(conditionExpr); var secondTableRef = SqlUtil.GetFunctionTableReference(GremlinKeyword.func.Repeat, repeatParameters, this, GetVariableName()); return(SqlUtil.GetCrossApplyTableReference(null, secondTableRef)); }
/// <summary> /// Check validity of the match clause /// </summary> /// <param name="node"></param> private void CheckValidity(WSelectQueryBlock node) { if (node.MatchClause == null) return; if (node.MatchClause.Paths.All( path => path.PathNodeList.All( part => _context.CheckTable(part.Item1.BaseIdentifier.Value) && IsNodeTable(_context[part.Item1.BaseIdentifier.Value]) ) )) { foreach (var path in node.MatchClause.Paths) { var index = 0; for (var count = path.PathNodeList.Count; index < count; ++index) { var pathNode = path.PathNodeList[index]; var table = _context[pathNode.Item1.BaseIdentifier.Value] as WNamedTableReference; var edge = pathNode.Item2.MultiPartIdentifier.Identifiers.Last().Value.ToLower(); var nodeTableKey = WNamedTableReference.SchemaNameToTuple(table.TableObjectName); if (_columnsOfNodeTables[nodeTableKey].ContainsKey(edge)) { if ( _columnsOfNodeTables.Keys.Contains( new Tuple<string, string>(nodeTableKey.Item1.ToLower(CultureInfo.CurrentCulture), _columnsOfNodeTables[nodeTableKey][edge].Reference))) { var nextNode = index != count - 1 ? path.PathNodeList[index + 1].Item1 : path.Tail; var getNextTable = _context[nextNode.BaseIdentifier.Value]; if (!IsNodeTable(getNextTable)) throw new GraphViewException("Node table expected in MATCH clause"); var nextTable = getNextTable as WNamedTableReference; if (nextTable == null || !String.Equals(nextTable.TableObjectName.BaseIdentifier.Value, _columnsOfNodeTables[nodeTableKey][edge].Reference, StringComparison.CurrentCultureIgnoreCase)) { throw new GraphViewException(String.Format(CultureInfo.CurrentCulture, "Wrong Reference Table {0}", nextNode.BaseIdentifier.Value)); } } else { throw new GraphViewException(String.Format(CultureInfo.CurrentCulture, "Node Table Referenced by the Edge {0} not exists", edge)); } } else { throw new GraphViewException(String.Format(CultureInfo.CurrentCulture, "Edge Column {0} not exists in the Node Table", edge)); } } } } else { throw new GraphViewException("Node table expected in MATCH clause"); } }
public void Invoke(WSelectQueryBlock node) { node.Accept(this); }
public override void Visit(WSelectQueryBlock node) { base.Visit(node); var flag = false; for (var i = 0; i < MatchList.Count; ++i) { if (MatchFlag[i]) continue; var nextToken = node.LastTokenIndex + 1; while (nextToken < Tokens.Count && Tokens[nextToken].TokenType == TSqlTokenType.WhiteSpace) nextToken++; var insideBlock = (node.FirstTokenIndex < MatchList[i].FirstTokenIndex && (node.LastTokenIndex > MatchList[i].LastTokenIndex || nextToken == MatchList[i].FirstTokenIndex)); if (!insideBlock) continue; nextToken = node.FromClause.LastTokenIndex + 1; while (nextToken < Tokens.Count && Tokens[nextToken].TokenType == TSqlTokenType.WhiteSpace) nextToken++; // match clause should exactly follow FROM clause if (node.FromClause.TableReferences == null || nextToken != MatchList[i].FirstTokenIndex) { var token = Tokens[nextToken]; _errors.Add(new ParseError(0, token.Offset, token.Line, token.Column, "MATCH clause should exactly follow FROM clause")); } // if a where/top row filter/group by/having clause exists, it should be followed by a match clause if (node.WhereClause.SearchCondition != null && node.WhereClause.FirstTokenIndex < MatchList[i].LastTokenIndex) { var token = Tokens[nextToken]; _errors.Add(new ParseError(0, token.Offset, token.Line, token.Column, "WHERE clause should be followed by a MATCH clause")); } if (node.TopRowFilter != null && node.TopRowFilter.FirstTokenIndex < MatchList[i].LastTokenIndex) { var token = Tokens[nextToken]; _errors.Add(new ParseError(0, token.Offset, token.Line, token.Column, "Top row filter should be followed by a MATCH clause")); } if (node.GroupByClause != null && node.GroupByClause.FirstTokenIndex < MatchList[i].LastTokenIndex) { var token = Tokens[nextToken]; _errors.Add(new ParseError(0, token.Offset, token.Line, token.Column, "GROUP BY clause should be followed by a MATCH clause")); } if (node.HavingClause != null && node.HavingClause.FirstTokenIndex < MatchList[i].LastTokenIndex) { var token = Tokens[nextToken]; _errors.Add(new ParseError(0, token.Offset, token.Line, token.Column, "HAVING clause should be followed by a MATCH clause")); } if (flag) { var token = Tokens[nextToken]; _errors.Add(new ParseError(0, token.Offset, token.Line, token.Column, "Mutiple MATCH clauses in same query block")); } if (_errors.Count > 0) return; if (node.LastTokenIndex < MatchList[i].FirstTokenIndex) { node.LastTokenIndex = MatchList[i].LastTokenIndex; } flag = true; if (node.MatchClause != null && node.MatchClause.Paths != null && node.MatchClause.Paths.Any()) { foreach (var path in MatchList[i].Paths) { node.MatchClause.Paths.Add(path); } } else { node.MatchClause = MatchList[i]; } MatchFlag[i] = true; } }
/// <summary> /// Constructs the graph pattern specified by the MATCH clause. /// The graph pattern may consist of multiple fully-connected sub-graphs. /// </summary> /// <param name="query">The SELECT query block</param> /// <returns>A graph object contains all the connected componeents</returns> private MatchGraph ConstructGraph(WSelectQueryBlock query) { if (query == null || query.MatchClause == null) return null; var unionFind = new UnionFind(); var edgeColumnToAliasesDict = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase); var pathDictionary = new Dictionary<string, MatchPath>(StringComparer.OrdinalIgnoreCase); var matchClause = query.MatchClause; var nodes = new Dictionary<string, MatchNode>(StringComparer.OrdinalIgnoreCase); var connectedSubGraphs = new List<ConnectedComponent>(); var subGrpahMap = new Dictionary<string, ConnectedComponent>(StringComparer.OrdinalIgnoreCase); var parent = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); unionFind.Parent = parent; // Constructs the graph pattern specified by the path expressions in the MATCH clause foreach (var path in matchClause.Paths) { var index = 0; MatchEdge preEdge = null; for (var count = path.PathEdgeList.Count; index < count; ++index) { var currentNodeTableRef = path.PathEdgeList[index].Item1; var currentEdgeColumnRef = path.PathEdgeList[index].Item2; var currentNodeExposedName = currentNodeTableRef.BaseIdentifier.Value; var nextNodeTableRef = index != count - 1 ? path.PathEdgeList[index + 1].Item1 : path.Tail; var nextNodeExposedName = nextNodeTableRef.BaseIdentifier.Value; var patternNode = nodes.GetOrCreate(currentNodeExposedName); if (patternNode.NodeAlias == null) { patternNode.NodeAlias = currentNodeExposedName; patternNode.Neighbors = new List<MatchEdge>(); patternNode.External = false; var nodeTable = _context[currentNodeExposedName] as WNamedTableReference; if (nodeTable != null) { patternNode.NodeTableObjectName = nodeTable.TableObjectName; if (patternNode.NodeTableObjectName.SchemaIdentifier == null) patternNode.NodeTableObjectName.Identifiers.Insert(0, new Identifier {Value = "dbo"}); } } string edgeAlias = currentEdgeColumnRef.Alias; if (edgeAlias == null) { var currentEdgeName = currentEdgeColumnRef.MultiPartIdentifier.Identifiers.Last().Value; edgeAlias = string.Format("{0}_{1}_{2}", currentNodeExposedName, currentEdgeName, nextNodeExposedName); if (edgeColumnToAliasesDict.ContainsKey(currentEdgeName)) { edgeColumnToAliasesDict[currentEdgeName].Add(edgeAlias); } else { edgeColumnToAliasesDict.Add(currentEdgeName, new List<string> { edgeAlias }); } } Identifier edgeIdentifier = currentEdgeColumnRef.MultiPartIdentifier.Identifiers.Last(); string schema = patternNode.NodeTableObjectName.SchemaIdentifier.Value.ToLower(); string nodeTableName = patternNode.NodeTableObjectName.BaseIdentifier.Value; string bindTableName = _context.EdgeNodeBinding[ new Tuple<string, string>(nodeTableName.ToLower(), edgeIdentifier.Value.ToLower())].ToLower(); MatchEdge edge; if (currentEdgeColumnRef.MinLength == 1 && currentEdgeColumnRef.MaxLength == 1) { edge = new MatchEdge { SourceNode = patternNode, EdgeColumn = currentEdgeColumnRef, EdgeAlias = edgeAlias, BindNodeTableObjName = new WSchemaObjectName( new Identifier {Value = schema}, new Identifier {Value = bindTableName} ), }; _context.AddEdgeReference(edge); } else { MatchPath matchPath = new MatchPath { SourceNode = patternNode, EdgeColumn = currentEdgeColumnRef, EdgeAlias = edgeAlias, BindNodeTableObjName = new WSchemaObjectName( new Identifier {Value = schema}, new Identifier {Value = bindTableName} ), MinLength = currentEdgeColumnRef.MinLength, MaxLength = currentEdgeColumnRef.MaxLength, ReferencePathInfo = false, AttributeValueDict = currentEdgeColumnRef.AttributeValueDict }; _context.AddEdgeReference(matchPath); pathDictionary[edgeAlias] = matchPath; edge = matchPath; } if (preEdge != null) { preEdge.SinkNode = patternNode; } preEdge = edge; if (!parent.ContainsKey(currentNodeExposedName)) parent[currentNodeExposedName] = currentNodeExposedName; if (!parent.ContainsKey(nextNodeExposedName)) parent[nextNodeExposedName] = nextNodeExposedName; unionFind.Union(currentNodeExposedName, nextNodeExposedName); patternNode.Neighbors.Add(edge); } var tailExposedName = path.Tail.BaseIdentifier.Value; var tailNode = nodes.GetOrCreate(tailExposedName); if (tailNode.NodeAlias == null) { tailNode.NodeAlias = tailExposedName; tailNode.Neighbors = new List<MatchEdge>(); var nodeTable = _context[tailExposedName] as WNamedTableReference; if (nodeTable != null) { tailNode.NodeTableObjectName = nodeTable.TableObjectName; if (tailNode.NodeTableObjectName.SchemaIdentifier == null) tailNode.NodeTableObjectName.Identifiers.Insert(0, new Identifier {Value = "dbo"}); } } if (preEdge != null) preEdge.SinkNode = tailNode; } // Puts nodes into subgraphs foreach (var node in nodes) { string root = unionFind.Find(node.Key); if (!subGrpahMap.ContainsKey(root)) { var subGraph = new ConnectedComponent(); subGraph.Nodes[node.Key] = node.Value; foreach (var edge in node.Value.Neighbors) { subGraph.Edges[edge.EdgeAlias] = edge; } subGrpahMap[root] = subGraph; connectedSubGraphs.Add(subGraph); subGraph.IsTailNode[node.Value] = false; } else { var subGraph = subGrpahMap[root]; subGraph.Nodes[node.Key] = node.Value; foreach (var edge in node.Value.Neighbors) { subGraph.Edges[edge.EdgeAlias] = edge; } subGraph.IsTailNode[node.Value] = false; } } var graph = new MatchGraph { ConnectedSubGraphs = connectedSubGraphs, }; unionFind.Parent = null; // When an edge in the MATCH clause is not associated with an alias, // assigns to it a default alias: sourceAlias_EdgeColumnName_sinkAlias. // Also rewrites edge attributes anywhere in the query that can be bound to this default alias. var replaceTableRefVisitor = new ReplaceEdgeReferenceVisitor(); replaceTableRefVisitor.Invoke(query, edgeColumnToAliasesDict); // Rematerializes node tables in the MATCH clause which are defined in the upper-level context // and join them with the upper-level table references. RematerilizeExtrenalNodeTableReference(query, nodes); // Transforms the path reference in the SELECT elements into a // scalar function to display path information. TransformPathInfoDisplaySelectElement(query, pathDictionary); return graph; }
private void TranslateEdgeDelete(WDeleteEdgeSpecification node, List<WSqlStatement> res) { const string deleteEdgeTableName = "GraphView_DeleteEdgeInternalTable"; string deleteEdgeTempTable = "DeleteTmp_" + Path.GetRandomFileName().Replace(".", "").Substring(0, 8); // Wraps the select statement in a common table expression WTableReference sinkTable, sourceTable; var deleteCommonTableExpr = ConstructDeleteEdgeSelect(node.SelectDeleteExpr, node.EdgeColumn.Alias, deleteEdgeTempTable, out sinkTable, out sourceTable); var sourceNameTable = sourceTable as WNamedTableReference; if (sourceNameTable == null) throw new GraphViewException("Source table of DELETE EDGE statement should be a named table reference."); var sinkNameTable = sinkTable as WNamedTableReference; if (sinkNameTable == null) throw new GraphViewException("Sink table of DELETE EDGE statement should be a named table reference."); // Get edge select query and reversed edge select query WSelectQueryBlock edgeSelectQuery = new WSelectQueryBlock { SelectElements = new List<WSelectElement> { new WSelectScalarExpression { SelectExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier(new Identifier {Value = "src"}) }, ColumnName = "source" }, new WSelectScalarExpression { SelectExpr = new WFunctionCall { CallTarget = new WMultiPartIdentifierCallTarget { Identifiers = new WMultiPartIdentifier(new Identifier {Value = "dbo"}) }, FunctionName = new Identifier {Value = GraphViewConnection.GraphViewUdfAssemblyName + "EdgeIdEncoder"}, Parameters = new List<WScalarExpression> { new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier(new Identifier {Value = "edgeid"}) }, }, }, ColumnName = "binary1" }, new WSelectScalarExpression { SelectExpr = new WFunctionCall { FunctionName = new Identifier{Value = "COUNT"}, Parameters = new List<WScalarExpression> { new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier(new Identifier {Value = "sink"}) }, }, }, ColumnName = "sinkCnt" } }, FromClause = new WFromClause { TableReferences = new List<WTableReference> { new WSpecialNamedTableReference { TableObjectName = new WSchemaObjectName(new Identifier{Value = deleteEdgeTempTable }) } } }, GroupByClause = new WGroupByClause { GroupingSpecifications = new List<WGroupingSpecification> { new WExpressionGroupingSpec { Expression = new WValueExpression("src",false) } } } }; WSelectQueryBlock reversedEdgeSelectQuery = new WSelectQueryBlock { SelectElements = new List<WSelectElement> { new WSelectScalarExpression { SelectExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier(new Identifier {Value = "sink"}) }, ColumnName = "source" }, new WSelectScalarExpression { SelectExpr = new WFunctionCall { FunctionName = new Identifier { Value = "COUNT" }, Parameters = new List<WScalarExpression> { new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier(new Identifier {Value = "src"}) }, } }, ColumnName = "srcCnt" }, }, FromClause = edgeSelectQuery.FromClause, GroupByClause = new WGroupByClause { GroupingSpecifications = new List<WGroupingSpecification> { new WExpressionGroupingSpec { Expression = new WValueExpression("sink", false) } } } }; //Check src & sink string sourceTableName = sourceNameTable.TableObjectName.ToString(); string sinkTableName = sinkNameTable.TableObjectName.ToString(); int compare = string.CompareOrdinal(sourceTableName, sinkTableName); WSetClause[] setClauses = new WSetClause[] { new WFunctionCallSetClause() { MutatorFuction = new WFunctionCall { CallTarget = new WMultiPartIdentifierCallTarget() { Identifiers = new WMultiPartIdentifier(new Identifier { Value = node.EdgeColumn.MultiPartIdentifier.Identifiers.Last().Value + "DeleteCol" }) }, FunctionName = new Identifier {Value = "WRITE"}, Parameters = new WScalarExpression[] { new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier( new Identifier {Value = deleteEdgeTableName}, new Identifier {Value = "binary1"}) }, new WValueExpression("null", false), new WValueExpression("null", false) } } }, new WAssignmentSetClause() { AssignmentKind = AssignmentKind.Equals, Column = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier(new Identifier { Value = "InDegree" }) }, NewValue = new WBinaryExpression { FirstExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier(new Identifier { Value = "InDegree" }) }, SecondExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier(new Identifier {Value = "srcCnt"}) }, ExpressionType = BinaryExpressionType.Subtract } }, new WAssignmentSetClause() { AssignmentKind = AssignmentKind.Equals, Column = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier(new Identifier { Value = node.EdgeColumn.MultiPartIdentifier.Identifiers.Last().Value + "OutDegree" }) }, NewValue = new WBinaryExpression { FirstExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier(new Identifier { Value = node.EdgeColumn.MultiPartIdentifier.Identifiers.Last().Value + "OutDegree" }) }, SecondExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier(new Identifier {Value = "sinkCnt"}) }, ExpressionType = BinaryExpressionType.Subtract } } }; WWhereClause whereClause = new WWhereClause { SearchCondition = new WBooleanComparisonExpression { FirstExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier(new Identifier { Value = "GlobalNodeId" }) }, SecondExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier( new Identifier { Value = deleteEdgeTableName }, new Identifier { Value = "source" } ) }, ComparisonType = BooleanComparisonType.Equals } }; if (compare == 0) { res.Add(new WDeclareTableVariable { VariableName = new Identifier { Value = "@" + deleteEdgeTempTable}, Definition = new WTableDefinition { ColumnDefinitions = new WColumnDefinition[] { new WColumnDefinition { ColumnIdentifier = new Identifier{Value = "source"}, DataType = new WParameterizedDataTypeReference{Name = new WSchemaObjectName(new Identifier{Value = "bigint"})}, Constraints = new List<WConstraintDefinition> { new WUniqueConstraintDefinition { Clustered = true, IsPrimaryKey = true } } }, new WColumnDefinition { ColumnIdentifier = new Identifier{Value = "binary1"}, DataType = new WParameterizedDataTypeReference { Name = new WSchemaObjectName(new Identifier{Value = "varbinary"}), Parameters = new List<Literal>{new MaxLiteral{Value = "max"}} }, }, new WColumnDefinition { ColumnIdentifier = new Identifier{Value = "sinkCnt"}, DataType = new WParameterizedDataTypeReference { Name = new WSchemaObjectName(new Identifier{Value = "int"}), }, }, new WColumnDefinition { ColumnIdentifier = new Identifier{Value = "srcCnt"}, DataType = new WParameterizedDataTypeReference { Name = new WSchemaObjectName(new Identifier{Value = "int"}), }, }, }, Indexes = new List<WIndexDefinition>(), TableConstraints = new List<WConstraintDefinition>() } }); var tableVar = new WSpecialNamedTableReference { TableObjectName = new WSchemaObjectName(new Identifier { Value = "@" + deleteEdgeTempTable }) }; res.Add(deleteCommonTableExpr); res.Add(new WInsertSpecification { Target = tableVar, InsertSource = new WSelectInsertSource { Select = new WSelectQueryBlock { SelectElements = new WSelectElement[] { new WSelectScalarExpression { SelectExpr = new WFunctionCall { FunctionName = new Identifier {Value = "ISNULL"}, Parameters = new WScalarExpression[] { new WColumnReferenceExpression { MultiPartIdentifier = new WSchemaObjectName(new Identifier[] { new Identifier {Value = deleteEdgeTableName + "_1"}, new Identifier {Value = "source"} }), }, new WColumnReferenceExpression { MultiPartIdentifier = new WSchemaObjectName(new Identifier[] { new Identifier {Value = deleteEdgeTableName + "_2"}, new Identifier {Value = "source"} }), }, } }, ColumnName = "source" }, new WSelectScalarExpression { SelectExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WSchemaObjectName(new Identifier[] {new Identifier {Value = "binary1"}}), } }, //new WSelectScalarExpression //{ // SelectExpr = new WColumnReferenceExpression // { // MultiPartIdentifier = new WSchemaObjectName(new Identifier[]{new Identifier{Value = "binary2"}}), // } //}, new WSelectScalarExpression { SelectExpr = new WFunctionCall { FunctionName = new Identifier {Value = "ISNULL"}, Parameters = new WScalarExpression[] { new WColumnReferenceExpression { MultiPartIdentifier = new WSchemaObjectName(new Identifier[] { new Identifier {Value = deleteEdgeTableName + "_1"}, new Identifier {Value = "sinkCnt"} }), }, new WValueExpression { Value = "0" }, } }, ColumnName = "sinkCnt" }, new WSelectScalarExpression { SelectExpr = new WFunctionCall { FunctionName = new Identifier {Value = "ISNULL"}, Parameters = new WScalarExpression[] { new WColumnReferenceExpression { MultiPartIdentifier = new WSchemaObjectName(new Identifier[] { new Identifier {Value = deleteEdgeTableName + "_2"}, new Identifier {Value = "srcCnt"} }), }, new WValueExpression { Value = "0" }, } }, ColumnName = "srcCnt" }, }, FromClause = new WFromClause { TableReferences = new WTableReference[] { new WQualifiedJoin { QualifiedJoinType = QualifiedJoinType.FullOuter, FirstTableRef = new WQueryDerivedTable { Alias = new Identifier {Value = deleteEdgeTableName + "_1"}, QueryExpr = edgeSelectQuery }, SecondTableRef = new WQueryDerivedTable { Alias = new Identifier {Value = deleteEdgeTableName + "_2"}, QueryExpr = reversedEdgeSelectQuery }, JoinCondition = new WBooleanComparisonExpression() { ComparisonType = BooleanComparisonType.Equals, FirstExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WSchemaObjectName(new Identifier[] { new Identifier {Value = deleteEdgeTableName + "_1"}, new Identifier {Value = "source"} }), }, SecondExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WSchemaObjectName(new Identifier[] { new Identifier {Value = deleteEdgeTableName + "_2"}, new Identifier {Value = "source"} }), } } }, } } } } }); res.Add(new WUpdateSpecification { Target = new WNamedTableReference { TableObjectName = sourceNameTable.TableObjectName }, SetClauses = setClauses, FromClause = new WFromClause { TableReferences = new WTableReference[] { new WQueryDerivedTable { Alias = new Identifier {Value = deleteEdgeTableName}, QueryExpr = new WSelectQueryBlock { SelectElements = new List<WSelectElement>() { new WSelectStarExpression() }, FromClause = new WFromClause { TableReferences = new List<WTableReference> {tableVar} } } }, } }, WhereClause = whereClause, TopRowFilter = node.TopRowFilter, }); res.Add(new WSqlUnknownStatement { Statement = String.Format("DELETE FROM @{0}", deleteEdgeTempTable) }); } else { // Declare table variables res.Add(new WDeclareTableVariable { VariableName = new Identifier { Value = "@" + deleteEdgeTempTable + "_1" }, Definition = new WTableDefinition { ColumnDefinitions = new WColumnDefinition[] { new WColumnDefinition { ColumnIdentifier = new Identifier {Value = "source"}, DataType = new WParameterizedDataTypeReference { Name = new WSchemaObjectName(new Identifier {Value = "bigint"}) }, Constraints = new List<WConstraintDefinition> { new WUniqueConstraintDefinition { Clustered = true, IsPrimaryKey = true } } }, new WColumnDefinition { ColumnIdentifier = new Identifier {Value = "binary1"}, DataType = new WParameterizedDataTypeReference { Name = new WSchemaObjectName(new Identifier {Value = "varbinary"}), Parameters = new List<Literal> {new MaxLiteral {Value = "max"}} }, }, new WColumnDefinition { ColumnIdentifier = new Identifier {Value = "sinkCnt"}, DataType = new WParameterizedDataTypeReference { Name = new WSchemaObjectName(new Identifier {Value = "int"}), }, }, }, Indexes = new List<WIndexDefinition>(), TableConstraints = new List<WConstraintDefinition>() } }); res.Add(new WDeclareTableVariable { VariableName = new Identifier { Value = "@" + deleteEdgeTempTable + "_2" }, Definition = new WTableDefinition { ColumnDefinitions = new WColumnDefinition[] { new WColumnDefinition { ColumnIdentifier = new Identifier {Value = "source"}, DataType = new WParameterizedDataTypeReference { Name = new WSchemaObjectName(new Identifier {Value = "bigint"}) }, Constraints = new List<WConstraintDefinition> { new WUniqueConstraintDefinition { Clustered = true, IsPrimaryKey = true } } }, new WColumnDefinition { ColumnIdentifier = new Identifier {Value = "srcCnt"}, DataType = new WParameterizedDataTypeReference { Name = new WSchemaObjectName(new Identifier {Value = "int"}), }, }, }, Indexes = new List<WIndexDefinition>(), TableConstraints = new List<WConstraintDefinition>() } }); var tableVar = new WSpecialNamedTableReference { TableObjectName = new WSchemaObjectName(new Identifier { Value = "@" + deleteEdgeTempTable + "_1" }) }; var reversedTableVar = new WSpecialNamedTableReference { TableObjectName = new WSchemaObjectName(new Identifier { Value = "@" + deleteEdgeTempTable + "_2" }) }; WInsertSpecification[] insertStat = new WInsertSpecification[] { new WInsertSpecification { Target = tableVar, InsertSource = new WSelectInsertSource { Select = edgeSelectQuery } }, new WInsertSpecification { Target = reversedTableVar, InsertSource = new WSelectInsertSource { Select = reversedEdgeSelectQuery } } }; // Update statments for edge and reversed edge WUpdateSpecification[] updateStat = new WUpdateSpecification[] { new WUpdateSpecification { Target = new WNamedTableReference { TableObjectName = sourceNameTable.TableObjectName }, SetClauses = new List<WSetClause> {setClauses[0], setClauses[2]}, FromClause = new WFromClause { TableReferences = new WTableReference[] { new WQueryDerivedTable { QueryExpr = new WSelectQueryBlock { SelectElements = new List<WSelectElement>() { new WSelectStarExpression() }, FromClause = new WFromClause { TableReferences = new List<WTableReference> {tableVar} } }, Alias = new Identifier {Value = deleteEdgeTableName}, }, } }, WhereClause = whereClause, TopRowFilter = node.TopRowFilter }, new WUpdateSpecification { Target = new WNamedTableReference { TableObjectName = sinkNameTable.TableObjectName }, SetClauses = new List<WSetClause> {setClauses[1]}, FromClause = new WFromClause { TableReferences = new WTableReference[] { new WQueryDerivedTable { Alias = new Identifier {Value = deleteEdgeTableName}, QueryExpr = new WSelectQueryBlock { SelectElements = new List<WSelectElement>() { new WSelectStarExpression() }, FromClause = new WFromClause { TableReferences = new List<WTableReference> {reversedTableVar} } } }, } }, WhereClause = whereClause, TopRowFilter = node.TopRowFilter }, }; bool sourceFirst = compare < 0; if (sourceFirst) { // With expression res.Add(deleteCommonTableExpr); res.Add(insertStat[0]); res.Add(updateStat[0]); res.Add(new WSqlUnknownStatement { Statement = String.Format("DELETE FROM @{0}_1", deleteEdgeTempTable) }); // With expression res.Add(deleteCommonTableExpr); res.Add(insertStat[1]); res.Add(updateStat[1]); res.Add(new WSqlUnknownStatement { Statement = String.Format("DELETE FROM @{0}_2", deleteEdgeTempTable) }); } else { // With expression res.Add(deleteCommonTableExpr); res.Add(insertStat[1]); res.Add(updateStat[1]); res.Add(new WSqlUnknownStatement { Statement = String.Format("DELETE FROM @{0}_2", deleteEdgeTempTable) }); // With expression res.Add(deleteCommonTableExpr); res.Add(insertStat[0]); res.Add(updateStat[0]); res.Add(new WSqlUnknownStatement { Statement = String.Format("DELETE FROM @{0}_1", deleteEdgeTempTable) }); } } }
private void UpdateMapping(WSelectQueryBlock expr) { if (expr == null) { throw new EdgeViewException("Invalid Select Statement in CREATE NODE VIEW"); } if (expr.FromClause.TableReferences.Count != 1) { throw new EdgeViewException( "Only one node table can be specified in each select statemtn of CREATE NODE VIEW"); } var tableRef = expr.FromClause.TableReferences.First() as WNamedTableReference; if (tableRef == null) { throw new EdgeViewException("Invalid node table"); } var schema = tableRef.TableObjectName.DatabaseIdentifier == null ? "dbo" : tableRef.TableObjectName.DatabaseIdentifier.Value; if (string.Compare(schema, _schema, StringComparison.OrdinalIgnoreCase) != 0) { throw new NodeViewException("All the node tables should be in the same schema as the node view"); } var tableRefName = tableRef.TableObjectName.SchemaIdentifier.Value; var edgeName = tableRef.TableObjectName.BaseIdentifier.Value; _edges.Add(new Tuple <string, string>(tableRefName, edgeName)); if (_defaultMerge) { if (expr.SelectElements.Count != 1) { throw new EdgeViewException( "Select element in each statement should be consistent to the first statement"); } if (!(expr.SelectElements.First() is WSelectStarExpression)) { throw new EdgeViewException( "Select element should be '*' for " + tableRefName + "." + edgeName); } } else if (_noAttributes) { if (expr.SelectElements.Count != 1) { throw new EdgeViewException( "Select element in each statement should be consistent to the first statement"); } var element = expr.SelectElements.First(); var valueExpr = element as WSelectScalarExpression; if (valueExpr == null) { throw new EdgeViewException( "Select element should be null for " + tableRefName + "." + edgeName); } var nullExpr = valueExpr.SelectExpr as WValueExpression; if (nullExpr == null || nullExpr.Value.ToLower() != "null") { throw new EdgeViewException( "Select element should be null for " + tableRefName + "." + edgeName); } } else if (!_attributeMapping.Any()) { if (expr.SelectElements.Count == 1 && expr.SelectElements.First() is WSelectStarExpression) { _defaultMerge = true; return; } foreach (var selectElement in expr.SelectElements) { var scalarElement = selectElement as WSelectScalarExpression; if (scalarElement == null) { throw new EdgeViewException("Invalid select element"); } string newColName = scalarElement.ColumnName; var oldColRefExpression = scalarElement.SelectExpr as WColumnReferenceExpression; if (oldColRefExpression == null) { var oldColValueExpression = scalarElement.SelectExpr as WValueExpression; if (oldColValueExpression == null) { throw new NodeViewException( "Each select element should be null or reference to a column"); } if (oldColValueExpression.Value.ToLower() != "null") { throw new NodeViewException( "Each select element should be null or reference to a column"); } if (string.IsNullOrEmpty(newColName)) { if (expr.SelectElements.Count == 1) { _noAttributes = true; return; } throw new EdgeViewException( "Column name should be specified for the first select null expression"); } _attributeMapping.Add( new Tuple <string, List <Tuple <string, string, string> > >(newColName.ToLower(), new List <Tuple <string, string, string> >())); } else { string oldColName = oldColRefExpression.MultiPartIdentifier.Identifiers.Last().Value; if (string.IsNullOrEmpty(newColName)) { newColName = oldColName; } _attributeMapping.Add( new Tuple <string, List <Tuple <string, string, string> > >(newColName.ToLower(), new List <Tuple <string, string, string> > { new Tuple <string, string, string>(tableRefName.ToLower(), edgeName.ToLower(), oldColName.ToLower()) })); } _edgeAttribute.Add(newColName); } } else { for (int i = 0; i < expr.SelectElements.Count; i++) { var scalarElement = expr.SelectElements[i] as WSelectScalarExpression; if (scalarElement == null) { throw new NodeViewException("Invalid select element"); } var oldColRefExpression = scalarElement.SelectExpr as WColumnReferenceExpression; if (oldColRefExpression == null) { var oldColValueExpression = scalarElement.SelectExpr as WValueExpression; if (oldColValueExpression == null || oldColValueExpression.Value.ToLower() != "null") { throw new EdgeViewException( "Each select element should be null or reference to a column"); } } else { string oldColName = oldColRefExpression.MultiPartIdentifier.Identifiers.Last().Value; _attributeMapping[i].Item2.Add(new Tuple <string, string, string>(tableRefName.ToLower(), edgeName.ToLower(), oldColName.ToLower())); } } } //expr.SelectElements.Add(new WSelectScalarExpression //{ // SelectExpr = // new WColumnReferenceExpression // { // MultiPartIdentifier = new WMultiPartIdentifier(new Identifier {Value = "Sink"}) // } //}); //expr.SelectElements.Add(new WSelectScalarExpression //{ // SelectExpr = // new WColumnReferenceExpression // { // MultiPartIdentifier = new WMultiPartIdentifier(new Identifier { Value = "Src" }) // } //}); //expr.FromClause.TableReferences[0] = new WNamedTableReference //{ // TableObjectName = // new WSchemaObjectName(new Identifier // { // Value = string.Format("{0}_{1}_{2}_Sampling", schema, tableRefName, edgeName) // }) //}; }
public virtual void Visit(WSelectQueryBlock node) { node.AcceptChildren(this); }
private WCommonTableExpression ConstructDeleteEdgeSelect(WSelectQueryBlock node, string edgeAlias, string tempTableName ,out WTableReference sinkTable, out WTableReference sourceTable) { sourceTable = null; sinkTable = null; var sourceTableScalar = node.SelectElements[0] as WSelectScalarExpression; if (sourceTableScalar == null) return null; var sourceTableColumn = sourceTableScalar.SelectExpr as WColumnReferenceExpression; if (sourceTableColumn == null) return null; var sourceTableName = sourceTableColumn.MultiPartIdentifier.Identifiers.Last().Value; var sourceNodeIdExpr = new WColumnReferenceExpression(); foreach (var identifier in sourceTableColumn.MultiPartIdentifier.Identifiers) sourceNodeIdExpr.Add(identifier); sourceNodeIdExpr.AddIdentifier("GlobalNodeId"); var collectVarVisitor = new CollectVariableVisitor(); var context = collectVarVisitor.Invoke(node); if (!context.NodeTableDictionary.Any()) { throw new GraphViewException("Missing From Clause in Delete Edge statement"); } WColumnReferenceExpression sinkNodeIdExpr = null; sourceTable = context[sourceTableName]; var groupByParams = new List<WScalarExpression>(); for (var index = 1; index < node.SelectElements.Count; ++index) { var element = node.SelectElements[index] as WSelectScalarExpression; if (element == null) return null; //sink table if (index == 1) { var sinkTableColumn = element.SelectExpr as WColumnReferenceExpression; if (sinkTableColumn == null) return null; var sinkTableName = sinkTableColumn.MultiPartIdentifier.Identifiers.Last().Value; sinkTable = context[sinkTableName]; sinkNodeIdExpr = new WColumnReferenceExpression(); foreach (var identifier in sinkTableColumn.MultiPartIdentifier.Identifiers) sinkNodeIdExpr.Add(identifier); sinkNodeIdExpr.AddIdentifier("GlobalNodeId"); } } var sourceNodeId = new WSelectScalarExpression { SelectExpr = sourceNodeIdExpr, ColumnName = "src", }; var sinkNodeId = new WSelectScalarExpression { SelectExpr = sinkNodeIdExpr, ColumnName = "sink" }; var edgeId = new WSelectScalarExpression { SelectExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier( new Identifier[] { new Identifier {Value = edgeAlias}, new Identifier {Value = "Edgeid"} } ) }, ColumnName = "edgeid" }; var elements = new List<WSelectElement> { sourceNodeId, sinkNodeId, edgeId }; return new WCommonTableExpression { ExpressionName = new Identifier { Value = tempTableName }, QueryExpression = new WSelectQueryBlock { FromClause = node.FromClause, WhereClause = new WWhereClause { SearchCondition = node.WhereClause.SearchCondition }, HavingClause = node.HavingClause, OrderByClause = node.OrderByClause, TopRowFilter = node.TopRowFilter, UniqueRowFilter = node.UniqueRowFilter, SelectElements = elements, MatchClause = node.MatchClause, } }; }
// Repeat algorithm // Firstly, we generate the repeatQueryBlock in order that we can get the initial query // Secondly, we generate the inputVariableVistorMap via repeatInputVariable.ProjectedProperties, // untilInputVariable.ProjectedProperties, emitInputVariable.ProjectedProperties and ProjectedProperties. // Generally, these properties are related to the input every time, but in repeatQueryBlock, these are // just related to the input of the first time. Therefore, we need to replace these after. // Thirdly, we need to generate the firstQueryExpr and the selectColumnExpr of repeatQueryBlock. Pay // attention, we need to repeatQueryBlock again because we need more properties about the output of // the last step in repeat-step. These properties are populated in the second step. // Fourthly, we use the inputVariableVistorMap to replace the columns in the repeatQueryBlock. But we // should not change the columns in path-step. Because if we generate the path in the repeat-step, the // path consists of // 1. the previous steps before the repeat-step // 2. the local path(_path) in the repeat-step // Keep in mind that the initial _path is null, the _path includes all steps as long as they are in // the repeat-step except for the first input. And the _path after the first pass includes the last step // in the repeat-step. So the path must include the two part. That means all columns in path-step should // not be replaced. Here, we use the ModifyRepeatInputVariablesVisitor to finish this work. If it visits // WPathTableReference, it does nothing, otherwise, it will replace the columns according to the // inputVariableVistorMap. public override WTableReference ToTableReference() { //The following two variables are used for manually creating SelectScalarExpression of repeat List <WSelectScalarExpression> firstSelectList = new List <WSelectScalarExpression>(); List <WSelectScalarExpression> secondSelectList = new List <WSelectScalarExpression>(); WScalarExpression firstSelectColumn, secondSelectColumn, firstExpr, secondExpr; // The following map is used to replace columns of the first input to columns of the repeat input // such as N_0.id -> R.key_0 string aliasName; Tuple <string, string> key, value; Dictionary <Tuple <string, string>, Tuple <string, string> > inputVariableVistorMap = new Dictionary <Tuple <string, string>, Tuple <string, string> >(); // We should generate the syntax tree firstly // Some variables will populate ProjectProperty only when we call the ToTableReference function where they appear. WRepeatConditionExpression repeatConditionExpr = this.GetRepeatConditionExpression(); WSelectQueryBlock repeatQueryBlock = this.RepeatContext.ToSelectQueryBlock(); GremlinVariable repeatInputVariable = this.RepeatContext.VariableList.First(); GremlinVariable repeatOutputVariable = this.RepeatContext.PivotVariable; GremlinVariable realInputVariable = this.InputVariable.RealVariable; GremlinVariable repeatPivotVariable = this.RepeatContext.PivotVariable; // this.DefaultProperty key = new Tuple <string, string>(this.GetVariableName(), this.DefaultProperty()); value = key; inputVariableVistorMap[key] = value; key = new Tuple <string, string>(realInputVariable.GetVariableName(), realInputVariable.DefaultProperty()); value = new Tuple <string, string>(GremlinKeyword.RepeatInitalTableName, this.DefaultProperty()); inputVariableVistorMap[key] = value; firstExpr = realInputVariable.DefaultProjection().ToScalarExpression(); secondExpr = repeatOutputVariable.DefaultProjection().ToScalarExpression(); firstSelectList.Add(SqlUtil.GetSelectScalarExpr(firstExpr, this.DefaultProperty())); secondSelectList.Add(SqlUtil.GetSelectScalarExpr(secondExpr, this.DefaultProperty())); foreach (string property in this.ProjectedProperties) { if (property == GremlinKeyword.Path) { firstSelectList.Add(SqlUtil.GetSelectScalarExpr(SqlUtil.GetValueExpr(null), GremlinKeyword.Path)); secondSelectList.Add( SqlUtil.GetSelectScalarExpr( this.RepeatContext.ContextLocalPath.DefaultProjection().ToScalarExpression(), GremlinKeyword.Path)); } else { key = new Tuple <string, string>(this.GetVariableName(), property); value = key; inputVariableVistorMap[key] = value; key = new Tuple <string, string>(realInputVariable.GetVariableName(), property); value = new Tuple <string, string>(GremlinKeyword.RepeatInitalTableName, property); inputVariableVistorMap[key] = value; firstExpr = realInputVariable.ProjectedProperties.Contains(property) ? realInputVariable.GetVariableProperty(property).ToScalarExpression() : SqlUtil.GetValueExpr(null); secondExpr = repeatOutputVariable.ProjectedProperties.Contains(property) ? this.RepeatContext.PivotVariable.GetVariableProperty(property).ToScalarExpression() : SqlUtil.GetValueExpr(null); firstSelectList.Add(SqlUtil.GetSelectScalarExpr(firstExpr, property)); secondSelectList.Add(SqlUtil.GetSelectScalarExpr(secondExpr, property)); } } // repeatInputVariable.DefaultProperty() key = new Tuple <string, string>(repeatInputVariable.GetVariableName(), repeatInputVariable.DefaultProperty()); if (!inputVariableVistorMap.Keys.Contains(key)) { aliasName = this.GenerateKey(); value = new Tuple <string, string>(GremlinKeyword.RepeatInitalTableName, aliasName); inputVariableVistorMap[key] = value; firstSelectColumn = repeatInputVariable.DefaultProjection().ToScalarExpression() as WColumnReferenceExpression; secondSelectColumn = repeatPivotVariable.DefaultProjection().ToScalarExpression() as WColumnReferenceExpression; firstSelectList.Add(SqlUtil.GetSelectScalarExpr(firstSelectColumn, aliasName)); secondSelectList.Add(SqlUtil.GetSelectScalarExpr(secondSelectColumn, aliasName)); } foreach (string property in repeatInputVariable.ProjectedProperties) { key = new Tuple <string, string>(repeatInputVariable.GetVariableName(), property); if (!inputVariableVistorMap.Keys.Contains(key)) { aliasName = this.GenerateKey(); value = new Tuple <string, string>(GremlinKeyword.RepeatInitalTableName, aliasName); inputVariableVistorMap[key] = value; firstSelectColumn = repeatInputVariable.GetVariableProperty(property).ToScalarExpression() as WColumnReferenceExpression; secondSelectColumn = repeatPivotVariable.GetVariableProperty(property).ToScalarExpression() as WColumnReferenceExpression; firstSelectList.Add(SqlUtil.GetSelectScalarExpr(firstSelectColumn, aliasName)); secondSelectList.Add(SqlUtil.GetSelectScalarExpr(secondSelectColumn, aliasName)); } } if (this.RepeatCondition.TerminationContext != null && this.RepeatCondition.TerminationContext.VariableList.Count > 0) { GremlinVariable untilInputVariable = this.RepeatCondition.TerminationContext.VariableList.First(); // untilInputVariable.DefaultProperty() key = new Tuple <string, string>(untilInputVariable.GetVariableName(), untilInputVariable.DefaultProperty()); if (!inputVariableVistorMap.Keys.Contains(key)) { aliasName = this.GenerateKey(); value = new Tuple <string, string>(GremlinKeyword.RepeatInitalTableName, aliasName); inputVariableVistorMap[key] = value; firstSelectColumn = untilInputVariable.DefaultProjection().ToScalarExpression() as WColumnReferenceExpression; secondSelectColumn = repeatPivotVariable.DefaultProjection().ToScalarExpression() as WColumnReferenceExpression; firstSelectList.Add(SqlUtil.GetSelectScalarExpr(firstSelectColumn, aliasName)); secondSelectList.Add(SqlUtil.GetSelectScalarExpr(secondSelectColumn, aliasName)); } foreach (string property in untilInputVariable.ProjectedProperties) { key = new Tuple <string, string>(untilInputVariable.GetVariableName(), property); if (!inputVariableVistorMap.Keys.Contains(key)) { aliasName = this.GenerateKey(); value = new Tuple <string, string>(GremlinKeyword.RepeatInitalTableName, aliasName); inputVariableVistorMap[key] = value; firstSelectColumn = untilInputVariable.GetVariableProperty(property).ToScalarExpression() as WColumnReferenceExpression; secondSelectColumn = repeatPivotVariable.GetVariableProperty(property).ToScalarExpression() as WColumnReferenceExpression; firstSelectList.Add(SqlUtil.GetSelectScalarExpr(firstSelectColumn, aliasName)); secondSelectList.Add(SqlUtil.GetSelectScalarExpr(secondSelectColumn, aliasName)); } } } if (this.RepeatCondition.EmitContext != null && this.RepeatCondition.EmitContext.VariableList.Count > 0) { GremlinVariable emitInputVariable = this.RepeatCondition.EmitContext.VariableList.First(); // emitInputVariable.DefaultProperty() key = new Tuple <string, string>(emitInputVariable.GetVariableName(), emitInputVariable.DefaultProperty()); if (!inputVariableVistorMap.Keys.Contains(key)) { aliasName = this.GenerateKey(); value = new Tuple <string, string>(GremlinKeyword.RepeatInitalTableName, aliasName); inputVariableVistorMap[key] = value; firstSelectColumn = emitInputVariable.DefaultProjection().ToScalarExpression() as WColumnReferenceExpression; secondSelectColumn = repeatPivotVariable.DefaultProjection().ToScalarExpression() as WColumnReferenceExpression; firstSelectList.Add(SqlUtil.GetSelectScalarExpr(firstSelectColumn, aliasName)); secondSelectList.Add(SqlUtil.GetSelectScalarExpr(secondSelectColumn, aliasName)); } foreach (string property in emitInputVariable.ProjectedProperties) { key = new Tuple <string, string>(emitInputVariable.GetVariableName(), property); if (!inputVariableVistorMap.Keys.Contains(key)) { aliasName = this.GenerateKey(); value = new Tuple <string, string>(GremlinKeyword.RepeatInitalTableName, aliasName); inputVariableVistorMap[key] = value; firstSelectColumn = emitInputVariable.GetVariableProperty(property).ToScalarExpression() as WColumnReferenceExpression; secondSelectColumn = repeatPivotVariable.GetVariableProperty(property).ToScalarExpression() as WColumnReferenceExpression; firstSelectList.Add(SqlUtil.GetSelectScalarExpr(firstSelectColumn, aliasName)); secondSelectList.Add(SqlUtil.GetSelectScalarExpr(secondSelectColumn, aliasName)); } } } WSelectQueryBlock firstQueryExpr = new WSelectQueryBlock(); foreach (WSelectScalarExpression selectColumnExpr in firstSelectList) { firstQueryExpr.SelectElements.Add(selectColumnExpr); } repeatQueryBlock = this.RepeatContext.ToSelectQueryBlock(); repeatQueryBlock.SelectElements.Clear(); foreach (WSelectScalarExpression selectColumnExpr in secondSelectList) { repeatQueryBlock.SelectElements.Add(selectColumnExpr); } //Then we will use the inputVariableVistorMap to replace ColumnRefernceExpression in the syntax tree //which matchs n_0.id to R_0.key_0 except for WPathTableReference new ModifyRepeatInputVariablesVisitor().Invoke(repeatQueryBlock, inputVariableVistorMap); new ModifyRepeatInputVariablesVisitor().Invoke(repeatConditionExpr, inputVariableVistorMap); List <WScalarExpression> repeatParameters = new List <WScalarExpression>() { SqlUtil.GetScalarSubquery(SqlUtil.GetBinaryQueryExpr(firstQueryExpr, repeatQueryBlock)), repeatConditionExpr }; WSchemaObjectFunctionTableReference tableRef = SqlUtil.GetFunctionTableReference(GremlinKeyword.func.Repeat, repeatParameters, this.GetVariableName()); return(SqlUtil.GetCrossApplyTableReference(tableRef)); }
private void UpdateMapping(WSelectQueryBlock expr) { if (expr == null) throw new EdgeViewException("Invalid Select Statement in CREATE NODE VIEW"); if (expr.FromClause.TableReferences.Count != 1) throw new EdgeViewException( "Only one node table can be specified in each select statemtn of CREATE NODE VIEW"); var tableRef = expr.FromClause.TableReferences.First() as WNamedTableReference; if (tableRef == null) throw new EdgeViewException("Invalid node table"); var schema = tableRef.TableObjectName.DatabaseIdentifier == null ? "dbo" : tableRef.TableObjectName.DatabaseIdentifier.Value; if (string.Compare(schema, _schema, StringComparison.OrdinalIgnoreCase) != 0) throw new NodeViewException("All the node tables should be in the same schema as the node view"); var tableRefName = tableRef.TableObjectName.SchemaIdentifier.Value; var edgeName = tableRef.TableObjectName.BaseIdentifier.Value; _edges.Add(new Tuple<string, string>(tableRefName, edgeName)); if (_defaultMerge) { if (expr.SelectElements.Count != 1) throw new EdgeViewException( "Select element in each statement should be consistent to the first statement"); if (!(expr.SelectElements.First() is WSelectStarExpression)) throw new EdgeViewException( "Select element should be '*' for " + tableRefName + "." + edgeName); } else if (_noAttributes) { if (expr.SelectElements.Count != 1) throw new EdgeViewException( "Select element in each statement should be consistent to the first statement"); var element = expr.SelectElements.First(); var valueExpr = element as WSelectScalarExpression; if (valueExpr == null) throw new EdgeViewException( "Select element should be null for " + tableRefName + "." + edgeName); var nullExpr = valueExpr.SelectExpr as WValueExpression; if (nullExpr == null || nullExpr.Value.ToLower()!="null") throw new EdgeViewException( "Select element should be null for " + tableRefName + "." + edgeName); } else if (!_attributeMapping.Any()) { if (expr.SelectElements.Count == 1 && expr.SelectElements.First() is WSelectStarExpression) { _defaultMerge = true; return; } foreach (var selectElement in expr.SelectElements) { var scalarElement = selectElement as WSelectScalarExpression; if (scalarElement == null) throw new EdgeViewException("Invalid select element"); string newColName = scalarElement.ColumnName; var oldColRefExpression = scalarElement.SelectExpr as WColumnReferenceExpression; if (oldColRefExpression == null) { var oldColValueExpression = scalarElement.SelectExpr as WValueExpression; if (oldColValueExpression == null) throw new NodeViewException( "Each select element should be null or reference to a column"); if (oldColValueExpression.Value.ToLower() != "null") throw new NodeViewException( "Each select element should be null or reference to a column"); if (string.IsNullOrEmpty(newColName)) { if (expr.SelectElements.Count == 1) { _noAttributes = true; return; } throw new EdgeViewException( "Column name should be specified for the first select null expression"); } _attributeMapping.Add( new Tuple<string, List<Tuple<string, string, string>>>(newColName.ToLower(), new List<Tuple<string, string, string>>())); } else { string oldColName = oldColRefExpression.MultiPartIdentifier.Identifiers.Last().Value; if (string.IsNullOrEmpty(newColName)) newColName = oldColName; _attributeMapping.Add( new Tuple<string, List<Tuple<string, string, string>>>(newColName.ToLower(), new List<Tuple<string, string, string>> { new Tuple<string, string, string>(tableRefName.ToLower(), edgeName.ToLower(), oldColName.ToLower()) })); } _edgeAttribute.Add(newColName); } } else { for (int i = 0; i < expr.SelectElements.Count; i++) { var scalarElement = expr.SelectElements[i] as WSelectScalarExpression; if (scalarElement == null) throw new NodeViewException("Invalid select element"); var oldColRefExpression = scalarElement.SelectExpr as WColumnReferenceExpression; if (oldColRefExpression == null) { var oldColValueExpression = scalarElement.SelectExpr as WValueExpression; if (oldColValueExpression == null || oldColValueExpression.Value.ToLower() != "null") throw new EdgeViewException( "Each select element should be null or reference to a column"); } else { string oldColName = oldColRefExpression.MultiPartIdentifier.Identifiers.Last().Value; _attributeMapping[i].Item2.Add(new Tuple<string, string, string>(tableRefName.ToLower(), edgeName.ToLower(), oldColName.ToLower())); } } } //expr.SelectElements.Add(new WSelectScalarExpression //{ // SelectExpr = // new WColumnReferenceExpression // { // MultiPartIdentifier = new WMultiPartIdentifier(new Identifier {Value = "Sink"}) // } //}); //expr.SelectElements.Add(new WSelectScalarExpression //{ // SelectExpr = // new WColumnReferenceExpression // { // MultiPartIdentifier = new WMultiPartIdentifier(new Identifier { Value = "Src" }) // } //}); //expr.FromClause.TableReferences[0] = new WNamedTableReference //{ // TableObjectName = // new WSchemaObjectName(new Identifier // { // Value = string.Format("{0}_{1}_{2}_Sampling", schema, tableRefName, edgeName) // }) //}; }
private WMultiCommonTableExpression ConstructDeleteEdgeSelect(WSelectQueryBlock node, WEdgeColumnReferenceExpression edgeCol, string tempTableName ,out WTableReference sinkTable, out WTableReference sourceTable, ref bool hasReversedEdge) { sourceTable = null; sinkTable = null; string sourceTableName = null; string sinkTableName = null; var sourceTableScalar = node.SelectElements[0] as WSelectScalarExpression; if (sourceTableScalar == null) return null; var sourceTableColumn = sourceTableScalar.SelectExpr as WColumnReferenceExpression; if (sourceTableColumn == null) return null; sourceTableName = sourceTableColumn.MultiPartIdentifier.Identifiers.Last().Value; var sourceNodeIdExpr = new WColumnReferenceExpression(); foreach (var identifier in sourceTableColumn.MultiPartIdentifier.Identifiers) sourceNodeIdExpr.Add(identifier); sourceNodeIdExpr.AddIdentifier("GlobalNodeId"); var collectVarVisitor = new CollectVariableVisitor(); var context = collectVarVisitor.Invoke(node); if (!context.NodeTableDictionary.Any()) { throw new GraphViewException("Missing From Clause in Delete Edge statement"); } WColumnReferenceExpression sinkNodeIdExpr = null; sourceTable = context[sourceTableName]; var groupByParams = new List<WScalarExpression>(); for (var index = 1; index < node.SelectElements.Count; ++index) { var element = node.SelectElements[index] as WSelectScalarExpression; if (element == null) return null; //sink table if (index == 1) { var sinkTableColumn = element.SelectExpr as WColumnReferenceExpression; if (sinkTableColumn == null) return null; sinkTableName = sinkTableColumn.MultiPartIdentifier.Identifiers.Last().Value; sinkTable = context[sinkTableName]; sinkNodeIdExpr = new WColumnReferenceExpression(); foreach (var identifier in sinkTableColumn.MultiPartIdentifier.Identifiers) sinkNodeIdExpr.Add(identifier); sinkNodeIdExpr.AddIdentifier("GlobalNodeId"); } } var sourceNodeId = new WSelectScalarExpression { SelectExpr = sourceNodeIdExpr, ColumnName = "src", }; var sinkNodeId = new WSelectScalarExpression { SelectExpr = sinkNodeIdExpr, ColumnName = "sink" }; var edgeId = new WSelectScalarExpression { SelectExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier( new Identifier[] { new Identifier {Value = edgeCol.Alias??string.Format("{0}_{1}_{2}",sourceTableName,edgeCol.MultiPartIdentifier.Identifiers.Last().Value,sinkTableName)}, new Identifier {Value = "Edgeid"} }) }, ColumnName = "edgeid" }; var sourceTableNameRef = sourceTable as WNamedTableReference; if (sourceTableNameRef == null) throw new GraphViewException("Source table of DELETE EDGE statement should be a named table reference."); var sinkTableNameRef = sinkTable as WNamedTableReference; if (sinkTableNameRef == null) throw new GraphViewException("Sink table of DELETE EDGE statement should be a named table reference."); int compare = string.CompareOrdinal(sourceTableNameRef.TableObjectName.ToString(), sinkTableNameRef.TableObjectName.ToString()); using (var command = Tx.Connection.CreateCommand()) { command.Transaction = Tx; command.Parameters.AddWithValue("@schema", WNamedTableReference.SchemaNameToTuple(sourceTableNameRef.TableObjectName).Item1); command.Parameters.AddWithValue("@tableName", sourceTableNameRef.TableObjectName.Identifiers.Last().Value); command.Parameters.AddWithValue("@colName", edgeCol.MultiPartIdentifier.Identifiers.Last().Value); command.Parameters.AddWithValue("@role", WNodeTableColumnRole.Edge); command.CommandText = string.Format(@" SELECT NodeColumn.HasReversedEdge FROM [{0}] AS NodeColumn WHERE NodeColumn.TableSchema = @schema and NodeColumn.TableName = @tableName and NodeColumn.ColumnName = @colName and NodeColumn.ColumnRole = @role", GraphViewConnection.MetadataTables[1]); using (var reader = command.ExecuteReader()) { if (reader.Read()) { hasReversedEdge = bool.Parse(reader[0].ToString()); } } } var reversedEdgeName = sourceTableNameRef.TableObjectName.Identifiers.Last().Value + "_" + edgeCol.MultiPartIdentifier.Identifiers.Last().Value + "Reversed"; var reversedEdgeId = new WSelectScalarExpression { SelectExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier( new Identifier[] { new Identifier {Value = edgeCol.Alias??string.Format("{0}_{1}_{2}",sourceTableName,edgeCol.MultiPartIdentifier.Identifiers.Last().Value,sinkTableName)}, new Identifier {Value = "Edgeid"} }) }, ColumnName = "edgeid" }; var elements = new List<WSelectElement> { sourceNodeId, sinkNodeId, edgeId }; var reversedElements = new List<WSelectElement> { sourceNodeId, sinkNodeId, reversedEdgeId, }; return new WMultiCommonTableExpression { WCommonTableExpressions = new List<WCommonTableExpression>() { new WCommonTableExpression { ExpressionName = new Identifier { Value = hasReversedEdge && compare == 0 ? tempTableName + "_1" : tempTableName }, QueryExpression = new WSelectQueryBlock { FromClause = node.FromClause, WhereClause = new WWhereClause { SearchCondition = node.WhereClause.SearchCondition }, HavingClause = node.HavingClause, OrderByClause = node.OrderByClause, TopRowFilter = node.TopRowFilter, UniqueRowFilter = node.UniqueRowFilter, SelectElements = elements, MatchClause = node.MatchClause, } }, new WCommonTableExpression { ExpressionName = new Identifier { Value = hasReversedEdge && compare == 0 ? tempTableName + "_2" : tempTableName }, QueryExpression = new WSelectQueryBlock { FromClause = node.FromClause, WhereClause = hasReversedEdge ? new WWhereClause { SearchCondition = ObjectExtensions.Copy(node.WhereClause.SearchCondition), } : node.WhereClause, HavingClause = node.HavingClause, OrderByClause = node.OrderByClause, TopRowFilter = node.TopRowFilter, UniqueRowFilter = node.UniqueRowFilter, SelectElements = hasReversedEdge ? reversedElements : elements, MatchClause = hasReversedEdge? ConstructReversedMatchClause(node, context) : node.MatchClause, } }, } }; }
public override WTableReference ToTableReference() { if (RepeatContext.ContextLocalPath != null) { RepeatContext.ContextLocalPath.PathList.Insert(0, null); RepeatContext.ContextLocalPath.IsInRepeatContext = true; } //The following two variables are used for manually creating SelectScalarExpression of repeat List <WSelectScalarExpression> repeatFirstSelect = new List <WSelectScalarExpression>(); List <WSelectScalarExpression> repeatSecondSelect = new List <WSelectScalarExpression>(); // The following two variables are used for Generating a Map // such as N_0.id -> key_0 // Then we will use this map to replace ColumnRefernceExpression in the syntax tree which matchs n_0.id to R_0.key_0 Dictionary <WColumnReferenceExpression, string> repeatVistorMap = new Dictionary <WColumnReferenceExpression, string>(); Dictionary <WColumnReferenceExpression, string> conditionVistorMap = new Dictionary <WColumnReferenceExpression, string>(); //We should generate the syntax tree firstly //Some variables will populate ProjectProperty only when we call the ToTableReference function where they appear. WRepeatConditionExpression repeatConditionExpr = GetRepeatConditionExpression(); WSelectQueryBlock repeatQueryBlock = RepeatContext.ToSelectQueryBlock(); // TODO: explain this step in detail var repeatNewToOldSelectedVarMap = GetNewToOldSelectedVarMap(RepeatContext); if (!repeatNewToOldSelectedVarMap.ContainsKey(RepeatContext.PivotVariable)) { repeatNewToOldSelectedVarMap[RepeatContext.PivotVariable] = RepeatContext.VariableList.First(); } foreach (var pair in repeatNewToOldSelectedVarMap) { GremlinVariable newVariable = pair.Key; GremlinVariable oldVariable = pair.Value; foreach (var property in pair.Value.ProjectedProperties) { var aliasName = GenerateKey(); var firstSelectColumn = oldVariable.GetVariableProperty(property).ToScalarExpression() as WColumnReferenceExpression; var secondSelectColumn = newVariable.GetVariableProperty(property).ToScalarExpression() as WColumnReferenceExpression; repeatFirstSelect.Add(SqlUtil.GetSelectScalarExpr(firstSelectColumn, aliasName)); repeatSecondSelect.Add(SqlUtil.GetSelectScalarExpr(secondSelectColumn, aliasName)); repeatVistorMap[firstSelectColumn.Copy()] = aliasName; } } if (RepeatCondition.TerminationContext != null && RepeatCondition.TerminationContext.VariableList.Count > 0) { var terminatedNewToOldSelectedVarMap = GetNewToOldSelectedVarMap(RepeatCondition.TerminationContext); if (!terminatedNewToOldSelectedVarMap.ContainsKey(RepeatContext.PivotVariable)) { terminatedNewToOldSelectedVarMap[RepeatContext.PivotVariable] = RepeatCondition.TerminationContext.VariableList.First(); } foreach (var pair in terminatedNewToOldSelectedVarMap) { GremlinVariable newVariable = pair.Key; GremlinVariable oldVariable = pair.Value; foreach (var property in oldVariable.ProjectedProperties) { var aliasName = GenerateKey(); var firstSelectColumn = RepeatCondition.StartFromContext ? oldVariable.GetVariableProperty(property).ToScalarExpression() : SqlUtil.GetValueExpr(null); var secondSelectColumn = newVariable.GetVariableProperty(property).ToScalarExpression() as WColumnReferenceExpression; repeatFirstSelect.Add(SqlUtil.GetSelectScalarExpr(firstSelectColumn, aliasName)); repeatSecondSelect.Add(SqlUtil.GetSelectScalarExpr(secondSelectColumn, aliasName)); if (RepeatCondition.StartFromContext) { conditionVistorMap[(firstSelectColumn as WColumnReferenceExpression).Copy()] = aliasName; } else { conditionVistorMap[secondSelectColumn.Copy()] = aliasName; } } } } if (RepeatCondition.EmitContext != null && RepeatCondition.EmitContext.VariableList.Count > 0) { var terminatedNewToOldSelectedVarMap = GetNewToOldSelectedVarMap(RepeatCondition.EmitContext); if (!terminatedNewToOldSelectedVarMap.ContainsKey(RepeatContext.PivotVariable)) { terminatedNewToOldSelectedVarMap[RepeatContext.PivotVariable] = RepeatCondition.EmitContext.VariableList.First(); } foreach (var pair in terminatedNewToOldSelectedVarMap) { GremlinVariable newVariable = pair.Key; GremlinVariable oldVariable = pair.Value; foreach (var property in pair.Value.ProjectedProperties) { var aliasName = GenerateKey(); var firstSelectColumn = RepeatCondition.IsEmitContext ? oldVariable.GetVariableProperty(property).ToScalarExpression() : SqlUtil.GetValueExpr(null); var secondSelectColumn = newVariable.GetVariableProperty(property).ToScalarExpression() as WColumnReferenceExpression; repeatFirstSelect.Add(SqlUtil.GetSelectScalarExpr(firstSelectColumn, aliasName)); repeatSecondSelect.Add(SqlUtil.GetSelectScalarExpr(secondSelectColumn, aliasName)); if (RepeatCondition.IsEmitContext) { conditionVistorMap[(firstSelectColumn as WColumnReferenceExpression).Copy()] = aliasName; } else { conditionVistorMap[secondSelectColumn.Copy()] = aliasName; } } } } foreach (var property in ProjectedProperties) { if (property == GremlinKeyword.Path) { repeatFirstSelect.Add(SqlUtil.GetSelectScalarExpr(SqlUtil.GetValueExpr(null), GremlinKeyword.Path)); repeatSecondSelect.Add(SqlUtil.GetSelectScalarExpr(RepeatContext.ContextLocalPath.GetDefaultProjection().ToScalarExpression(), GremlinKeyword.Path)); continue; } WScalarExpression firstExpr = InputVariable.ProjectedProperties.Contains(property) ? InputVariable.GetVariableProperty(property).ToScalarExpression() : SqlUtil.GetValueExpr(null); WScalarExpression secondExpr = RepeatContext.PivotVariable.ProjectedProperties.Contains(property) ? RepeatContext.PivotVariable.GetVariableProperty(property).ToScalarExpression() : SqlUtil.GetValueExpr(null); repeatFirstSelect.Add(SqlUtil.GetSelectScalarExpr(firstExpr, property)); repeatSecondSelect.Add(SqlUtil.GetSelectScalarExpr(secondExpr, property)); } WSelectQueryBlock firstQueryExpr = new WSelectQueryBlock(); foreach (var selectColumnExpr in repeatFirstSelect) { firstQueryExpr.SelectElements.Add(selectColumnExpr); } repeatQueryBlock = RepeatContext.ToSelectQueryBlock(); repeatQueryBlock.SelectElements.Clear(); foreach (var selectColumnExpr in repeatSecondSelect) { repeatQueryBlock.SelectElements.Add(selectColumnExpr); } //Replace N_0.id -> R_0.key_0, when N_0 is a outer variable new ModifyColumnNameVisitor().Invoke(repeatQueryBlock, repeatVistorMap); new ModifyColumnNameVisitor().Invoke(repeatConditionExpr, conditionVistorMap); List <WScalarExpression> repeatParameters = new List <WScalarExpression>() { SqlUtil.GetScalarSubquery(SqlUtil.GetBinaryQueryExpr(firstQueryExpr, repeatQueryBlock)), repeatConditionExpr }; var tableRef = SqlUtil.GetFunctionTableReference(GremlinKeyword.func.Repeat, repeatParameters, GetVariableName()); return(SqlUtil.GetCrossApplyTableReference(tableRef)); }
private WMatchClause ConstructReversedMatchClause(WSelectQueryBlock node, WSqlTableContext context) { var result = new WMatchClause() { Paths = new List<WMatchPath>(), }; foreach (var path in node.MatchClause.Paths) { var reversedEdgeNameList = new List<string>(); var nodeList = new List<Tuple<WSchemaObjectName, WEdgeColumnReferenceExpression>>(); var edgeList = path.PathEdgeList; for (var i = 0; i < edgeList.Count; ++i) { var sourceTableName = edgeList[i].Item1.Identifiers.Last().Value; var edgeName = edgeList[i].Item2.MultiPartIdentifier.Identifiers.Last().Value; WTableReference sourceTable = context[sourceTableName]; var sourceTableObjectName = (sourceTable as WNamedTableReference).TableObjectName.Identifiers.Last().Value; reversedEdgeNameList.Add(sourceTableObjectName + "_" + edgeName + "Reversed"); } var sinkIdentifier = path.Tail.Identifiers.Last(); for (var i = edgeList.Count - 1; i >= 0; --i) { var nodeName = new WSchemaObjectName() { Identifiers = new List<Identifier> { new Identifier() { Value = sinkIdentifier.Value, QuoteType = sinkIdentifier.QuoteType, } }, }; var originalPath = edgeList[i].Item2; var edgeCol = new WEdgeColumnReferenceExpression() { ColumnType = ColumnType.Regular, Alias = originalPath.Alias, MaxLength = originalPath.MaxLength, MinLength = originalPath.MinLength, AttributeValueDict = originalPath.AttributeValueDict, MultiPartIdentifier = new WMultiPartIdentifier(new Identifier() { QuoteType = originalPath.MultiPartIdentifier.Identifiers.Last().QuoteType, Value = reversedEdgeNameList[i], }), }; nodeList.Add(new Tuple<WSchemaObjectName, WEdgeColumnReferenceExpression>(nodeName, edgeCol)); sinkIdentifier = edgeList[i].Item1.Identifiers.Last(); } result.Paths.Add(new WMatchPath { PathEdgeList = nodeList, Tail = edgeList[0].Item1, IsReversed = true, }); } return result; }
/// <summary> /// Marks the node table alias, if it has no outgoing edges in the graph pattern /// and its node properties are never referenced or projected in the query. /// Such node table aliases will not appear in the FROM clause in the translated /// query, but only be materialized in adjacency lists, i.e., table-valued functions. /// </summary> /// <param name="query"></param> /// <param name="graph"></param> private void OptimizeTail(WSelectQueryBlock query, MatchGraph graph) { var visitor = new CheckTableReferenceVisitor(); foreach (var connectedSubGraph in graph.ConnectedSubGraphs) { var toRemove = connectedSubGraph.Nodes.Where( node => node.Value.Neighbors.Count == 0 && !visitor.Invoke(query, node.Key, _context, _graphMetaData.ColumnsOfNodeTables) ) .ToArray(); foreach (var item in toRemove) { connectedSubGraph.IsTailNode[item.Value] = true; } } }
/// <summary> /// If a table alias in the MATCH clause is defined in an upper-level context, /// to be able to translate this MATCH clause, this table alias must be re-materialized /// in the FROM clause of the current context and joined with the corresponding table /// in the upper-level context. /// </summary> /// <param name="query">Select query</param> /// <param name="nodes">A collection of node alias and match node instance</param> private void RematerilizeExtrenalNodeTableReference(WSelectQueryBlock query, Dictionary<string, MatchNode> nodes) { var tableRefs = query.FromClause.TableReferences; var tableSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); var newTableRefs = new List<WTableReference>(); for (int index = 0; index < tableRefs.Count; ++index) { var table = tableRefs[index] as WNamedTableReference; if (table == null) { newTableRefs.Add(tableRefs[index]); continue; } if (!nodes.ContainsKey(table.ExposedName.Value)) { newTableRefs.Add(table); } else { tableSet.Add(table.ExposedName.Value); } } query.FromClause = new WFromClause { TableReferences = newTableRefs, }; WBooleanExpression whereCondiction = null; foreach (var node in nodes.Where(node => !tableSet.Contains(node.Key))) { node.Value.External = true; var newWhereCondition = new WBooleanComparisonExpression { ComparisonType = BooleanComparisonType.Equals, FirstExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier( new Identifier { Value = node.Key }, new Identifier { Value = "GlobalNodeId" }) }, SecondExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier( new Identifier { Value = node.Value.RefAlias }, new Identifier { Value = "GlobalNodeId" }) }, }; whereCondiction = WBooleanBinaryExpression.Conjunction(whereCondiction, newWhereCondition); } if (whereCondiction != null) { if (query.WhereClause == null) { query.WhereClause = new WWhereClause { SearchCondition = whereCondiction }; } else { if (query.WhereClause.SearchCondition == null) { query.WhereClause.SearchCondition = whereCondiction; } else { query.WhereClause.SearchCondition = new WBooleanBinaryExpression { BooleanExpressionType = BooleanBinaryExpressionType.And, FirstExpr = new WBooleanParenthesisExpression { Expression = query.WhereClause.SearchCondition }, SecondExpr = new WBooleanParenthesisExpression { Expression = whereCondiction } }; } } } }
public override void Visit(WSelectQueryBlock node) { if (node.SelectElements == null || node.SelectElements.Count < 2) { throw new GraphViewException("Numbers of select elements mismatch."); } var sourceTableScalar = node.SelectElements[0] as WSelectScalarExpression; if (sourceTableScalar == null) throw new GraphViewException("Source node id should be a scalar expression."); var sourceTableColumn = sourceTableScalar.SelectExpr as WColumnReferenceExpression; if (sourceTableColumn == null) throw new GraphViewException("Source node id column should be a column reference expression."); var sourceTableName = sourceTableColumn.MultiPartIdentifier.Identifiers.Last().Value; var sourceNodeIdExpr = new WColumnReferenceExpression(); foreach (var identifier in sourceTableColumn.MultiPartIdentifier.Identifiers) sourceNodeIdExpr.Add(identifier); sourceNodeIdExpr.AddIdentifier("GlobalNodeId"); if (node.SelectElements.Count < 2) throw new GraphViewException("Source and Sink tables should be specified in select elements."); if (node.GroupByClause != null) { throw new GraphViewException("GROUP BY clause is not allowed in INSERT EDGE statement."); } var collectVarVisitor = new CollectVariableVisitor(); var context = collectVarVisitor.Invoke(node); WColumnReferenceExpression sinkNodeIdExpr = null; for (var index = 1; index < node.SelectElements.Count; ++index) { var element = node.SelectElements[index] as WSelectScalarExpression; if (element == null) throw new GraphViewException("Edge property should be a scalar expression."); //sink table if (index == 1) { var sinkTableColumn = element.SelectExpr as WColumnReferenceExpression; if (sinkTableColumn == null) throw new GraphViewException("Sink node id column should be a column reference expression."); var sinkTableName = sinkTableColumn.MultiPartIdentifier.Identifiers.Last().Value; _sinkTable = context[sinkTableName]; sinkNodeIdExpr = new WColumnReferenceExpression(); foreach (var identifier in sinkTableColumn.MultiPartIdentifier.Identifiers) sinkNodeIdExpr.Add(identifier); sinkNodeIdExpr.AddIdentifier("GlobalNodeId"); } else { _edgeProperties.Add(element.SelectExpr); } } var sourceTable = context[sourceTableName] as WNamedTableReference; if (sourceTable == null) { throw new GraphViewException("Source table of INSERT EDGE statement should be a named table reference."); } var sourceNodeId = new WSelectScalarExpression { SelectExpr = sourceNodeIdExpr, ColumnName = "src", }; var sinkNodeId = new WSelectScalarExpression { SelectExpr = sinkNodeIdExpr, ColumnName = "sink" }; var elements = new List<WSelectElement> { sourceNodeId, sinkNodeId }; var result = new WCommonTableExpression { ExpressionName = new Identifier {Value = _tempTableName}, QueryExpression = new WSelectQueryBlock { FromClause = node.FromClause, WhereClause = new WWhereClause { SearchCondition = node.WhereClause.SearchCondition }, HavingClause = node.HavingClause, OrderByClause = node.OrderByClause, TopRowFilter = node.TopRowFilter, UniqueRowFilter = node.UniqueRowFilter, SelectElements = elements, MatchClause = node.MatchClause } }; _result = result; }
/// <summary> /// If paths is referenced in the MATCH clause, checks the SELECT elements to decide whether the /// paths informaion is needed to be displayed. If PathAlias.* occurs in the SELECT elements, /// sets the corresponding bool value in MatchPath, and replaces this element with /// an scalar function to display readable path information /// </summary> /// <param name="query">Select query</param> /// <param name="pathDictionary">A collection of path alias and match path instance</param> private void TransformPathInfoDisplaySelectElement(WSelectQueryBlock query, Dictionary<string, MatchPath> pathDictionary) { if (pathDictionary!=null && pathDictionary.Any()) { List<WSelectElement> newSelectElements = new List<WSelectElement>(); foreach (var selectElement in query.SelectElements) { var starElement = selectElement as WSelectStarExpression; if (starElement != null && starElement.Qulifier != null) { var colName = starElement.Qulifier.Identifiers[starElement.Qulifier.Count - 1].Value; MatchPath path; if (pathDictionary.TryGetValue(colName, out path)) { path.ReferencePathInfo = true; string schema = path.BindNodeTableObjName.SchemaIdentifier.Value; string tableName = path.BindNodeTableObjName.BaseIdentifier.Value; string pathName = path.EdgeColumn.MultiPartIdentifier.Identifiers.Last().Value; var parameters = new List<WScalarExpression> { new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier(new Identifier() {Value = path.EdgeAlias}, new Identifier() {Value = "PathMessage"}) }, }; if ( _graphMetaData.NodeViewMapping.ContainsKey( WNamedTableReference.SchemaNameToTuple(path.SinkNode.NodeTableObjectName))) { parameters.Add(new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier(new Identifier() { Value = path.SinkNode.RefAlias }, new Identifier() { Value = "_NodeType" }) }); parameters.Add(new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier(new Identifier() { Value = path.SinkNode.RefAlias }, new Identifier() { Value = "_NodeId" }) }); } else { parameters.Add(new WValueExpression { Value = path.SinkNode.NodeTableObjectName.BaseIdentifier.Value, SingleQuoted = true }); string sinkNodeIdName = _graphMetaData.ColumnsOfNodeTables[ WNamedTableReference.SchemaNameToTuple(path.SinkNode.NodeTableObjectName)] .FirstOrDefault(e => e.Value.Role == WNodeTableColumnRole.NodeId).Key; if (string.IsNullOrEmpty(sinkNodeIdName)) parameters.Add(new WValueExpression { Value = "null" }); else { parameters.Add(new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier(new Identifier() { Value = path.SinkNode.RefAlias }, new Identifier() { Value = sinkNodeIdName }) }); } } newSelectElements.Add(new WSelectScalarExpression { ColumnName = path.EdgeAlias + "_PathInfo", SelectExpr = new WFunctionCall { CallTarget = new WMultiPartIdentifierCallTarget { Identifiers = new WMultiPartIdentifier(new Identifier { Value = "dbo" }) }, FunctionName = new Identifier { Value = string.Format("{0}_{1}_{2}_PathMessageDecoder", schema, tableName, pathName) }, Parameters = parameters } }); continue; } } newSelectElements.Add(selectElement); } query.SelectElements = newSelectElements; } }
/// <summary> /// The entry of the optimizer, activated when visting each Select Query Block /// </summary> /// <param name="node"></param> public override void Visit(WSelectQueryBlock node) { var checkVarVisitor = new CollectVariableVisitor(); var currentContext = checkVarVisitor.Invoke(node.FromClause, _columnsOfNodeTables.Keys); currentContext.UpperLevel = _context; _context = currentContext; base.Visit(node); _statisticsCalculator.Context = _context; CheckValidity(node); var graph = ConstructGraph(node); //ChangeSelectStarExpression(node, graph); if (graph != null) { OptimizeTail(node, graph); EstimateRows(node, graph); EstimateAverageDegree(graph); var components = new List<MatchComponent>(); foreach (var subGraph in graph.ConnectedSubGraphs) { components.Add(ConstructComponent(subGraph)); #if DEBUG foreach (var matchNode in subGraph.Nodes.Values) { Trace.WriteLine(matchNode.NodeAlias); Trace.WriteLine(string.Format(" RowCount:{0}", matchNode.TableRowCount)); Trace.WriteLine(string.Format(" EstiRow:{0}", matchNode.EstimatedRows)); } #endif } UpdateQuery(node, components); #if DEBUG Trace.WriteLine(string.Format("Rows:{0}", components[0].Size)); Trace.WriteLine(string.Format("Cost:{0}", components[0].Cost)); Trace.WriteLine(string.Format("Estimated Rows:{0}", components[0].EstimateSize)); #endif node.MatchClause = null; } _context = _context.UpperLevel; }
/// <summary> /// Update FROM clause, adds DOWNSIZE predicates in the corresponding join conditions, /// and add corresponding predicates on the spilt nodes in the WHERE clause using /// optimal component of each connected sub-graph. /// </summary> /// <param name="node">The SELECT statement</param> /// <param name="components">The optimal components of each fully-connected graph</param> private void UpdateQuery(WSelectQueryBlock node, List<MatchComponent> components) { // Removes schema name in SELECT clause and all column references. var removeSchemaVisitor = new RemoveSchemanameInIdentifersVisitor(); removeSchemaVisitor.Visit(node); foreach (var component in components) { //// Cross apply the unmaterilized edges which point to the optimized tail nodes //Dictionary<MatchNode, MatchEdge> UnMatEdgeJoinDict = new Dictionary<MatchNode, MatchEdge>(); //foreach (var unMatEdge in component.EdgeMaterilizedDict.Where(e => !e.Value).Select(e => e.Key)) //{ // component.TableRef = component.SpanTableRef(component.TableRef, // unMatEdge, component.GetNodeRefName(unMatEdge.SourceNode), _graphMetaData); // MatchEdge joinEdge; // if (UnMatEdgeJoinDict.TryGetValue(unMatEdge.SinkNode, out joinEdge)) // { // if (!string.IsNullOrEmpty(newWhereString)) // newWhereString += " AND "; // newWhereString += string.Format("{0}.Sink = {1}.Sink", joinEdge.EdgeAlias, unMatEdge.EdgeAlias); // } // else // { // UnMatEdgeJoinDict[unMatEdge.SinkNode] = unMatEdge; // } //} // Updates from clause node.FromClause.TableReferences.Add(component.TableRef); } WBooleanExpression attachWhereCondition = null; foreach (var component in components) { attachWhereCondition = WBooleanBinaryExpression.Conjunction(attachWhereCondition, component.WhereCondition); } if (attachWhereCondition != null) { if (node.WhereClause != null && node.WhereClause.SearchCondition != null) node.WhereClause.SearchCondition = new WBooleanParenthesisExpression { Expression = node.WhereClause.SearchCondition }; else { node.WhereClause = new WWhereClause(); } node.WhereClause.SearchCondition = WBooleanBinaryExpression.Conjunction(attachWhereCondition, node.WhereClause.SearchCondition); } }
/// <summary> /// The entry point of the optimizer, activated when visting each SELECT query block. /// </summary> /// <param name="node">The SELECT query block</param> public override void Visit(WSelectQueryBlock node) { var checkVarVisitor = new CollectVariableVisitor(); var currentContext = checkVarVisitor.Invoke(node.FromClause, _graphMetaData.ColumnsOfNodeTables.Keys); currentContext.ParentContext = _context; _context = currentContext; base.Visit(node); CheckValidity(node); var graph = ConstructGraph(node); //ChangeSelectStarExpression(node, graph); if (graph != null) { //OptimizeTail(node, graph); AttachPredicates(node.WhereClause,graph); EstimateRows(graph); RetrieveStatistics(graph); var components = new List<MatchComponent>(); foreach (var subGraph in graph.ConnectedSubGraphs) { components.Add(ConstructComponent(subGraph, graph.ReversedEdgeDict, graph.SourceNodeStatisticsDict)); #if DEBUG foreach (var matchNode in subGraph.Nodes.Values) { Trace.WriteLine(matchNode.NodeAlias); Trace.WriteLine(string.Format(" RowCount:{0}", matchNode.TableRowCount)); Trace.WriteLine(string.Format(" EstiRow:{0}", matchNode.EstimatedRows)); } #endif } UpdateQuery(node, components); #if DEBUG Trace.WriteLine(string.Format("Rows:{0}", components[0].Cardinality)); Trace.WriteLine(string.Format("Cost:{0}", components[0].Cost)); Trace.WriteLine(string.Format("Estimated Rows:{0}", components[0].SqlEstimatedSize)); #endif node.MatchClause = null; } _context = _context.ParentContext; }
private WSelectQueryExpression ParseSelectQueryStatement(QueryExpression queryExpr) { if (queryExpr == null) { return null; } switch (queryExpr.GetType().Name) { case "BinaryQueryExpression": { var bqe = queryExpr as BinaryQueryExpression; var pQueryExpr = new WBinaryQueryExpression { All = bqe.All, FirstQueryExpr = ParseSelectQueryStatement(bqe.FirstQueryExpression), SecondQueryExpr = ParseSelectQueryStatement(bqe.SecondQueryExpression), FirstTokenIndex = bqe.FirstTokenIndex, LastTokenIndex = bqe.LastTokenIndex }; //pQueryExpr.OrderByExpr = parseOrderbyExpr(bqe.OrderByClause); return pQueryExpr; } case "QueryParenthesisExpression": { var qpe = queryExpr as QueryParenthesisExpression; var pQueryExpr = new WQueryParenthesisExpression { QueryExpr = ParseSelectQueryStatement(qpe.QueryExpression), FirstTokenIndex = qpe.FirstTokenIndex, LastTokenIndex = qpe.LastTokenIndex }; //pQueryExpr.OrderByExpr = parseOrderbyExpr(qpe.OrderByClause); return pQueryExpr; } case "QuerySpecification": { var qs = queryExpr as QuerySpecification; var pQueryExpr = new WSelectQueryBlock { FirstTokenIndex = qs.FirstTokenIndex, LastTokenIndex = qs.LastTokenIndex, SelectElements = new List<WSelectElement>(qs.SelectElements.Count), }; // // SELECT clause // foreach (var wsel in qs.SelectElements.Select(ParseSelectElement).Where(wsel => wsel != null)) { pQueryExpr.SelectElements.Add(wsel); } // // Top row filter // if (qs.TopRowFilter != null) { pQueryExpr.TopRowFilter = new WTopRowFilter { Percent = qs.TopRowFilter.Percent, WithTies = qs.TopRowFilter.WithTies, Expression = ParseScalarExpression(qs.TopRowFilter.Expression), FirstTokenIndex = qs.TopRowFilter.FirstTokenIndex, LastTokenIndex = qs.TopRowFilter.LastTokenIndex }; } pQueryExpr.UniqueRowFilter = qs.UniqueRowFilter; // // FROM clause // if (qs.FromClause != null && qs.FromClause.TableReferences != null) { pQueryExpr.FromClause.FirstTokenIndex = qs.FromClause.FirstTokenIndex; pQueryExpr.FromClause.LastTokenIndex = qs.FromClause.LastTokenIndex; pQueryExpr.FromClause.TableReferences = new List<WTableReference>(qs.FromClause.TableReferences.Count); foreach (var pref in qs.FromClause.TableReferences.Select(ParseTableReference).Where(pref => pref != null)) { pQueryExpr.FromClause.TableReferences.Add(pref); } } // // WHERE clause // if (qs.WhereClause != null && qs.WhereClause.SearchCondition != null) { pQueryExpr.WhereClause.FirstTokenIndex = qs.WhereClause.FirstTokenIndex; pQueryExpr.WhereClause.LastTokenIndex = qs.WhereClause.LastTokenIndex; pQueryExpr.WhereClause.SearchCondition = ParseBooleanExpression(qs.WhereClause.SearchCondition); } // GROUP-BY clause if (qs.GroupByClause != null) { pQueryExpr.GroupByClause = ParseGroupbyClause(qs.GroupByClause); } // Having clause if (qs.HavingClause != null) { pQueryExpr.HavingClause = new WHavingClause { SearchCondition = ParseBooleanExpression(qs.HavingClause.SearchCondition), FirstTokenIndex = qs.HavingClause.FirstTokenIndex, LastTokenIndex = qs.HavingClause.LastTokenIndex }; } // // ORDER-BY clause // if (qs.OrderByClause != null) { pQueryExpr.OrderByClause = ParseOrderbyClause(qs.OrderByClause); } return pQueryExpr; } default: return null; } }
/// <summary> /// Update FROM clause, adds DOWNSIZE predicates in the corresponding join conditions, /// and add corresponding predicates on the spilt nodes in the WHERE clause using /// optimal component of each connected sub-graph. /// </summary> /// <param name="node">The SELECT statement</param> /// <param name="components">The optimal components of each fully-connected graph</param> private void UpdateQuery(WSelectQueryBlock node, List<MatchComponent> components) { // Removes schema name in SELECT clause and all column references. var removeSchemaVisitor = new RemoveSchemanameInIdentifersVisitor(); removeSchemaVisitor.Visit(node); string newWhereString = ""; foreach (var component in components) { // Adds predicates for split nodes var component1 = component; foreach ( var compNode in component.MaterializedNodeSplitCount.Where( e => e.Value > 0 && e.Key.Predicates != null && e.Key.Predicates.Any())) { var matchNode = compNode.Key; WBooleanExpression newExpression = matchNode.Predicates.Aggregate<WBooleanExpression, WBooleanExpression>(null, WBooleanBinaryExpression.Conjunction); string predicateString = newExpression.ToString(); var nodeCount = component1.MaterializedNodeSplitCount[matchNode]; while (nodeCount > 0) { newWhereString += " AND "; string tempStr = predicateString.Replace(string.Format("[{0}]", matchNode.RefAlias.ToUpper()), string.Format("[{0}_{1}]", matchNode.RefAlias.ToUpper(), nodeCount)); tempStr = tempStr.Replace(string.Format("[{0}]", matchNode.RefAlias.ToLower()), string.Format("[{0}_{1}]", matchNode.RefAlias.ToLower(), nodeCount)); newWhereString += tempStr; nodeCount--; } } // Cross apply the unmaterilized edges which point to the optimized tail nodes Dictionary<MatchNode,MatchEdge> UnMatEdgeJoinDict = new Dictionary<MatchNode, MatchEdge>(); foreach (var unMatEdge in component.EdgeMaterilizedDict.Where(e => !e.Value).Select(e=>e.Key)) { component.TableRef = component.SpanTableRef(component.TableRef, unMatEdge, component.GetNodeRefName(unMatEdge.SourceNode),_graphMetaData); MatchEdge joinEdge; if (UnMatEdgeJoinDict.TryGetValue(unMatEdge.SinkNode, out joinEdge)) { if (!string.IsNullOrEmpty(newWhereString)) newWhereString += " AND "; newWhereString += string.Format("{0}.Sink = {1}.Sink", joinEdge.EdgeAlias, unMatEdge.EdgeAlias); } else { UnMatEdgeJoinDict[unMatEdge.SinkNode] = unMatEdge; } } // Updates from clause node.FromClause.TableReferences.Add(component.TableRef); } if (!string.IsNullOrEmpty(newWhereString)) { if (node.WhereClause!=null && node.WhereClause.SearchCondition!=null) node.WhereClause.SearchCondition = new WBooleanParenthesisExpression { Expression = node.WhereClause.SearchCondition }; else { node.WhereClause = new WWhereClause(); } node.WhereClause.GhostString = newWhereString; } }
/// <summary> /// Update from clause in the query using optimal component of each connected sub-graph /// </summary> /// <param name="node"></param> /// <param name="components"></param> private void UpdateQuery(WSelectQueryBlock node, List<MatchComponent> components) { string newWhereString = ""; foreach (var component in components) { // Add down size predicates foreach (var joinTableTuple in component.FatherListofDownSizeTable) { var joinTable = joinTableTuple.Item1; joinTable.JoinCondition = WBooleanBinaryExpression.Conjunction(joinTable.JoinCondition, new WBooleanComparisonExpression { ComparisonType = BooleanComparisonType.Equals, FirstExpr = new WFunctionCall { CallTarget = new WMultiPartIdentifierCallTarget { Identifiers = new WMultiPartIdentifier(new Identifier { Value = "dbo" }) }, FunctionName = new Identifier { Value = "DownSizeFunction" }, Parameters = new List<WScalarExpression> { new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier { Identifiers = new List<Identifier> { new Identifier{Value = joinTableTuple.Item2}, new Identifier {Value = "LocalNodeid"} } } } } }, SecondExpr = new WValueExpression("1", false) }); } // Update from clause node.FromClause.TableReferences.Add(component.TableRef); // Add predicates for split nodes var component1 = component; foreach ( var compNode in component.MaterializedNodeSplitCount.Where( e => e.Value>0 && e.Key.Predicates != null && e.Key.Predicates.Any())) { var matchNode = compNode.Key; WBooleanExpression newExpression = null; foreach (var predicate in matchNode.Predicates) { newExpression = WBooleanBinaryExpression.Conjunction(newExpression, predicate); } string predicateString = newExpression.ToString().ToLower(); var nodeCount = component1.MaterializedNodeSplitCount[matchNode]; while (nodeCount > 0) { newWhereString += " AND "; newWhereString += predicateString.Replace(string.Format("[{0}]", matchNode.RefAlias.ToLower()), string.Format("[{0}_{1}]", matchNode.RefAlias, nodeCount)); nodeCount--; } } } if (newWhereString.Any()) { node.WhereClause.SearchCondition = new WBooleanParenthesisExpression { Expression = node.WhereClause.SearchCondition }; node.WhereClause.GhostString = newWhereString; } var visitor2 = new DeleteSchemanameInSelectVisitor(); visitor2.Invoke(node); }
internal bool HasAggregateFunctionInTheRepeatSelectQuery(WSelectQueryBlock repeatSelectQuery) { AggregateFunctionCountVisitor aggregateCountVisitor = new AggregateFunctionCountVisitor(); return(aggregateCountVisitor.Invoke(repeatSelectQuery) > 0); }
/// <summary> /// Constructs the graph pattern specified by the MATCH clause. /// The graph pattern may consist of multiple fully-connected sub-graphs. /// </summary> /// <param name="query">The SELECT query block</param> /// <returns>A graph object contains all the connected componeents</returns> private MatchGraph ConstructGraph(WSelectQueryBlock query) { if (query == null || query.MatchClause == null) return null; var columnsOfNodeTables = _graphMetaData.ColumnsOfNodeTables; var nodeViewMapping = _graphMetaData.NodeViewMapping; var unionFind = new UnionFind(); var edgeColumnToAliasesDict = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase); var pathDictionary = new Dictionary<string, MatchPath>(StringComparer.OrdinalIgnoreCase); var reversedEdgeDict = new Dictionary<string, MatchEdge>(); var matchClause = query.MatchClause; var nodes = new Dictionary<string, MatchNode>(StringComparer.OrdinalIgnoreCase); var connectedSubGraphs = new List<ConnectedComponent>(); var subGrpahMap = new Dictionary<string, ConnectedComponent>(StringComparer.OrdinalIgnoreCase); var parent = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); unionFind.Parent = parent; // Constructs the graph pattern specified by the path expressions in the MATCH clause foreach (var path in matchClause.Paths) { var index = 0; MatchEdge preEdge = null; for (var count = path.PathEdgeList.Count; index < count; ++index) { var currentNodeTableRef = path.PathEdgeList[index].Item1; var currentEdgeColumnRef = path.PathEdgeList[index].Item2; var currentNodeExposedName = currentNodeTableRef.BaseIdentifier.Value; var nextNodeTableRef = index != count - 1 ? path.PathEdgeList[index + 1].Item1 : path.Tail; var nextNodeExposedName = nextNodeTableRef.BaseIdentifier.Value; var patternNode = nodes.GetOrCreate(currentNodeExposedName); if (patternNode.NodeAlias == null) { patternNode.NodeAlias = currentNodeExposedName; patternNode.Neighbors = new List<MatchEdge>(); patternNode.External = false; var nodeTable = _context[currentNodeExposedName] as WNamedTableReference; if (nodeTable != null) { patternNode.NodeTableObjectName = nodeTable.TableObjectName; if (patternNode.NodeTableObjectName.SchemaIdentifier == null) patternNode.NodeTableObjectName.Identifiers.Insert(0, new Identifier {Value = "dbo"}); } } Identifier edgeIdentifier = currentEdgeColumnRef.MultiPartIdentifier.Identifiers.Last(); string schema = patternNode.NodeTableObjectName.SchemaIdentifier.Value.ToLower(); string nodeTableName = patternNode.NodeTableObjectName.BaseIdentifier.Value; string bindTableName = _context.EdgeNodeBinding[ new Tuple<string, string>(nodeTableName.ToLower(), edgeIdentifier.Value.ToLower())].ToLower(); var bindNodeTableObjName = new WSchemaObjectName( new Identifier {Value = schema}, new Identifier {Value = bindTableName} ); var edgeColumn = columnsOfNodeTables[ WNamedTableReference.SchemaNameToTuple(bindNodeTableObjName)][ currentEdgeColumnRef.MultiPartIdentifier.Identifiers.Last().Value]; string edgeAlias = currentEdgeColumnRef.Alias; //string revEdgeAlias = edgeAlias; bool isReversed = path.IsReversed || edgeColumn.EdgeInfo.IsReversedEdge; string currentRevEdgeName = null; // get original edge name var currentEdgeName = currentEdgeColumnRef.MultiPartIdentifier.Identifiers.Last().Value; var originalSourceName = isReversed ? (_context[nextNodeExposedName] as WNamedTableReference).TableObjectName.BaseIdentifier.Value : (_context[currentNodeExposedName] as WNamedTableReference).TableObjectName.BaseIdentifier.Value; if (isReversed) { var i = currentEdgeName.IndexOf(originalSourceName, StringComparison.OrdinalIgnoreCase) + originalSourceName.Length; currentRevEdgeName = currentEdgeName.Substring(i + 1, currentEdgeName.Length - "Reversed".Length - i - 1); } else { var srcTuple = WNamedTableReference.SchemaNameToTuple(patternNode.NodeTableObjectName); // [nodeView]-[edge]->[node/nodeView] if (edgeColumn.Role == WNodeTableColumnRole.Edge && nodeViewMapping.ContainsKey(srcTuple)) { var physicalNodeName = _context.EdgeNodeBinding[new Tuple<string, string>(srcTuple.Item2, currentEdgeName.ToLower())]; currentRevEdgeName = physicalNodeName + "_" + currentEdgeName + "Reversed"; } else { currentRevEdgeName = originalSourceName + "_" + currentEdgeName + "Reversed"; } } if (edgeAlias == null) { edgeAlias = !isReversed ? string.Format("{0}_{1}_{2}", currentNodeExposedName, currentEdgeName, nextNodeExposedName) : string.Format("{0}_{1}_{2}", nextNodeExposedName, currentRevEdgeName, currentNodeExposedName); //when the current edge is a reversed edge, the key should still be the original edge name //e.g.: TestDeleteEdgeWithTableAlias var edgeNameKey = isReversed ? currentRevEdgeName : currentEdgeName; if (edgeColumnToAliasesDict.ContainsKey(edgeNameKey)) { edgeColumnToAliasesDict[edgeNameKey].Add(edgeAlias); } else { edgeColumnToAliasesDict.Add(edgeNameKey, new List<string> { edgeAlias }); } } MatchEdge edge, revEdge; if (currentEdgeColumnRef.MinLength == 1 && currentEdgeColumnRef.MaxLength == 1) { var isEdgeView = edgeColumn.Role == WNodeTableColumnRole.EdgeView; var hasRevEdge = edgeColumn.EdgeInfo.HasReversedEdge; edge = new MatchEdge { IsEdgeView = isEdgeView, IsReversedEdge = false, HasReversedEdge = hasRevEdge, SourceNode = patternNode, EdgeColumn = currentEdgeColumnRef, EdgeAlias = edgeAlias, BindNodeTableObjName = bindNodeTableObjName, }; _context.AddEdgeReference(edge); if (hasRevEdge) { revEdge = new MatchEdge { IsEdgeView = isEdgeView, IsReversedEdge = true, SinkNode = patternNode, EdgeColumn = new WEdgeColumnReferenceExpression() { ColumnType = currentEdgeColumnRef.ColumnType, Alias = currentEdgeColumnRef.Alias, MaxLength = currentEdgeColumnRef.MaxLength, MinLength = currentEdgeColumnRef.MinLength, AttributeValueDict = currentEdgeColumnRef.AttributeValueDict, MultiPartIdentifier = new WMultiPartIdentifier(new Identifier() { QuoteType = currentEdgeColumnRef.MultiPartIdentifier.Identifiers.Last().QuoteType, Value = currentRevEdgeName, }), }, EdgeAlias = edgeAlias, //EdgeAlias = revEdgeAlias, }; reversedEdgeDict[edge.EdgeAlias] = revEdge; } } else { MatchPath matchPath = new MatchPath { SourceNode = patternNode, EdgeColumn = currentEdgeColumnRef, EdgeAlias = edgeAlias, BindNodeTableObjName = new WSchemaObjectName( new Identifier {Value = schema}, new Identifier {Value = bindTableName} ), MinLength = currentEdgeColumnRef.MinLength, MaxLength = currentEdgeColumnRef.MaxLength, ReferencePathInfo = false, AttributeValueDict = currentEdgeColumnRef.AttributeValueDict }; _context.AddEdgeReference(matchPath); pathDictionary[edgeAlias] = matchPath; edge = matchPath; } if (preEdge != null) { preEdge.SinkNode = patternNode; if (preEdge.HasReversedEdge) { //var preSourceNode = preEdge.SourceNode; var preEdgeBindNodeTableObjName = preEdge.BindNodeTableObjName; var preEdgeColName = preEdge.EdgeColumn.MultiPartIdentifier.Identifiers.Last().Value; var preEdgeColumn = columnsOfNodeTables[ WNamedTableReference.SchemaNameToTuple(preEdgeBindNodeTableObjName)][ preEdgeColName]; var isEdgeView = preEdgeColumn.Role == WNodeTableColumnRole.EdgeView; var isNodeView = _graphMetaData.NodeViewMapping.ContainsKey( WNamedTableReference.SchemaNameToTuple(patternNode.NodeTableObjectName)); reversedEdgeDict[preEdge.EdgeAlias].SourceNode = patternNode; // [node/nodeView]-[edgeView]->[node/nodeView] if (isEdgeView) { reversedEdgeDict[preEdge.EdgeAlias].BindNodeTableObjName = preEdgeBindNodeTableObjName; } // [node/nodeView]-[edge]->[nodeView] else if (!isEdgeView && isNodeView) { reversedEdgeDict[preEdge.EdgeAlias].BindNodeTableObjName = new WSchemaObjectName( new Identifier { Value = "dbo" }, new Identifier { Value = preEdgeColumn.EdgeInfo.SinkNodes.First() } ); } else { reversedEdgeDict[preEdge.EdgeAlias].BindNodeTableObjName = new WSchemaObjectName( new Identifier { Value = schema }, new Identifier { Value = bindTableName } ); } } } preEdge = edge; if (!parent.ContainsKey(currentNodeExposedName)) parent[currentNodeExposedName] = currentNodeExposedName; if (!parent.ContainsKey(nextNodeExposedName)) parent[nextNodeExposedName] = nextNodeExposedName; unionFind.Union(currentNodeExposedName, nextNodeExposedName); patternNode.Neighbors.Add(edge); } var tailExposedName = path.Tail.BaseIdentifier.Value; var tailNode = nodes.GetOrCreate(tailExposedName); if (tailNode.NodeAlias == null) { tailNode.NodeAlias = tailExposedName; tailNode.Neighbors = new List<MatchEdge>(); var nodeTable = _context[tailExposedName] as WNamedTableReference; if (nodeTable != null) { tailNode.NodeTableObjectName = nodeTable.TableObjectName; if (tailNode.NodeTableObjectName.SchemaIdentifier == null) tailNode.NodeTableObjectName.Identifiers.Insert(0, new Identifier {Value = "dbo"}); } } if (preEdge != null) { preEdge.SinkNode = tailNode; if (preEdge.HasReversedEdge) { var schema = tailNode.NodeTableObjectName.SchemaIdentifier.Value.ToLower(); var nodeTableName = tailNode.NodeTableObjectName.BaseIdentifier.Value; //var preSourceNode = preEdge.SourceNode; var preEdgeBindNodeTableObjName = preEdge.BindNodeTableObjName; var preEdgeColName = preEdge.EdgeColumn.MultiPartIdentifier.Identifiers.Last().Value; var preEdgeColumn = columnsOfNodeTables[ WNamedTableReference.SchemaNameToTuple(preEdgeBindNodeTableObjName)][ preEdgeColName]; var isEdgeView = preEdgeColumn.Role == WNodeTableColumnRole.EdgeView; var isNodeView = _graphMetaData.NodeViewMapping.ContainsKey( WNamedTableReference.SchemaNameToTuple(tailNode.NodeTableObjectName)); reversedEdgeDict[preEdge.EdgeAlias].SourceNode = tailNode; // [node/nodeView]-[edgeView]->[node/nodeView] if (isEdgeView) { reversedEdgeDict[preEdge.EdgeAlias].BindNodeTableObjName = preEdgeBindNodeTableObjName; } // [node/nodeView]-[edge]->[nodeView] else if (!isEdgeView && isNodeView) { reversedEdgeDict[preEdge.EdgeAlias].BindNodeTableObjName = new WSchemaObjectName( new Identifier { Value = "dbo" }, new Identifier { Value = preEdgeColumn.EdgeInfo.SinkNodes.First() } ); } else { reversedEdgeDict[preEdge.EdgeAlias].BindNodeTableObjName = new WSchemaObjectName( new Identifier { Value = schema }, new Identifier { Value = nodeTableName.ToLower() } ); } } } } // Puts nodes into subgraphs foreach (var node in nodes) { string root = unionFind.Find(node.Key); if (!subGrpahMap.ContainsKey(root)) { var subGraph = new ConnectedComponent(); subGraph.Nodes[node.Key] = node.Value; foreach (var edge in node.Value.Neighbors) { subGraph.Edges[edge.EdgeAlias] = edge; } subGrpahMap[root] = subGraph; connectedSubGraphs.Add(subGraph); subGraph.IsTailNode[node.Value] = false; } else { var subGraph = subGrpahMap[root]; subGraph.Nodes[node.Key] = node.Value; foreach (var edge in node.Value.Neighbors) { subGraph.Edges[edge.EdgeAlias] = edge; } subGraph.IsTailNode[node.Value] = false; } } var graph = new MatchGraph { ReversedEdgeDict = reversedEdgeDict, ConnectedSubGraphs = connectedSubGraphs, SourceNodeStatisticsDict = new Dictionary<Tuple<string, bool>, Statistics>(), }; unionFind.Parent = null; // When an edge in the MATCH clause is not associated with an alias, // assigns to it a default alias: sourceAlias_EdgeColumnName_sinkAlias. // Also rewrites edge attributes anywhere in the query that can be bound to this default alias. var replaceTableRefVisitor = new ReplaceEdgeReferenceVisitor(); replaceTableRefVisitor.Invoke(query, edgeColumnToAliasesDict); // Rematerializes node tables in the MATCH clause which are defined in the upper-level context // and join them with the upper-level table references. RematerilizeExtrenalNodeTableReference(query, nodes); // Transforms the path reference in the SELECT elements into a // scalar function to display path information. TransformPathInfoDisplaySelectElement(query, pathDictionary); return graph; }
/// <summary> /// Replaces the SELECT * expression with all visible columns /// </summary> /// <param name="node"></param> /// <param name="graph"></param> private void ChangeSelectStarExpression(WSelectQueryBlock node, MatchGraph graph) { var newSelectElements = new List<WSelectElement>(); Dictionary<string, List<WSelectElement>> starReplacement = null; foreach (var element in node.SelectElements) { var starElement = element as WSelectStarExpression; if (starElement != null) { if (starReplacement == null) { starReplacement = new Dictionary<string, List<WSelectElement>>(StringComparer.OrdinalIgnoreCase); // Fetch table in order foreach (var table in _context.NodeTableDictionary) { var alias = table.Key; var namedTable = table.Value as WNamedTableReference; if (namedTable != null) { foreach ( var column in _graphMetaData.ColumnsOfNodeTables[ WNamedTableReference.SchemaNameToTuple(namedTable.TableObjectName)].Where( e => e.Value.Role != WNodeTableColumnRole.Edge).Select(e => e.Key)) { var elementList = starReplacement.GetOrCreate(alias); elementList.Add(new WSelectScalarExpression { SelectExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier { Identifiers = new List<Identifier> { new Identifier {Value = alias}, new Identifier {Value = column} } } } }); } if (graph == null) continue; foreach (var subGraph in graph.ConnectedSubGraphs) { if (subGraph.Nodes.ContainsKey(alias)) { var matchNode = subGraph.Nodes[alias]; foreach (var edge in matchNode.Neighbors) { var schemaName = edge.SourceNode.NodeTableObjectName.SchemaIdentifier == null ? "dbo" : edge.SourceNode.NodeTableObjectName.SchemaIdentifier.Value.ToLower(); var nodeTuple = new Tuple<string, string>(schemaName, edge.SourceNode.NodeTableObjectName.BaseIdentifier.Value.ToLower()); var edgeColumnName = edge.EdgeColumn.MultiPartIdentifier.Identifiers.Last().Value.ToLower(); if (!_graphMetaData.ColumnsOfNodeTables[nodeTuple].ContainsKey(edgeColumnName)) { throw new GraphViewException("Invalid Edge Alias"); } foreach ( var column in _graphMetaData.ColumnsOfNodeTables[nodeTuple][edgeColumnName].EdgeInfo .ColumnAttributes) { var elementList = starReplacement.GetOrCreate(edge.EdgeAlias); elementList.Add(new WSelectScalarExpression { SelectExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier { Identifiers = new List<Identifier> { new Identifier {Value = edge.EdgeAlias}, new Identifier {Value = column} } } } }); } } } } } else { var derivedTable = table.Value as WQueryDerivedTable; if (derivedTable == null) continue; var elementList = starReplacement.GetOrCreate(alias); elementList.Add(new WSelectStarExpression { Qulifier = new WMultiPartIdentifier { Identifiers = new List<Identifier> {new Identifier {Value = alias}} } }); } } } if (starElement.Qulifier != null) { newSelectElements.AddRange(starReplacement[starElement.Qulifier.Identifiers.Last().Value]); } else { foreach (var value in starReplacement.Values) { newSelectElements.AddRange(value); } } } else { newSelectElements.Add(element); } } if (newSelectElements.Any()) node.SelectElements = newSelectElements; }
/// <summary> /// Construct Graph from the match clause. The Graph can consist of multiple connected SubGraph. /// Not supported in this version /// </summary> /// <param name="query"></param> /// <returns></returns> private MatchGraph ConstructGraph(WSelectQueryBlock query) { var unionFind = new UnionFind(); if (query.MatchClause == null) return null; var edgeTableReferenceDict = new Dictionary<string, List<string>>(StringComparer.CurrentCultureIgnoreCase); var matchClause = query.MatchClause; var nodes = new Dictionary<string, MatchNode>(StringComparer.CurrentCultureIgnoreCase); var connectedSubGraphs = new List<ConnectedComponent>(); var subGrpahMap = new Dictionary<string, ConnectedComponent>(StringComparer.CurrentCultureIgnoreCase); var parent = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase); unionFind.Parent = parent; HashSet<Tuple<string, string>> nodeTypes = new HashSet<Tuple<string, string>>(); //Construct Graph from Match Pattern foreach (var path in matchClause.Paths) { var index = 0; MatchEdge preEdge = null; for (var count = path.PathNodeList.Count; index < count; ++index) { var currentNode = path.PathNodeList[index].Item1; var currentEdge = path.PathNodeList[index].Item2; var currentNodeExposedName = currentNode.BaseIdentifier.Value; var nextNode = index != count - 1 ? path.PathNodeList[index + 1].Item1 : path.Tail; var nextNodeExposedName = nextNode.BaseIdentifier.Value; var node = nodes.GetOrCreate(currentNodeExposedName); if (node.NodeAlias == null) { node.NodeAlias = currentNodeExposedName; node.Neighbors = new List<MatchEdge>(); node.External = false; var nodeTable = _context[currentNodeExposedName] as WNamedTableReference; if (nodeTable != null) { node.TableObjectName = nodeTable.TableObjectName; if (node.TableObjectName.SchemaIdentifier == null) node.TableObjectName.Identifiers.Insert(0, new Identifier { Value = "dbo" }); var nodeTypeTuple = WNamedTableReference.SchemaNameToTuple(node.TableObjectName); if (!nodeTypes.Contains(nodeTypeTuple)) nodeTypes.Add(nodeTypeTuple); } } if (currentEdge.AliasRole == AliasType.Default) { var currentEdgeName = currentEdge.MultiPartIdentifier.Identifiers.Last().Value; if (edgeTableReferenceDict.ContainsKey(currentEdgeName)) { edgeTableReferenceDict[currentEdgeName].Add(currentEdge.Alias); } else { edgeTableReferenceDict.Add(currentEdgeName, new List<string> { currentEdge.Alias }); } } var edge = new MatchEdge { SourceNode = node, EdgeColumn = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier { Identifiers = new List<Identifier> { new Identifier {Value = node.NodeAlias}, currentEdge.MultiPartIdentifier.Identifiers.Last() } } }, EdgeAlias = currentEdge.Alias }; if (preEdge != null) { preEdge.SinkNode = node; } preEdge = edge; if (!parent.ContainsKey(currentNodeExposedName)) parent[currentNodeExposedName] = currentNodeExposedName; if (!parent.ContainsKey(nextNodeExposedName)) parent[nextNodeExposedName] = nextNodeExposedName; unionFind.Union(currentNodeExposedName, nextNodeExposedName); node.Neighbors.Add(edge); _context.AddEdgeReference(currentEdge.Alias, edge.SourceNode.TableObjectName, currentEdge); } var tailExposedName = path.Tail.BaseIdentifier.Value; var tailNode = nodes.GetOrCreate(tailExposedName); if (tailNode.NodeAlias == null) { tailNode.NodeAlias = tailExposedName; tailNode.Neighbors = new List<MatchEdge>(); var nodeTable = _context[tailExposedName] as WNamedTableReference; if (nodeTable != null) { tailNode.TableObjectName = nodeTable.TableObjectName; if (tailNode.TableObjectName.SchemaIdentifier == null) tailNode.TableObjectName.Identifiers.Insert(0, new Identifier { Value = "dbo" }); var nodeTypeTuple = WNamedTableReference.SchemaNameToTuple(tailNode.TableObjectName); if (!nodeTypes.Contains(nodeTypeTuple)) nodeTypes.Add(nodeTypeTuple); } } if (preEdge != null) preEdge.SinkNode = tailNode; } // Put nodes into subgraphs foreach (var node in nodes) { string root = unionFind.Find(node.Key); if (!subGrpahMap.ContainsKey(root)) { var subGraph = new ConnectedComponent(); subGraph.Nodes[node.Key] = node.Value; foreach (var edge in node.Value.Neighbors) { subGraph.Edges[edge.EdgeAlias] = edge; } subGrpahMap[root] = subGraph; connectedSubGraphs.Add(subGraph); subGraph.IsTailNode[node.Value] = false; } else { var subGraph = subGrpahMap[root]; subGraph.Nodes[node.Key] = node.Value; foreach (var edge in node.Value.Neighbors) { subGraph.Edges[edge.EdgeAlias] = edge; } subGraph.IsTailNode[node.Value] = false; } } // Replace Edge name alias with proper alias in the query var replaceTableRefVisitor = new ReplaceTableRefVisitor(); replaceTableRefVisitor.Invoke(query, edgeTableReferenceDict); // If a table alias in the MATCH clause is defined in an upper-level context, // to be able to translate this MATCH clause, this table alias must be re-materialized // in the FROM clause of the current context and joined with the corresponding table // in the upper-level context. var tableRefs = query.FromClause.TableReferences; var tableSet = new HashSet<string>(StringComparer.CurrentCultureIgnoreCase); var newTableRefs = new List<WTableReference>(); for (int index = 0; index < tableRefs.Count; ++index) { var table = tableRefs[index] as WNamedTableReference; if (table == null) { newTableRefs.Add(tableRefs[index]); continue; } var tableTuple = WNamedTableReference.SchemaNameToTuple(table.TableObjectName); if (!nodeTypes.Contains(tableTuple)) { newTableRefs.Add(table); } else { tableSet.Add(table.ExposedName.Value); } } query.FromClause = new WFromClause { TableReferences = newTableRefs, }; WBooleanExpression whereCondiction = null; foreach (var node in nodes) { if (!tableSet.Contains(node.Key)) { node.Value.External = true; var newWhereCondition = new WBooleanComparisonExpression { ComparisonType = BooleanComparisonType.Equals, FirstExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier( new Identifier { Value = node.Key }, new Identifier { Value = "GlobalNodeId" }) }, SecondExpr = new WColumnReferenceExpression { MultiPartIdentifier = new WMultiPartIdentifier( new Identifier { Value = node.Value.RefAlias }, new Identifier { Value = "GlobalNodeId" }) }, }; whereCondiction = WBooleanBinaryExpression.Conjunction(whereCondiction, newWhereCondition); } } if (whereCondiction != null) { if (query.WhereClause == null) { query.WhereClause = new WWhereClause { SearchCondition = whereCondiction }; } else { if (query.WhereClause.SearchCondition == null) { query.WhereClause.SearchCondition = whereCondiction; } else { query.WhereClause.SearchCondition = new WBooleanBinaryExpression { BooleanExpressionType = BooleanBinaryExpressionType.And, FirstExpr = new WBooleanParenthesisExpression { Expression = query.WhereClause.SearchCondition }, SecondExpr = new WBooleanParenthesisExpression { Expression = whereCondiction } }; } } } var graph = new MatchGraph { ConnectedSubGraphs = connectedSubGraphs, NodeTypesSet = nodeTypes, }; unionFind.Parent = null; return graph; }
/// <summary> /// Checks the validity of the MATCH clause, including /// (1) an edge is bound to a node table or node view, /// (2) the source and the sink of an edeg in a path expression are bound to corresponding node tables, /// as specified when node tables are created, and /// (3) The length constraint for a path construct is valid /// </summary> /// <param name="node">True, if the MATCH clause passes the test; false, otherwise.</param> private void CheckValidity(WSelectQueryBlock node) { if (node.MatchClause == null) return; // Checks validity of the source node/node view if (node.MatchClause.Paths.All( path => path.PathEdgeList.All( part => _context.CheckTable(part.Item1.BaseIdentifier.Value) && IsNodeTable(_context[part.Item1.BaseIdentifier.Value]) ) )) { foreach (var path in node.MatchClause.Paths) { var index = 0; for (var count = path.PathEdgeList.Count; index < count; ++index) { var pathNode = path.PathEdgeList[index]; var table = _context[pathNode.Item1.BaseIdentifier.Value] as WNamedTableReference; var edgeCol = pathNode.Item2; var edge = edgeCol.MultiPartIdentifier.Identifiers.Last().Value.ToLower(); var nodeTableTuple = WNamedTableReference.SchemaNameToTuple(table.TableObjectName); var schema = nodeTableTuple.Item1; // Binds edge/edge view to node/node view and check validity string bindNode = _context.BindEdgeToNode(schema, edge, nodeTableTuple.Item2, _graphMetaData); if (string.IsNullOrEmpty(bindNode)) throw new GraphViewException(string.Format("Edge/EdgeView {0} cannot be bind to {1}.{2}", edge, nodeTableTuple.Item1, nodeTableTuple.Item2)); // Check edge length if (edgeCol.MinLength<0) throw new GraphViewException( string.Format( "The minimal length of the path {0} should be non-negative integer", edge)); if (edgeCol.MaxLength!=-1 && edgeCol.MinLength > edgeCol.MaxLength) throw new GraphViewException( string.Format( "The minimal length of the path {0} should not be larger than the maximal length", edge)); // Checks whether the sink of the edge/edge view exist HashSet<string> edgeSinkNodes = _graphMetaData.ColumnsOfNodeTables[new Tuple<string, string>(schema, bindNode)][edge].EdgeInfo .SinkNodes; HashSet<string> sinkNodes; if ( !edgeSinkNodes.All( e => _graphMetaData.ColumnsOfNodeTables.ContainsKey(new Tuple<string, string>( schema.ToLower(), e)))) throw new GraphViewException(String.Format(CultureInfo.CurrentCulture, "Node Table Referenced by the Edge {0} not exists", edge)); // Checks validity of sink node(s) var nextNode = index != count - 1 ? path.PathEdgeList[index + 1].Item1 : path.Tail; var getNextTable = _context[nextNode.BaseIdentifier.Value]; if (!IsNodeTable(getNextTable)) throw new GraphViewException("Node table expected in MATCH clause"); // Checks whether the intersection of the edge sink and sink node(s) is empty var nextTable = getNextTable as WNamedTableReference; if (nextTable == null || !_graphMetaData.NodeViewMapping.TryGetValue( WNamedTableReference.SchemaNameToTuple(nextTable.TableObjectName), out sinkNodes)) sinkNodes = new HashSet<string> {nextTable.TableObjectName.BaseIdentifier.Value}; if (sinkNodes.All(e => !edgeSinkNodes.Contains(e))) { throw new GraphViewException(String.Format(CultureInfo.CurrentCulture, "Wrong Reference Table {0}", nextTable.TableObjectName.BaseIdentifier.Value)); } } } } else { throw new GraphViewException("Node table/view expected in MATCH clause"); } }
public override void Visit(WSelectQueryBlock node) { base.Visit(node); var flag = false; for (var i = 0; i < MatchList.Count; ++i) { if (MatchFlag[i]) { continue; } var nextToken = node.LastTokenIndex + 1; while (nextToken < Tokens.Count && Tokens[nextToken].TokenType == TSqlTokenType.WhiteSpace) { nextToken++; } var insideBlock = (node.FirstTokenIndex < MatchList[i].FirstTokenIndex && (node.LastTokenIndex > MatchList[i].LastTokenIndex || nextToken == MatchList[i].FirstTokenIndex)); if (!insideBlock) { continue; } nextToken = node.FromClause.LastTokenIndex + 1; while (nextToken < Tokens.Count && Tokens[nextToken].TokenType == TSqlTokenType.WhiteSpace) { nextToken++; } // match clause should exactly follow FROM clause if (node.FromClause.TableReferences == null || nextToken != MatchList[i].FirstTokenIndex) { var token = Tokens[nextToken]; _errors.Add(new ParseError(0, token.Offset, token.Line, token.Column, "MATCH clause should exactly follow FROM clause")); } // if a where/top row filter/group by/having clause exists, it should be followed by a match clause if (node.WhereClause.SearchCondition != null && node.WhereClause.FirstTokenIndex < MatchList[i].LastTokenIndex) { var token = Tokens[nextToken]; _errors.Add(new ParseError(0, token.Offset, token.Line, token.Column, "WHERE clause should be followed by a MATCH clause")); } if (node.TopRowFilter != null && node.TopRowFilter.FirstTokenIndex < MatchList[i].LastTokenIndex) { var token = Tokens[nextToken]; _errors.Add(new ParseError(0, token.Offset, token.Line, token.Column, "Top row filter should be followed by a MATCH clause")); } if (node.GroupByClause != null && node.GroupByClause.FirstTokenIndex < MatchList[i].LastTokenIndex) { var token = Tokens[nextToken]; _errors.Add(new ParseError(0, token.Offset, token.Line, token.Column, "GROUP BY clause should be followed by a MATCH clause")); } if (node.HavingClause != null && node.HavingClause.FirstTokenIndex < MatchList[i].LastTokenIndex) { var token = Tokens[nextToken]; _errors.Add(new ParseError(0, token.Offset, token.Line, token.Column, "HAVING clause should be followed by a MATCH clause")); } if (flag) { var token = Tokens[nextToken]; _errors.Add(new ParseError(0, token.Offset, token.Line, token.Column, "Mutiple MATCH clauses in same query block")); } if (_errors.Count > 0) { return; } if (node.LastTokenIndex < MatchList[i].FirstTokenIndex) { node.LastTokenIndex = MatchList[i].LastTokenIndex; } flag = true; if (node.MatchClause != null && node.MatchClause.Paths != null && node.MatchClause.Paths.Any()) { foreach (var path in MatchList[i].Paths) { node.MatchClause.Paths.Add(path); } } else { node.MatchClause = MatchList[i]; } MatchFlag[i] = true; } }