# HG changeset patch # User Olaf Wintermann # Date 1514832877 -3600 # Node ID 9a75b6df33074189f9a86eff6fcbdd5eabe70bad # Parent b6f2462ee0558f26c4e4474113f90e6a5f7e250a# Parent 125b9c976f39c05670eabeb1b5fc8fcc7966fd79 merge diff -r b6f2462ee055 -r 9a75b6df3307 bsd.mk --- a/bsd.mk Mon Jan 01 19:53:36 2018 +0100 +++ b/bsd.mk Mon Jan 01 19:54:37 2018 +0100 @@ -43,5 +43,5 @@ APP_EXT = DAV_CFLAGS = `curl-config --cflags` `pkg-config --cflags libxml-2.0` -DAV_LDFLAGS = `curl-config --libs` `pkg-config --libs libxml-2.0` -lssl -lcrypto -lpthread +DAV_LDFLAGS = -L/usr/lib -lssl -lcrypto `curl-config --libs` `pkg-config --libs libxml-2.0` -lpthread diff -r b6f2462ee055 -r 9a75b6df3307 libidav/davqlparser.c --- a/libidav/davqlparser.c Mon Jan 01 19:53:36 2018 +0100 +++ b/libidav/davqlparser.c Mon Jan 01 19:54:37 2018 +0100 @@ -182,6 +182,19 @@ } } +static void dav_debug_ql_field_print(DavQLField *field) { + if (field) { + printf("Name: %.*s\n", sfmtarg(field->name)); + if (field->expr) { + dav_debug_ql_expr_print(field->expr); + } else { + printf("No expression.\n"); + } + } else { + printf("No field selected.\n"); + } +} + static void dav_debug_ql_tree_print(DavQLExpression *expr, int depth) { if (expr) { if (expr->left) { @@ -279,12 +292,14 @@ case DQLD_CMD_PT: dav_debug_ql_tree_print(examineexpr, 1); break; case DQLD_CMD_PF: dav_debug_ql_fnames_print(stmt); break; case DQLD_CMD_F: - if (examineclause != DQLD_CMD_F) { - examineclause = DQLD_CMD_F; - examineelem = stmt->fields; - examineexpr = stmt->fields ? - ((DavQLField*)stmt->fields->data)->expr : NULL; - dav_debug_ql_expr_print(examineexpr); + examineclause = DQLD_CMD_F; + examineelem = stmt->fields; + if (stmt->fields) { + DavQLField* field = ((DavQLField*)stmt->fields->data); + examineexpr = field->expr; + dav_debug_ql_field_print(field); + } else { + examineexpr = NULL; } break; case DQLD_CMD_W: @@ -293,13 +308,11 @@ dav_debug_ql_expr_print(examineexpr); break; case DQLD_CMD_O: - if (examineclause != DQLD_CMD_O) { - examineclause = DQLD_CMD_O; - examineelem = stmt->orderby; - examineexpr = stmt->orderby ? - ((DavQLOrderCriterion*)stmt->orderby->data)->column : NULL; - dav_debug_ql_expr_print(examineexpr); - } + examineclause = DQLD_CMD_O; + examineelem = stmt->orderby; + examineexpr = stmt->orderby ? + ((DavQLOrderCriterion*)stmt->orderby->data)->column : NULL; + dav_debug_ql_expr_print(examineexpr); break; case DQLD_CMD_N: case DQLD_CMD_P: @@ -311,12 +324,14 @@ if (examineclause == DQLD_CMD_O) { examineexpr = ((DavQLOrderCriterion*) examineelem->data)->column; + dav_debug_ql_expr_print(examineexpr); } else if (examineclause == DQLD_CMD_F) { - examineexpr = ((DavQLField*)examineelem->data)->expr; + DavQLField* field = (DavQLField*)examineelem->data; + examineexpr = field->expr; + dav_debug_ql_field_print(field); } else { printf("Examining unknown clause type."); } - dav_debug_ql_expr_print(examineexpr); } else { printf("Reached end of list.\n"); } @@ -379,10 +394,14 @@ #define _error_invalid_token "invalid token " _error_context #define _error_missing_path "expected path " _error_context #define _error_missing_from "expecting FROM keyword " _error_context +#define _error_missing_at "expecting AT keyword " _error_context #define _error_missing_by "expecting BY keyword " _error_context #define _error_missing_as "expecting alias ('as ') " _error_context #define _error_missing_identifier "expecting identifier " _error_context #define _error_missing_par "missing closed parenthesis " _error_context +#define _error_missing_assign "expecting assignment ('=') " _error_context +#define _error_missing_where "SET statements must have a WHERE clause or " \ + "explicitly use ANYWHERE " _error_context #define _error_invalid_depth "invalid depth " _error_context #define _error_missing_expr "missing expression " _error_context #define _error_invalid_expr "invalid expression " _error_context @@ -1462,6 +1481,91 @@ return total_consumed; } + +static int dav_parse_assignments(DavQLStatement *stmt, UcxList *token) { + + // RULE: Assignment, {",", Assignment} + int total_consumed = 0, consumed; + do { + // RULE: Identifier, "=", Expression + if (token_is(token, DAVQL_TOKEN_IDENTIFIER)) { + + // Identifier + DavQLField *field; + dqlsec_malloc(stmt, field, DavQLField); + field->name = token_sstr(token); + total_consumed++; + token = token->next; + + // "=" + if (!token_is(token, DAVQL_TOKEN_OPERATOR) + || !tokenvalue_is(token, "=")) { + dav_free_field(field); + + dav_error_in_context(DAVQL_ERROR_MISSING_ASSIGN, + _error_missing_assign, stmt, token); + return total_consumed; + } + total_consumed++; + token = token->next; + + // Expression + dqlsec_mallocz(stmt, field->expr, DavQLExpression); + consumed = dav_parse_expression(stmt, token, field->expr); + if (stmt->errorcode) { + dav_free_field(field); + return total_consumed; + } + token = ucx_list_get(token, consumed); + total_consumed += consumed; + + // Add assignment to list and check if there's another one + dqlsec_list_append_or_free(stmt, stmt->fields, field); + consumed = token_is(token, DAVQL_TOKEN_COMMA) ? 1 : 0; + if (consumed) { + token = token->next; + total_consumed++; + } + } else { + dav_error_in_context(DAVQL_ERROR_MISSING_TOKEN, + _error_missing_identifier, stmt, token); + return total_consumed; + } + } while (consumed); + + return total_consumed; +} + +static int dav_parse_path(DavQLStatement *stmt, UcxList *tokens) { + if (token_is(tokens, DAVQL_TOKEN_STRING)) { + stmt->path = token_sstr(tokens); + tokens = tokens->next; + return 1; + } else if (token_is(tokens, DAVQL_TOKEN_OPERATOR) + && tokenvalue_is(tokens, "/")) { + stmt->path = token_sstr(tokens); + tokens = tokens->next; + int consumed = 1; + while (!token_is(tokens, DAVQL_TOKEN_KEYWORD) && + !token_is(tokens, DAVQL_TOKEN_END)) { + sstr_t toksstr = token_sstr(tokens); + stmt->path.length = toksstr.ptr-stmt->path.ptr+toksstr.length; + tokens = tokens->next; + consumed++; + } + return consumed; + } else if (token_is(tokens, DAVQL_TOKEN_FMTSPEC) && + tokenvalue_is(tokens, "%s")) { + stmt->path = token_sstr(tokens); + tokens = tokens->next; + return 1; + } else { + dav_error_in_context(DAVQL_ERROR_MISSING_TOKEN, + _error_missing_path, stmt, tokens); + return 0; + } +} + /** * Parser of a select statement. * @param stmt the statement object that shall contain the syntax tree @@ -1476,7 +1580,7 @@ return; } - // Consume from keyword + // Consume FROM keyword if (token_is(tokens, DAVQL_TOKEN_KEYWORD) && tokenvalue_is(tokens, "from")) { tokens = tokens->next; @@ -1487,26 +1591,8 @@ } // Consume path - if (token_is(tokens, DAVQL_TOKEN_STRING)) { - stmt->path = token_sstr(tokens); - tokens = tokens->next; - } else if (token_is(tokens, DAVQL_TOKEN_OPERATOR) - && tokenvalue_is(tokens, "/")) { - stmt->path = token_sstr(tokens); - tokens = tokens->next; - while (!token_is(tokens, DAVQL_TOKEN_KEYWORD) && - !token_is(tokens, DAVQL_TOKEN_END)) { - sstr_t toksstr = token_sstr(tokens); - stmt->path.length = toksstr.ptr-stmt->path.ptr+toksstr.length; - tokens = tokens->next; - } - } else if (token_is(tokens, DAVQL_TOKEN_FMTSPEC) && - tokenvalue_is(tokens, "%s")) { - stmt->path = token_sstr(tokens); - tokens = tokens->next; - } else { - dav_error_in_context(DAVQL_ERROR_MISSING_TOKEN, - _error_missing_path, stmt, tokens); + tokens = ucx_list_get(tokens, dav_parse_path(stmt, tokens)); + if (stmt->errorcode) { return; } @@ -1527,6 +1613,11 @@ tokens = tokens->next; tokens = ucx_list_get(tokens, dav_parse_where_clause(stmt, tokens)); + } else if (token_is(tokens, DAVQL_TOKEN_KEYWORD) + && tokenvalue_is(tokens, "anywhere")) { + // useless, but the user may want to explicitly express his intent + tokens = tokens->next; + stmt->where = NULL; } if (stmt->errorcode) { return; @@ -1566,7 +1657,55 @@ static void dav_parse_set_statement(DavQLStatement *stmt, UcxList *tokens) { stmt->type = DAVQL_SET; - // TODO: make it so + // Consume assignments + tokens = ucx_list_get(tokens, dav_parse_assignments(stmt, tokens)); + if (stmt->errorcode) { + return; + } + + // Consume AT keyword + if (token_is(tokens, DAVQL_TOKEN_KEYWORD) + && tokenvalue_is(tokens, "at")) { + tokens = tokens->next; + } else { + dav_error_in_context(DAVQL_ERROR_MISSING_TOKEN, + _error_missing_at, stmt, tokens); + return; + } + + // Consume path + tokens = ucx_list_get(tokens, dav_parse_path(stmt, tokens)); + if (stmt->errorcode) { + return; + } + + // Consume with clause (if any) + if (token_is(tokens, DAVQL_TOKEN_KEYWORD) + && tokenvalue_is(tokens, "with")) { + tokens = tokens->next; + tokens = ucx_list_get(tokens, + dav_parse_with_clause(stmt, tokens)); + } + if (stmt->errorcode) { + return; + } + + // Consume mandatory where clause (or anywhere keyword) + if (token_is(tokens, DAVQL_TOKEN_KEYWORD) + && tokenvalue_is(tokens, "where")) { + tokens = tokens->next; + tokens = ucx_list_get(tokens, + dav_parse_where_clause(stmt, tokens)); + } else if (token_is(tokens, DAVQL_TOKEN_KEYWORD) + && tokenvalue_is(tokens, "anywhere")) { + // no-op, but we want the user to be explicit about this + tokens = tokens->next; + stmt->where = NULL; + } else { + dav_error_in_context(DAVQL_ERROR_MISSING_TOKEN, + _error_missing_where, stmt, tokens); + return; + } } DavQLStatement* dav_parse_statement(sstr_t srctext) { diff -r b6f2462ee055 -r 9a75b6df3307 libidav/davqlparser.h --- a/libidav/davqlparser.h Mon Jan 01 19:53:36 2018 +0100 +++ b/libidav/davqlparser.h Mon Jan 01 19:54:37 2018 +0100 @@ -200,10 +200,10 @@ * | "*", {",", NamedField} * | FieldExpression, {",", FieldExpression}; * FieldExpression = NamedField | Identifier; - * NamedField = Expression, " as ", Identifier; + * NamedField = Expression, " as ", Identifier; * - * SetExpressions = SetExpression, {",", SetExpression}; - * SetExpression = Identifier, "=", Expression; + * Assignments = Assignment, {",", Assignment}; + * Assignment = Identifier, "=", Expression; * * Path = String * | "/", [PathNode, {"/", PathNode}], ["/"]; @@ -225,13 +225,13 @@ * SelectStatement = "select ", FieldExpressions, * " from ", Path, * [" with ", WithClause], - * [" where ", LogicalExpression], + * [(" where ", LogicalExpression) | " anywhere"], * [" order by ", OrderByClause]; * * * SET: *
- * SetStatement = "set ",SetExpressions,
+ * SetStatement = "set ",Assignments,
  * " at ", Path,
  * [" with ", WithClause],
  * (" where ", LogicalExpression) | " anywhere";
@@ -304,6 +304,9 @@
 /** A closed parenthesis ')' is missing. */
 #define DAVQL_ERROR_MISSING_PAR 13
 
+/** An assignment operator '=' is missing. */
+#define DAVQL_ERROR_MISSING_ASSIGN 14
+
 /** The type of the expression could not be determined. */
 #define DAVQL_ERROR_INVALID_EXPR 21