856 lines
15 KiB
C
856 lines
15 KiB
C
/*
|
|
* Copyright (c) 2010, Swedish Institute of Computer Science
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the Institute nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
/**
|
|
* \file
|
|
* A recursive parser for AQL, the Antelope Query Language.
|
|
* \author
|
|
* Nicolas Tsiftes <nvt@sics.se>
|
|
*/
|
|
|
|
#include "attribute.h"
|
|
#include "db-options.h"
|
|
#include "index.h"
|
|
#include "aql.h"
|
|
#include "lvm.h"
|
|
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#define DEBUG DEBUG_NONE
|
|
#include "debug.h"
|
|
|
|
#if DEBUG
|
|
static char error_message[DB_ERROR_BUF_SIZE];
|
|
static int error_line;
|
|
static const char *error_function;
|
|
#define RETURN(value) \
|
|
do { \
|
|
if(error_message[0] == '\0') { \
|
|
strncpy(error_message, lexer->input, sizeof(error_message) - 1); \
|
|
error_line = __LINE__; \
|
|
error_function = __func__; \
|
|
} \
|
|
} while(0); \
|
|
return (value)
|
|
#define RESET_ERROR() \
|
|
do { \
|
|
error_message[0] = '\0'; \
|
|
error_line = 0; \
|
|
error_function = NULL; \
|
|
} while(0)
|
|
#else
|
|
#define RETURN(value) return (value)
|
|
#define RESET_ERROR()
|
|
#endif
|
|
#define PARSER(name) \
|
|
static aql_status_t \
|
|
parse_##name(lexer_t *lexer)
|
|
#define PARSER_ARG(name, arg) \
|
|
static aql_status_t \
|
|
parse_##name(lexer_t *lexer, arg)
|
|
#define PARSER_TOKEN(name) \
|
|
static token_t \
|
|
parse_##name(lexer_t *lexer)
|
|
#define PARSE(name) \
|
|
!AQL_ERROR(parse_##name(lexer))
|
|
#define PARSE_TOKEN(name) \
|
|
parse_##name(lexer)
|
|
|
|
#define NEXT lexer_next(lexer)
|
|
#define REWIND lexer_rewind(lexer); RESET_ERROR()
|
|
#define TOKEN *lexer->token
|
|
#define VALUE *lexer->value
|
|
|
|
#define CONSUME(token) \
|
|
do { \
|
|
NEXT; \
|
|
if(TOKEN != (token)) { \
|
|
RETURN(SYNTAX_ERROR); \
|
|
} \
|
|
} while(0)
|
|
|
|
/* The parsing of AQL results in this aql_adt_t object. */
|
|
static aql_adt_t *adt;
|
|
|
|
/* Conditional statements are compiled into VM bytecode, which is stored in
|
|
an instance of the LogicVM. */
|
|
static lvm_instance_t p;
|
|
static unsigned char vmcode[DB_VM_BYTECODE_SIZE];
|
|
|
|
/* Parsing functions for AQL. */
|
|
PARSER_TOKEN(cmp)
|
|
{
|
|
NEXT;
|
|
switch(TOKEN) {
|
|
case EQUAL:
|
|
case NOT_EQUAL:
|
|
case GT:
|
|
case LT:
|
|
case GEQ:
|
|
case LEQ:
|
|
return TOKEN;
|
|
default:
|
|
return NONE;
|
|
}
|
|
}
|
|
|
|
PARSER_TOKEN(op)
|
|
{
|
|
NEXT;
|
|
switch(TOKEN) {
|
|
case ADD:
|
|
case SUB:
|
|
case MUL:
|
|
case DIV:
|
|
case RIGHT_PAREN:
|
|
return TOKEN;
|
|
default:
|
|
return NONE;
|
|
}
|
|
}
|
|
|
|
PARSER_TOKEN(aggregator)
|
|
{
|
|
NEXT;
|
|
switch(TOKEN) {
|
|
case COUNT:
|
|
case SUM:
|
|
case MEAN:
|
|
case MEDIAN:
|
|
case MAX:
|
|
case MIN:
|
|
return TOKEN;
|
|
default:
|
|
return NONE;
|
|
}
|
|
}
|
|
|
|
PARSER(attributes)
|
|
{
|
|
token_t token;
|
|
aql_aggregator_t function;
|
|
|
|
token = PARSE_TOKEN(aggregator);
|
|
if(token != NONE) {
|
|
switch(TOKEN) {
|
|
case COUNT:
|
|
function = AQL_COUNT;
|
|
break;
|
|
case SUM:
|
|
function = AQL_SUM;
|
|
break;
|
|
case MEAN:
|
|
function = AQL_MEAN;
|
|
break;
|
|
case MEDIAN:
|
|
function = AQL_MEDIAN;
|
|
break;
|
|
case MAX:
|
|
function = AQL_MAX;
|
|
break;
|
|
case MIN:
|
|
function = AQL_MIN;
|
|
break;
|
|
default:
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
|
|
AQL_SET_FLAG(adt, AQL_FLAG_AGGREGATE);
|
|
|
|
PRINTF("aggregator: %d\n", TOKEN);
|
|
|
|
/* Parse the attribute to aggregate. */
|
|
CONSUME(LEFT_PAREN);
|
|
CONSUME(IDENTIFIER);
|
|
|
|
AQL_ADD_AGGREGATE(adt, function, VALUE);
|
|
PRINTF("aggregated attribute: %s\n", VALUE);
|
|
|
|
CONSUME(RIGHT_PAREN);
|
|
goto check_more_attributes;
|
|
} else {
|
|
REWIND;
|
|
}
|
|
|
|
/* Plain identifier. */
|
|
|
|
CONSUME(IDENTIFIER);
|
|
|
|
AQL_ADD_ATTRIBUTE(adt, VALUE, DOMAIN_UNSPECIFIED, 0);
|
|
|
|
check_more_attributes:
|
|
NEXT;
|
|
if(TOKEN == COMMA) {
|
|
if(!PARSE(attributes)) {
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
} else {
|
|
REWIND;
|
|
}
|
|
|
|
RETURN(OK);
|
|
}
|
|
|
|
PARSER(relations)
|
|
{
|
|
/* Parse comma-separated identifiers for relations. */
|
|
CONSUME(IDENTIFIER);
|
|
|
|
AQL_ADD_RELATION(adt, VALUE);
|
|
NEXT;
|
|
if(TOKEN == COMMA) {
|
|
if(!PARSE(relations)) {
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
} else {
|
|
REWIND;
|
|
}
|
|
|
|
RETURN(OK);
|
|
}
|
|
|
|
PARSER(values)
|
|
{
|
|
/* Parse comma-separated attribute values. */
|
|
NEXT;
|
|
switch(TOKEN) {
|
|
case STRING_VALUE:
|
|
AQL_ADD_VALUE(adt, DOMAIN_STRING, VALUE);
|
|
break;
|
|
case INTEGER_VALUE:
|
|
AQL_ADD_VALUE(adt, DOMAIN_INT, VALUE);
|
|
break;
|
|
default:
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
|
|
NEXT;
|
|
if(TOKEN == COMMA) {
|
|
return PARSE(values);
|
|
} else {
|
|
REWIND;
|
|
}
|
|
|
|
RETURN(OK);
|
|
}
|
|
|
|
PARSER(operand)
|
|
{
|
|
NEXT;
|
|
switch(TOKEN) {
|
|
case IDENTIFIER:
|
|
lvm_register_variable(VALUE, LVM_LONG);
|
|
lvm_set_variable(&p, VALUE);
|
|
AQL_ADD_PROCESSING_ATTRIBUTE(adt, VALUE);
|
|
break;
|
|
case STRING_VALUE:
|
|
break;
|
|
case FLOAT_VALUE:
|
|
break;
|
|
case INTEGER_VALUE:
|
|
lvm_set_long(&p, *(long *)lexer->value);
|
|
break;
|
|
default:
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
|
|
RETURN(OK);
|
|
}
|
|
|
|
PARSER(expr)
|
|
{
|
|
token_t token;
|
|
size_t saved_end;
|
|
operator_t op;
|
|
|
|
saved_end = lvm_get_end(&p);
|
|
|
|
NEXT;
|
|
if(TOKEN == LEFT_PAREN) {
|
|
if(!PARSE(expr)) {
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
CONSUME(RIGHT_PAREN);
|
|
} else {
|
|
REWIND;
|
|
if(!PARSE(operand)) {
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
}
|
|
|
|
while(1) {
|
|
token = PARSE_TOKEN(op);
|
|
if(token == NONE) {
|
|
saved_end = lvm_get_end(&p);
|
|
REWIND;
|
|
break;
|
|
} else if (token == RIGHT_PAREN) {
|
|
break;
|
|
}
|
|
|
|
if(!PARSE(operand) && !PARSE(expr)) {
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
|
|
saved_end = lvm_shift_for_operator(&p, saved_end);
|
|
|
|
switch(token) {
|
|
case ADD:
|
|
op = LVM_ADD;
|
|
break;
|
|
case SUB:
|
|
op = LVM_SUB;
|
|
break;
|
|
case MUL:
|
|
op = LVM_MUL;
|
|
break;
|
|
case DIV:
|
|
op = LVM_DIV;
|
|
break;
|
|
default:
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
lvm_set_op(&p, op);
|
|
lvm_set_end(&p, saved_end);
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
PARSER(comparison)
|
|
{
|
|
token_t token;
|
|
size_t saved_end;
|
|
operator_t rel;
|
|
|
|
saved_end = lvm_jump_to_operand(&p);
|
|
|
|
if(!PARSE(expr)) {
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
|
|
saved_end = lvm_set_end(&p, saved_end);
|
|
|
|
token = PARSE_TOKEN(cmp);
|
|
if(token == NONE) {
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
|
|
switch(token) {
|
|
case GT:
|
|
rel = LVM_GE;
|
|
break;
|
|
case GEQ:
|
|
rel = LVM_GEQ;
|
|
break;
|
|
case LT:
|
|
rel = LVM_LE;
|
|
break;
|
|
case LEQ:
|
|
rel = LVM_LEQ;
|
|
break;
|
|
case EQUAL:
|
|
rel = LVM_EQ;
|
|
break;
|
|
case NOT_EQUAL:
|
|
rel = LVM_NEQ;
|
|
break;
|
|
default:
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
|
|
lvm_set_relation(&p, rel);
|
|
lvm_set_end(&p, saved_end);
|
|
|
|
if(!PARSE(expr)) {
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
|
|
RETURN(OK);
|
|
}
|
|
|
|
PARSER(where)
|
|
{
|
|
int r;
|
|
operator_t connective;
|
|
size_t saved_end;
|
|
|
|
if(!PARSE(comparison)) {
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
|
|
saved_end = 0;
|
|
|
|
/* The WHERE clause can consist of multiple prepositions. */
|
|
for(;;) {
|
|
NEXT;
|
|
if(TOKEN != AND && TOKEN != OR) {
|
|
REWIND;
|
|
break;
|
|
}
|
|
|
|
connective = TOKEN == AND ? LVM_AND : LVM_OR;
|
|
|
|
saved_end = lvm_shift_for_operator(&p, saved_end);
|
|
lvm_set_relation(&p, connective);
|
|
lvm_set_end(&p, saved_end);
|
|
|
|
NEXT;
|
|
if(TOKEN == LEFT_PAREN) {
|
|
r = PARSE(where);
|
|
if(!r) {
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
CONSUME(RIGHT_PAREN);
|
|
} else {
|
|
REWIND;
|
|
r = PARSE(comparison);
|
|
if(!r) {
|
|
RETURN(r);
|
|
}
|
|
}
|
|
}
|
|
|
|
lvm_print_code(&p);
|
|
|
|
return OK;
|
|
}
|
|
|
|
PARSER(join)
|
|
{
|
|
AQL_SET_TYPE(adt, AQL_TYPE_JOIN);
|
|
|
|
CONSUME(IDENTIFIER);
|
|
|
|
PRINTF("Left relation: %s\n", VALUE);
|
|
AQL_ADD_RELATION(adt, VALUE);
|
|
|
|
CONSUME(COMMA);
|
|
CONSUME(IDENTIFIER);
|
|
|
|
PRINTF("Right relation: %s\n", VALUE);
|
|
AQL_ADD_RELATION(adt, VALUE);
|
|
|
|
CONSUME(ON);
|
|
CONSUME(IDENTIFIER);
|
|
|
|
PRINTF("Join on attribute %s\n", VALUE);
|
|
AQL_ADD_ATTRIBUTE(adt, VALUE, DOMAIN_UNSPECIFIED, 0);
|
|
|
|
CONSUME(PROJECT);
|
|
|
|
/* projection attributes... */
|
|
if(!PARSE(attributes)) {
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
|
|
CONSUME(END);
|
|
|
|
RETURN(OK);
|
|
}
|
|
|
|
PARSER(select)
|
|
{
|
|
AQL_SET_TYPE(adt, AQL_TYPE_SELECT);
|
|
|
|
/* projection attributes... */
|
|
if(!PARSE(attributes)) {
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
|
|
CONSUME(FROM);
|
|
if(!PARSE(relations)) {
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
|
|
NEXT;
|
|
if(TOKEN == WHERE) {
|
|
lvm_reset(&p, vmcode, sizeof(vmcode));
|
|
|
|
if(!PARSE(where)) {
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
|
|
AQL_SET_CONDITION(adt, &p);
|
|
} else {
|
|
REWIND;
|
|
RETURN(OK);
|
|
}
|
|
|
|
CONSUME(END);
|
|
|
|
return OK;
|
|
}
|
|
|
|
PARSER(insert)
|
|
{
|
|
AQL_SET_TYPE(adt, AQL_TYPE_INSERT);
|
|
|
|
CONSUME(LEFT_PAREN);
|
|
|
|
if(!PARSE(values)) {
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
|
|
CONSUME(RIGHT_PAREN);
|
|
CONSUME(INTO);
|
|
|
|
if(!PARSE(relations)) {
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
|
|
RETURN(OK);
|
|
}
|
|
|
|
PARSER(remove_attribute)
|
|
{
|
|
AQL_SET_TYPE(adt, AQL_TYPE_REMOVE_ATTRIBUTE);
|
|
|
|
CONSUME(IDENTIFIER);
|
|
AQL_ADD_RELATION(adt, VALUE);
|
|
|
|
CONSUME(DOT);
|
|
CONSUME(IDENTIFIER);
|
|
|
|
PRINTF("Removing the index for the attribute %s\n", VALUE);
|
|
AQL_ADD_ATTRIBUTE(adt, VALUE, DOMAIN_UNSPECIFIED, 0);
|
|
|
|
RETURN(OK);
|
|
}
|
|
|
|
#if DB_FEATURE_REMOVE
|
|
PARSER(remove_from)
|
|
{
|
|
AQL_SET_TYPE(adt, AQL_TYPE_REMOVE_TUPLES);
|
|
|
|
/* Use a temporary persistent relation to assign the query result to. */
|
|
AQL_SET_FLAG(adt, AQL_FLAG_ASSIGN);
|
|
AQL_ADD_RELATION(adt, TEMP_RELATION);
|
|
|
|
CONSUME(IDENTIFIER);
|
|
AQL_ADD_RELATION(adt, VALUE);
|
|
|
|
CONSUME(WHERE);
|
|
|
|
lvm_reset(&p, vmcode, sizeof(vmcode));
|
|
AQL_SET_CONDITION(adt, &p);
|
|
|
|
return PARSE(where);
|
|
|
|
}
|
|
#endif /* DB_FEATURE_REMOVE */
|
|
|
|
PARSER(remove_index)
|
|
{
|
|
AQL_SET_TYPE(adt, AQL_TYPE_REMOVE_INDEX);
|
|
|
|
CONSUME(IDENTIFIER);
|
|
AQL_ADD_RELATION(adt, VALUE);
|
|
|
|
CONSUME(DOT);
|
|
CONSUME(IDENTIFIER);
|
|
|
|
PRINTF("remove index: %s\n", VALUE);
|
|
AQL_ADD_ATTRIBUTE(adt, VALUE, DOMAIN_UNSPECIFIED, 0);
|
|
|
|
RETURN(OK);
|
|
}
|
|
|
|
PARSER(remove_relation)
|
|
{
|
|
AQL_SET_TYPE(adt, AQL_TYPE_REMOVE_RELATION);
|
|
|
|
CONSUME(IDENTIFIER);
|
|
PRINTF("remove relation: %s\n", VALUE);
|
|
AQL_ADD_RELATION(adt, VALUE);
|
|
|
|
RETURN(OK);
|
|
}
|
|
|
|
PARSER(remove)
|
|
{
|
|
aql_status_t r;
|
|
|
|
NEXT;
|
|
switch(TOKEN) {
|
|
case ATTRIBUTE:
|
|
r = PARSE(remove_attribute);
|
|
break;
|
|
#if DB_FEATURE_REMOVE
|
|
case FROM:
|
|
r = PARSE(remove_from);
|
|
break;
|
|
#endif
|
|
case INDEX:
|
|
r = PARSE(remove_index);
|
|
break;
|
|
case RELATION:
|
|
r = PARSE(remove_relation);
|
|
break;
|
|
default:
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
|
|
if(!r) {
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
|
|
CONSUME(END);
|
|
|
|
RETURN(OK);
|
|
}
|
|
|
|
PARSER_TOKEN(index_type)
|
|
{
|
|
index_type_t type;
|
|
|
|
NEXT;
|
|
switch(TOKEN) {
|
|
case INLINE:
|
|
type = INDEX_INLINE;
|
|
break;
|
|
case MAXHEAP:
|
|
type = INDEX_MAXHEAP;
|
|
break;
|
|
case MEMHASH:
|
|
type = INDEX_MEMHASH;
|
|
break;
|
|
default:
|
|
return NONE;
|
|
};
|
|
|
|
AQL_SET_INDEX_TYPE(adt, type);
|
|
return TOKEN;
|
|
}
|
|
|
|
PARSER(create_index)
|
|
{
|
|
AQL_SET_TYPE(adt, AQL_TYPE_CREATE_INDEX);
|
|
|
|
CONSUME(IDENTIFIER);
|
|
AQL_ADD_RELATION(adt, VALUE);
|
|
|
|
CONSUME(DOT);
|
|
CONSUME(IDENTIFIER);
|
|
|
|
PRINTF("Creating an index for the attribute %s\n", VALUE);
|
|
AQL_ADD_ATTRIBUTE(adt, VALUE, DOMAIN_UNSPECIFIED, 0);
|
|
|
|
CONSUME(TYPE);
|
|
|
|
if(PARSE_TOKEN(index_type) == NONE) {
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
|
|
RETURN(OK);
|
|
}
|
|
|
|
PARSER(create_relation)
|
|
{
|
|
CONSUME(IDENTIFIER);
|
|
|
|
AQL_SET_TYPE(adt, AQL_TYPE_CREATE_RELATION);
|
|
AQL_ADD_RELATION(adt, VALUE);
|
|
|
|
RETURN(OK);
|
|
}
|
|
|
|
PARSER_ARG(domain, char *name)
|
|
{
|
|
domain_t domain;
|
|
unsigned element_size;
|
|
|
|
NEXT;
|
|
switch(TOKEN) {
|
|
case STRING:
|
|
domain = DOMAIN_STRING;
|
|
|
|
/* Parse the amount of characters for this domain. */
|
|
CONSUME(LEFT_PAREN);
|
|
CONSUME(INTEGER_VALUE);
|
|
element_size = *(long *)lexer->value;
|
|
CONSUME(RIGHT_PAREN);
|
|
|
|
break;
|
|
case LONG:
|
|
domain = DOMAIN_LONG;
|
|
element_size = 4;
|
|
break;
|
|
case INT:
|
|
domain = DOMAIN_INT;
|
|
element_size = 2;
|
|
break;
|
|
default:
|
|
return NONE;
|
|
}
|
|
|
|
AQL_ADD_ATTRIBUTE(adt, name, domain, element_size);
|
|
|
|
return OK;
|
|
}
|
|
|
|
PARSER(create_attributes)
|
|
{
|
|
aql_status_t r;
|
|
char name[ATTRIBUTE_NAME_LENGTH];
|
|
|
|
AQL_SET_TYPE(adt, AQL_TYPE_CREATE_ATTRIBUTE);
|
|
|
|
CONSUME(IDENTIFIER);
|
|
|
|
strncpy(name, VALUE, sizeof(name) - 1);
|
|
name[sizeof(name) - 1] = '\0';
|
|
|
|
CONSUME(DOMAIN);
|
|
|
|
r = parse_domain(lexer, name);
|
|
if(AQL_ERROR(r)) {
|
|
RETURN(r);
|
|
}
|
|
|
|
CONSUME(IN);
|
|
CONSUME(IDENTIFIER);
|
|
|
|
AQL_ADD_RELATION(adt, VALUE);
|
|
|
|
RETURN(OK);
|
|
}
|
|
|
|
PARSER(create)
|
|
{
|
|
aql_status_t r;
|
|
|
|
NEXT;
|
|
switch(TOKEN) {
|
|
case ATTRIBUTE:
|
|
r = PARSE(create_attributes);
|
|
break;
|
|
case INDEX:
|
|
r = PARSE(create_index);
|
|
break;
|
|
case RELATION:
|
|
r = PARSE(create_relation);
|
|
break;
|
|
default:
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
|
|
if(!r) {
|
|
RETURN(SYNTAX_ERROR);
|
|
}
|
|
|
|
CONSUME(END);
|
|
|
|
RETURN(OK);
|
|
}
|
|
|
|
aql_status_t
|
|
aql_parse(aql_adt_t *external_adt, char *input_string)
|
|
{
|
|
lexer_t lex;
|
|
token_t token = NONE;
|
|
value_t value;
|
|
aql_status_t result;
|
|
|
|
RESET_ERROR();
|
|
|
|
PRINTF("Parsing \"%s\"\n", input_string);
|
|
|
|
adt = external_adt;
|
|
AQL_CLEAR(adt);
|
|
AQL_SET_CONDITION(adt, NULL);
|
|
|
|
lexer_start(&lex, input_string, &token, &value);
|
|
|
|
result = lexer_next(&lex);
|
|
if(!AQL_ERROR(result)) {
|
|
switch(token) {
|
|
case IDENTIFIER:
|
|
PRINTF("Assign the result to relation %s\n", *lex.value);
|
|
AQL_ADD_RELATION(adt, *lex.value);
|
|
AQL_SET_FLAG(adt, AQL_FLAG_ASSIGN);
|
|
if(AQL_ERROR(lexer_next(&lex))) {
|
|
result = SYNTAX_ERROR;
|
|
break;
|
|
}
|
|
if(*lex.token != ASSIGN) {
|
|
result = SYNTAX_ERROR;
|
|
break;
|
|
}
|
|
if(AQL_ERROR(lexer_next(&lex))) {
|
|
result = SYNTAX_ERROR;
|
|
break;
|
|
}
|
|
switch(*lex.token) {
|
|
case SELECT:
|
|
result = parse_select(&lex);
|
|
break;
|
|
case JOIN:
|
|
result = parse_join(&lex);
|
|
break;
|
|
default:
|
|
result = SYNTAX_ERROR;
|
|
}
|
|
break;
|
|
case JOIN:
|
|
result = parse_join(&lex);
|
|
break;
|
|
case CREATE:
|
|
result = parse_create(&lex);
|
|
break;
|
|
case REMOVE:
|
|
result = parse_remove(&lex);
|
|
break;
|
|
case INSERT:
|
|
result = parse_insert(&lex);
|
|
break;
|
|
case SELECT:
|
|
result = parse_select(&lex);
|
|
break;
|
|
case NONE:
|
|
case COMMENT:
|
|
result = OK;
|
|
case END:
|
|
break;
|
|
default:
|
|
result = SYNTAX_ERROR;
|
|
}
|
|
}
|
|
|
|
if(AQL_ERROR(result)) {
|
|
PRINTF("Error in function %s, line %d: input \"%s\"\n",
|
|
error_function, error_line, error_message);
|
|
}
|
|
|
|
return result;
|
|
}
|