validate UNION clauses
| 988 | |
| 989 | // validate UNION clauses |
| 990 | static AST_Validation _ValidateUnion_Clauses |
| 991 | ( |
| 992 | const AST *ast // ast-node |
| 993 | ) { |
| 994 | AST_Validation res = AST_VALID; |
| 995 | |
| 996 | uint *union_indices = AST_GetClauseIndices(ast, CYPHER_AST_UNION); |
| 997 | uint union_clause_count = array_len(union_indices); |
| 998 | array_free(union_indices); |
| 999 | |
| 1000 | if(union_clause_count != 0) { |
| 1001 | // Require all RETURN clauses to perform the exact same projection |
| 1002 | uint *return_indices = AST_GetClauseIndices(ast, CYPHER_AST_RETURN); |
| 1003 | uint return_clause_count = array_len(return_indices); |
| 1004 | |
| 1005 | // We should have one more RETURN clauses than we have UNION clauses. |
| 1006 | if(return_clause_count != union_clause_count + 1) { |
| 1007 | ErrorCtx_SetError("Found %d UNION clauses but only %d RETURN clauses.", union_clause_count, |
| 1008 | return_clause_count); |
| 1009 | array_free(return_indices); |
| 1010 | return AST_INVALID; |
| 1011 | } |
| 1012 | |
| 1013 | const cypher_astnode_t *return_clause = cypher_ast_query_get_clause(ast->root, return_indices[0]); |
| 1014 | uint proj_count = cypher_ast_return_nprojections(return_clause); |
| 1015 | const char *projections[proj_count]; |
| 1016 | |
| 1017 | for(uint j = 0; j < proj_count; j++) { |
| 1018 | const cypher_astnode_t *proj = cypher_ast_return_get_projection(return_clause, j); |
| 1019 | const cypher_astnode_t *alias_node = cypher_ast_projection_get_alias(proj); |
| 1020 | if(alias_node == NULL) { |
| 1021 | // The projection was not aliased, so the projection itself must be an identifier. |
| 1022 | alias_node = cypher_ast_projection_get_expression(proj); |
| 1023 | ASSERT(cypher_astnode_type(alias_node) == CYPHER_AST_IDENTIFIER); |
| 1024 | } |
| 1025 | const char *alias = cypher_ast_identifier_get_name(alias_node); |
| 1026 | projections[j] = alias; |
| 1027 | } |
| 1028 | |
| 1029 | for(uint i = 1; i < return_clause_count; i++) { |
| 1030 | return_clause = cypher_ast_query_get_clause(ast->root, return_indices[i]); |
| 1031 | if(proj_count != cypher_ast_return_nprojections(return_clause)) { |
| 1032 | ErrorCtx_SetError("All sub queries in a UNION must have the same column names."); |
| 1033 | res = AST_INVALID; |
| 1034 | goto cleanup; |
| 1035 | } |
| 1036 | |
| 1037 | for(uint j = 0; j < proj_count; j++) { |
| 1038 | const cypher_astnode_t *proj = cypher_ast_return_get_projection(return_clause, j); |
| 1039 | const cypher_astnode_t *alias_node = cypher_ast_projection_get_alias(proj); |
| 1040 | if(alias_node == NULL) { |
| 1041 | // The projection was not aliased, so the projection itself must be an identifier. |
| 1042 | alias_node = cypher_ast_projection_get_expression(proj); |
| 1043 | ASSERT(cypher_astnode_type(alias_node) == CYPHER_AST_IDENTIFIER); |
| 1044 | } |
| 1045 | const char *alias = cypher_ast_identifier_get_name(alias_node); |
| 1046 | if(strcmp(projections[j], alias) != 0) { |
| 1047 | ErrorCtx_SetError("All sub queries in a UNION must have the same column names."); |
no test coverage detected