A query must end in a RETURN clause, a procedure, or an updating clause (CREATE, MERGE, DELETE, SET, REMOVE, FOREACH or CALL {})
| 1810 | // A query must end in a RETURN clause, a procedure, or an updating clause |
| 1811 | // (CREATE, MERGE, DELETE, SET, REMOVE, FOREACH or CALL {}) |
| 1812 | static AST_Validation _ValidateQueryTermination |
| 1813 | ( |
| 1814 | const AST *ast // ast |
| 1815 | ) { |
| 1816 | ASSERT(ast != NULL); |
| 1817 | |
| 1818 | const cypher_astnode_t *root = ast->root; |
| 1819 | uint clause_idx = 0; |
| 1820 | const cypher_astnode_t *return_clause = NULL; |
| 1821 | const cypher_astnode_t *following_clause = NULL; |
| 1822 | uint clause_count = cypher_ast_query_nclauses(root); |
| 1823 | |
| 1824 | const cypher_astnode_t *last_clause = cypher_ast_query_get_clause(root, |
| 1825 | clause_count - 1); |
| 1826 | cypher_astnode_type_t type = cypher_astnode_type(last_clause); |
| 1827 | if(type != CYPHER_AST_RETURN && |
| 1828 | type != CYPHER_AST_CREATE && |
| 1829 | type != CYPHER_AST_MERGE && |
| 1830 | type != CYPHER_AST_DELETE && |
| 1831 | type != CYPHER_AST_SET && |
| 1832 | type != CYPHER_AST_CALL && |
| 1833 | type != CYPHER_AST_CALL_SUBQUERY && |
| 1834 | type != CYPHER_AST_REMOVE && |
| 1835 | type != CYPHER_AST_FOREACH |
| 1836 | ) { |
| 1837 | ErrorCtx_SetError("Query cannot conclude with %s (must be a RETURN \ |
| 1838 | clause, an update clause, a procedure call or a non-returning subquery)", |
| 1839 | cypher_astnode_typestr(type)); |
| 1840 | return AST_INVALID; |
| 1841 | } |
| 1842 | |
| 1843 | // if the last clause is CALL {}, it must be non-returning |
| 1844 | if(type == CYPHER_AST_CALL_SUBQUERY) { |
| 1845 | cypher_astnode_t *query = |
| 1846 | cypher_ast_call_subquery_get_query(last_clause); |
| 1847 | if(cypher_astnode_type(cypher_ast_query_get_clause(query, |
| 1848 | cypher_ast_query_nclauses(query)-1)) == |
| 1849 | CYPHER_AST_RETURN) { |
| 1850 | ErrorCtx_SetError("A query cannot conclude with a returning \ |
| 1851 | subquery (must be a RETURN clause, an update clause, a procedure call or a \ |
| 1852 | non-returning subquery)"); |
| 1853 | |
| 1854 | return AST_INVALID; |
| 1855 | } |
| 1856 | } |
| 1857 | |
| 1858 | // validate that `UNION` is the only clause following a `RETURN` clause, and |
| 1859 | // termination of embedded call {} clauses |
| 1860 | bool last_was_return = false; |
| 1861 | for(uint i = 0; i < clause_count; i++) { |
| 1862 | const cypher_astnode_t *clause = cypher_ast_query_get_clause(root, i); |
| 1863 | cypher_astnode_type_t type = cypher_astnode_type(clause); |
| 1864 | if(type != CYPHER_AST_UNION && last_was_return) { |
| 1865 | // unexpected clause following RETURN |
| 1866 | ErrorCtx_SetError("Unexpected clause following RETURN"); |
| 1867 | return AST_INVALID; |
| 1868 | } else if(type == CYPHER_AST_RETURN) { |
| 1869 | last_was_return = true; |
no test coverage detected