| 46 | } |
| 47 | |
| 48 | static ExecutionPlan *_ExecutionPlan_UnionPlans(AST *ast) { |
| 49 | uint end_offset = 0; |
| 50 | uint start_offset = 0; |
| 51 | uint clause_count = cypher_ast_query_nclauses(ast->root); |
| 52 | uint *union_indices = AST_GetClauseIndices(ast, CYPHER_AST_UNION); |
| 53 | array_append(union_indices, clause_count); |
| 54 | int union_count = array_len(union_indices); |
| 55 | ASSERT(union_count > 1); |
| 56 | |
| 57 | // Placeholder for each execution plan, these all will be joined |
| 58 | // via a single UNION operation |
| 59 | ExecutionPlan *plans[union_count]; |
| 60 | |
| 61 | for(int i = 0; i < union_count; i++) { |
| 62 | // Create an AST segment from which we will build an execution plan. |
| 63 | end_offset = union_indices[i]; |
| 64 | AST *ast_segment = AST_NewSegment(ast, start_offset, end_offset); |
| 65 | plans[i] = ExecutionPlan_FromTLS_AST(); |
| 66 | AST_Free(ast_segment); // Free the AST segment. |
| 67 | |
| 68 | // Next segment starts where this one ends. |
| 69 | start_offset = union_indices[i] + 1; |
| 70 | } |
| 71 | |
| 72 | QueryCtx_SetAST(ast); // AST segments have been freed, set master AST in QueryCtx. |
| 73 | |
| 74 | array_free(union_indices); |
| 75 | |
| 76 | /* Join streams: |
| 77 | * MATCH (a) RETURN a UNION MATCH (a) RETURN a .... |
| 78 | * left stream: [Scan]->[Project]->[Results] |
| 79 | * right stream: [Scan]->[Project]->[Results] |
| 80 | * |
| 81 | * Joined: |
| 82 | * left stream: [Scan]->[Project] |
| 83 | * right stream: [Scan]->[Project] |
| 84 | * [Union]->[Distinct]->[Result] */ |
| 85 | ExecutionPlan *plan = ExecutionPlan_NewEmptyExecutionPlan(); |
| 86 | plan->record_map = raxNew(); |
| 87 | |
| 88 | OpBase *results_op = NewResultsOp(plan); |
| 89 | OpBase *parent = results_op; |
| 90 | ExecutionPlan_UpdateRoot(plan, results_op); |
| 91 | |
| 92 | // Introduce distinct only if `ALL` isn't specified. |
| 93 | const cypher_astnode_t *union_clause = AST_GetClause(ast, CYPHER_AST_UNION, |
| 94 | NULL); |
| 95 | if(!cypher_ast_union_has_all(union_clause)) { |
| 96 | uint clause_count = cypher_ast_query_nclauses(ast->root); |
| 97 | const cypher_astnode_t *last_clause = cypher_ast_query_get_clause(ast->root, clause_count - 1); |
| 98 | if(cypher_astnode_type(last_clause) == CYPHER_AST_RETURN) { |
| 99 | uint projection_count = cypher_ast_return_nprojections(last_clause); |
| 100 | // Build a stack array to hold the aliases to perform Distinct on |
| 101 | const char *projections[projection_count]; |
| 102 | for(uint i = 0; i < projection_count; i++) { |
| 103 | // Retrieve aliases from the RETURN clause |
| 104 | const cypher_astnode_t *projection = cypher_ast_return_get_projection(last_clause, i); |
| 105 | const cypher_astnode_t *alias = cypher_ast_projection_get_alias(projection); |
no test coverage detected