8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-24 06:03:02 +01:00
firebird-mirror/src/qli/parse.cpp
2008-01-15 20:15:58 +00:00

5542 lines
122 KiB
C++

/*
* PROGRAM: JRD Command Oriented Query Language
* MODULE: parse.cpp
* DESCRIPTION: Statement parser
*
* The contents of this file are subject to the Interbase Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy
* of the License at http://www.Inprise.com/IPL.html
*
* Software distributed under the License is distributed on an
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
* or implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code was created by Inprise Corporation
* and its predecessors. Portions created by Inprise Corporation are
* Copyright (C) Inprise Corporation.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*/
#include "firebird.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../qli/dtr.h"
#include "../qli/exe.h" // This is only included to suppress a compiler warning
#include "../qli/parse.h"
#include "../qli/compile.h"
#include "../qli/report.h"
#include "../qli/all_proto.h"
#include "../qli/err_proto.h"
#include "../qli/hsh_proto.h"
#include "../qli/lex_proto.h"
#include "../qli/mov_proto.h"
#include "../qli/parse_proto.h"
#include "../qli/proc_proto.h"
#include "../jrd/gdsassert.h"
#include "../jrd/constants.h"
using MsgFormat::SafeArg;
#define KEYWORD(kw) (QLI_token->tok_keyword == kw)
#define INT_CAST (qli_syntax*) (IPTR)
static void check_end(void);
static void command_end(void);
static DBB get_dbb(qli_symbol*);
static qli_syntax* make_list(qli_lls*);
static NAM make_name(void);
static qli_const* make_numeric_constant(const TEXT*, USHORT);
static TEXT* make_string(const TEXT*, USHORT);
static qli_syntax* negate(qli_syntax*);
static KWWORDS next_keyword(void);
static qli_syntax* parse_abort(void);
static qli_syntax* parse_accept(void);
static qli_syntax* parse_add(USHORT *, bool *);
static qli_syntax* parse_and(USHORT *);
static qli_syntax* parse_assignment(void);
static qli_syntax* parse_boolean(USHORT *);
static qli_syntax* parse_copy(void);
static DBB parse_database(void);
static qli_syntax* parse_declare(void);
static qli_syntax* parse_define(void);
static qli_syntax* parse_def_index(void);
static qli_syntax* parse_def_relation(void);
static qli_syntax* parse_delete(void);
static qli_syntax* parse_drop(void);
static int parse_dtype(USHORT *, USHORT *);
static int parse_dtype_subtype(void);
static qli_syntax* parse_edit(void);
static TEXT* parse_edit_string(void);
static qli_syntax* parse_erase(void);
static qli_syntax* parse_extract(void);
static qli_fld* parse_field(bool);
static qli_syntax* parse_field_name(qli_syntax* *);
static qli_syntax* parse_for(void);
static qli_syntax* parse_from(USHORT*, bool*);
static qli_syntax* parse_function(void);
static TEXT* parse_header(void);
static qli_syntax* parse_help(void);
static qli_syntax* parse_if(void);
static qli_syntax* parse_in(qli_syntax*, NOD_T, bool);
static qli_syntax* parse_insert(void);
static NOD_T parse_join_type(void);
static qli_syntax* parse_list_fields(void);
static qli_const* parse_literal(void);
static qli_syntax* parse_matches(void);
static void parse_matching_paren(void);
static qli_syntax* parse_modify(void);
static qli_syntax* parse_modify_index(void);
static qli_syntax* parse_modify_relation(void);
static qli_syntax* parse_multiply(USHORT*, bool*);
static NAM parse_name(void);
static qli_syntax* parse_not(USHORT *);
static int parse_ordinal(void);
static qli_syntax* parse_output(void);
static qli_syntax* parse_primitive_value(USHORT*, bool*);
static qli_syntax* parse_print_list(void);
static qli_syntax* parse_print(void);
static qli_syntax* parse_prompt(void);
static QFL parse_qualified_filter(void);
static QFN parse_qualified_function(void);
static QPR parse_qualified_procedure(void);
static qli_rel* parse_qualified_relation(void);
static qli_syntax* parse_ready(NOD_T);
static qli_syntax* parse_relational(USHORT*);
static qli_syntax* parse_relation(void);
static qli_syntax* parse_rename(void);
static qli_syntax* parse_repeat(void);
static qli_syntax* parse_report(void);
static qli_syntax* parse_rse(void);
static qli_syntax* parse_select(void);
static qli_syntax* parse_set(void);
static qli_syntax* parse_shell(void);
static qli_syntax* parse_show(void);
static qli_syntax* parse_sort(void);
static qli_syntax* parse_sql_alter(void);
static qli_syntax* parse_sql_create(void);
static int parse_sql_dtype(USHORT* length, USHORT* scale, USHORT* precision,
USHORT* sub_type);
static qli_fld* parse_sql_field(void);
static qli_syntax* parse_sql_grant_revoke(USHORT);
static qli_syntax* parse_sql_index_create(const bool, const bool);
static qli_syntax* parse_sql_joined_relation(qli_syntax*);
static qli_syntax* parse_sql_join_clause(qli_syntax*);
static qli_syntax* parse_sql_table_create(void);
#ifdef NOT_USED_OR_REPLACED
static qli_syntax* parse_sql_view_create(void);
#endif
static qli_syntax* parse_sql_relation(void);
static qli_syntax* parse_sql_rse(void);
static qli_syntax* parse_sql_singleton_select(void);
static qli_syntax* parse_sql_subquery(void);
static qli_syntax* parse_statement(void);
static qli_syntax* parse_statistical(void);
static qli_syntax* parse_store(void);
static TEXT *parse_string(void);
static qli_symbol* parse_symbol(void);
static void parse_terminating_parens(USHORT*, USHORT*);
static qli_syntax* parse_transaction(NOD_T);
static qli_syntax* parse_udf_or_field(void);
static qli_syntax* parse_update(void);
static qli_syntax* parse_value(USHORT*, bool*);
static bool potential_rse(void);
static qli_rel* resolve_relation(qli_symbol*, qli_symbol*);
static qli_syntax* syntax_node(NOD_T, USHORT);
static bool test_end(void);
typedef struct {
SLONG gds_quad_high;
ULONG gds_quad_low;
} gds_quad;
/*
The following flags are:
sql_flag indicates whether we are parsing in a SQL environment.
The flag is used to turn off automatic end-of-command
recognition.
else_count indicates the depth of if/then/else's
sw_report indicates whether we're in a report statement
sw_statement indicates that we're actively parsing a statement/command
sw_sql_view indicates parsing a SQL VIEW, so restrict the select.
*/
static USHORT sql_flag, else_count, sw_report;
static bool sw_statement, sw_sql_view;
static SSHORT function_count; // indicates the depth of UDF calls
struct nod_types {
KWWORDS nod_t_keyword;
NOD_T nod_t_node;
NOD_T nod_t_rpt_node;
NOD_T nod_t_sql_node;
};
static const nod_types statisticals[] = {
{ KW_MAX, nod_max, nod_rpt_max, nod_agg_max },
{ KW_MIN, nod_min, nod_rpt_min, nod_agg_min },
{ KW_COUNT, nod_count, nod_rpt_count, nod_agg_count },
{ KW_AVERAGE, nod_average, nod_rpt_average, nod_agg_average },
{ KW_TOTAL, nod_total, nod_rpt_total, nod_agg_total },
{ KW_none, nod_any, nod_any, nod_any }
};
static const NOD_T relationals[] = {
nod_eql, nod_neq, nod_leq, nod_lss, nod_gtr, nod_geq, nod_containing,
nod_like, nod_starts, nod_missing, nod_between, nod_sleuth,
nod_matches, nod_and, nod_or, nod_not, nod_any, nod_unique, (NOD_T) 0
};
qli_syntax* PARQ_parse(void)
{
/**************************************
*
* P A R Q _ p a r s e
*
**************************************
*
* Functional description
* Parse a single statement or command.
*
**************************************/
sql_flag = else_count = sw_report = 0;
sw_statement = true;
switch (next_keyword()) {
case KW_COMMIT:
return parse_transaction(nod_commit);
case KW_COPY:
return parse_copy();
case KW_EXIT:
return syntax_node(nod_exit, 0);
case KW_EXTRACT:
return parse_extract();
case KW_QUIT:
return syntax_node(nod_quit, 0);
case KW_DELETE:
case KW_DROP:
{
qli_syntax* node = parse_drop();
if (node)
return node;
node = parse_delete();
check_end();
if (!PAR_match(KW_THEN))
return node;
qli_lls* stack = NULL;
ALLQ_push((blk*) node, &stack);
ALLQ_push((blk*) parse_statement(), &stack);
return make_list(stack);
}
case KW_DEFINE:
return parse_define();
case KW_CREATE:
return parse_sql_create();
case KW_ALTER:
return parse_sql_alter();
case KW_EDIT:
return parse_edit();
case KW_FINISH:
return parse_transaction(nod_finish);
case KW_GRANT:
return parse_sql_grant_revoke(nod_sql_grant);
case KW_HELP:
return parse_help();
case KW_PREPARE:
return parse_transaction(nod_prepare);
case KW_READY:
return parse_ready(nod_ready);
case KW_RENAME:
return parse_rename();
case KW_REVOKE:
return parse_sql_grant_revoke(nod_sql_revoke);
case KW_ROLLBACK:
return parse_transaction(nod_rollback);
case KW_SET:
return parse_set();
case KW_SHELL:
return parse_shell();
case KW_SHOW:
return parse_show();
}
return parse_statement();
}
bool PAR_match( KWWORDS keyword)
{
/**************************************
*
* P A R _ m a t c h
*
**************************************
*
* Functional description
* Test the current token for a particular keyword.
* If so, advance the token stream.
*
**************************************/
if (KEYWORD(keyword)) {
PAR_token();
return true;
}
for (const qli_symbol* symbol = QLI_token->tok_symbol; symbol;
symbol = symbol->sym_homonym)
{
if (symbol->sym_type == SYM_keyword &&
symbol->sym_keyword == (int) keyword)
{
PAR_token();
return true;
}
}
return false;
}
void PAR_real(void)
{
/**************************************
*
* P A R _ r e a l
*
**************************************
*
* Functional description
* Get a real (not EOL) token.
*
* If the token is an end of line, get the next token.
* If the next token is a colon, start reading the
* procedure.
*
**************************************/
while ((QLI_token->tok_type == tok_eol) || KEYWORD(KW_continuation))
LEX_token();
if (PAR_match(KW_COLON)) {
DBB database = parse_database();
PRO_invoke(database, QLI_token->tok_string);
}
}
void PAR_real_token(void)
{
/**************************************
*
* P A R _ r e a l _ t o k e n
*
**************************************
*
* Functional description
* Composition of PAR_token followed by PAR_real.
*
**************************************/
PAR_token();
PAR_real();
}
void PAR_token(void)
{
/**************************************
*
* P A R _ t o k e n
*
**************************************
*
* Functional description
* Get the next token.
* If it's a procedure invocation, handle it
* or complain and get rid of the evidence.
*
**************************************/
for (;;) {
LEX_token();
if (!(KEYWORD(KW_continuation)) &&
!(sw_statement && QLI_semi && QLI_token->tok_type == tok_eol))
{
break;
}
}
if (PAR_match(KW_COLON)) {
if (!QLI_databases) {
ERRQ_error_format(159); // Msg159 no databases are ready
ERRQ_pending();
LEX_token();
}
else {
DBB database = parse_database();
PRO_invoke(database, QLI_token->tok_string);
}
}
}
static void check_end(void)
{
/**************************************
*
* c h e c k _ e n d
*
**************************************
*
* Functional description
* Check for end of statement. If it isn't complain bitterly.
*
**************************************/
if (QLI_token->tok_type == tok_eol ||
KEYWORD(KW_SEMI) ||
KEYWORD(KW_THEN) || (else_count && KEYWORD(KW_ELSE)))
{
sw_statement = false;
return;
}
ERRQ_syntax(161); // Msg161 end of statement
}
static void command_end(void)
{
/**************************************
*
* c o m m a n d _ e n d
*
**************************************
*
* Functional description
* Check for end of command. If it isn't complain bitterly.
*
**************************************/
if (QLI_token->tok_type == tok_eol || KEYWORD(KW_SEMI)) {
sw_statement = false;
return;
}
ERRQ_syntax(162); // Msg162 end of command
}
static DBB get_dbb( qli_symbol* symbol)
{
/**************************************
*
* g e t _ d b b
*
**************************************
*
* Functional description
* Find a database block from a symbol
* or its homonyms.
*
**************************************/
for (; symbol; symbol = symbol->sym_homonym)
{
if (symbol->sym_type == SYM_database)
return (DBB) symbol->sym_object;
}
return NULL;
}
static qli_syntax* make_list( qli_lls* stack)
{
/**************************************
*
* m a k e _ l i s t
*
**************************************
*
* Functional description
* Dump a stack of junk into a list node. Best count
* them first.
*
**************************************/
qli_lls* temp = stack;
USHORT count = 0;
while (temp) {
count++;
temp = temp->lls_next;
}
qli_syntax* node = syntax_node(nod_list, count);
qli_syntax** ptr = &node->syn_arg[count];
while (stack)
*--ptr = (qli_syntax*) ALLQ_pop(&stack);
return node;
}
static NAM make_name(void)
{
/**************************************
*
* m a k e _ n a m e
*
**************************************
*
* Functional description
* Generate a unique name for something
* (like a database) that needs one.
*
**************************************/
SSHORT l;
TEXT string[32];
for (SSHORT i = 0; i < 1000; i++) {
sprintf(string, "QLI_%d", i);
if (i < 10)
l = 5;
else
l = (i < 100) ? 6 : 7;
if (!(HSH_lookup(string, l)))
break;
}
NAM name = (NAM) ALLOCDV(type_nam, l);
name->nam_length = l;
TEXT* p = name->nam_string;
const TEXT* q = string;
if (l)
do {
const TEXT c = *q++;
*p++ = UPPER(c);
} while (--l);
return name;
}
static qli_const* make_numeric_constant(const TEXT* string, USHORT length)
{
/**************************************
*
* m a k e _ n u m e r i c _ c o n s t a n t
*
**************************************
*
* Functional description
* Build a constant block for a numeric
* constant. Numeric constants are normally
* stored as long words, but especially large
* ones become text. They ought to become
* double precision, one would think, but they'd
* have to be VAX style double precision which is
* more pain than gain.
*
**************************************/
qli_const* constant;
// If there are a reasonable number of digits, convert to binary
if (length < 9) {
constant = (qli_const*) ALLOCDV(type_con, sizeof(SLONG));
constant->con_desc.dsc_dtype = dtype_long;
constant->con_desc.dsc_length = sizeof(SLONG);
constant->con_desc.dsc_address = constant->con_data;
constant->con_desc.dsc_scale =
MOVQ_decompose(string, length, (SLONG*) constant->con_data);
}
else {
++length;
constant = (qli_const*) ALLOCDV(type_con, length);
constant->con_desc.dsc_dtype = dtype_text;
constant->con_desc.dsc_length = length;
constant->con_desc.dsc_address = constant->con_data;
TEXT* p = (TEXT *) constant->con_desc.dsc_address;
*p++ = '0';
memcpy(p, string, --length);
}
return constant;
}
static TEXT* make_string(const TEXT* address, USHORT length)
{
/**************************************
*
* m a k e _ s t r i n g
*
**************************************
*
* Functional description
* Copy a string into a temporary string block.
*
**************************************/
qli_str* string = (qli_str*) ALLOCDV(type_str, length);
if (length)
memcpy(string->str_data, address, length);
return string->str_data;
}
static qli_syntax* negate( qli_syntax* expr)
{
/**************************************
*
* n e g a t e
*
**************************************
*
* Functional description
* Build negation of expression.
*
**************************************/
qli_syntax* node = syntax_node(nod_not, 1);
node->syn_arg[0] = expr;
return node;
}
static KWWORDS next_keyword(void)
{
/**************************************
*
* n e x t _ k e y w o r d
*
**************************************
*
* Functional description
* Get a real token and return the keyword number.
*
**************************************/
PAR_real();
for (const qli_symbol* symbol = QLI_token->tok_symbol; symbol;
symbol = symbol->sym_homonym)
{
if (symbol->sym_type == SYM_keyword)
return (KWWORDS) symbol->sym_keyword;
}
return KW_none;
}
static qli_syntax* parse_abort(void)
{
/**************************************
*
* p a r s e _ a b o r t
*
**************************************
*
* Functional description
* Parse an ABORT statement.
*
**************************************/
PAR_token();
qli_syntax* node = syntax_node(nod_abort, 1);
if (KEYWORD(KW_SEMI))
node->syn_count = 0;
else
node->syn_arg[0] = parse_value(0, 0);
return node;
}
static qli_syntax* parse_accept(void)
{
/**************************************
*
* p a r s e _ a c c e p t
*
**************************************
*
* Functional description
* Parse form update statement.
*
**************************************/
IBERROR(484); // FORMs not supported
return 0;
}
static qli_syntax* parse_add( USHORT* paren_count, bool* bool_flag)
{
/**************************************
*
* p a r s e _ a d d
*
**************************************
*
* Functional description
* Parse the lowest precedence operatrs, ADD and SUBTRACT.
*
**************************************/
NOD_T operatr;
qli_syntax* node = parse_multiply(paren_count, bool_flag);
while (true) {
if (PAR_match(KW_PLUS))
operatr = nod_add;
else if (PAR_match(KW_MINUS))
operatr = nod_subtract;
else
return node;
qli_syntax* arg = node;
node = syntax_node(operatr, 2);
node->syn_arg[0] = arg;
node->syn_arg[1] = parse_multiply(paren_count, bool_flag);
}
}
static qli_syntax* parse_and( USHORT * paren_count)
{
/**************************************
*
* p a r s e _ a n d
*
**************************************
*
* Functional description
* Parse an AND expression.
*
**************************************/
qli_syntax* expr = parse_not(paren_count);
/*
while (*paren_count && KEYWORD (KW_RIGHT_PAREN))
{
parse_matching_paren();
(*paren_count)--;
}
*/
if (!PAR_match(KW_AND))
return expr;
qli_syntax* node = syntax_node(nod_and, 2);
node->syn_arg[0] = expr;
node->syn_arg[1] = parse_and(paren_count);
return node;
}
static qli_syntax* parse_assignment(void)
{
/**************************************
*
* p a r s e _ a s s i g n m e n t
*
**************************************
*
* Functional description
* Parse an assignment statement (or give an error). The
* assignment statement can be either a simple assignment
* (field = value) or a restructure (relation = rse).
* If the assignment operator is missing,
* generate an "expected statement" error.
*
**************************************/
qli_syntax* field;
qli_syntax* node = syntax_node(nod_assign, s_asn_count);
node->syn_arg[s_asn_to] = parse_field_name(&field);
NAM name = (NAM) field->syn_arg[0];
/* If the next token is an equals sign, the statement really is an
assignment, and we're off the hook. */
if (!PAR_match(KW_EQUALS))
ERRQ_print_error(156, name->nam_string); // Msg156 expected statement, encountered %s
/* See if the "field name" is really a relation reference. If so,
turn the assignment into a restructure. */
qli_rel* relation = NULL;
if (field->syn_count == 1)
relation = resolve_relation(0, name->nam_symbol);
else if (field->syn_count == 2 && name->nam_symbol) {
NAM name2 = (NAM) field->syn_arg[1];
relation = resolve_relation(name->nam_symbol, name2->nam_symbol);
}
if (relation) {
ALLQ_release((FRB) field);
node->syn_type = nod_restructure;
node->syn_arg[s_asn_to] = field =
syntax_node(nod_relation, s_rel_count);
field->syn_arg[s_rel_relation] = (qli_syntax*) relation;
node->syn_arg[s_asn_from] = parse_rse();
}
else
node->syn_arg[s_asn_from] = parse_value(0, 0);
return node;
}
static qli_syntax* parse_boolean( USHORT * paren_count)
{
/**************************************
*
* p a r s e _ b o o l e a n
*
**************************************
*
* Functional description
* Parse a general boolean expression. By precedence, handle an OR
* here.
*
**************************************/
USHORT local_count;
if (!paren_count) {
local_count = 0;
paren_count = &local_count;
}
qli_syntax* expr = parse_and(paren_count);
/*
while (*paren_count && KEYWORD (KW_RIGHT_PAREN))
{
parse_matching_paren();
(*paren_count)--;
}
*/
if (!PAR_match(KW_OR)) {
parse_terminating_parens(paren_count, &local_count);
return expr;
}
qli_syntax* node = syntax_node(nod_or, 2);
node->syn_arg[0] = expr;
node->syn_arg[1] = parse_boolean(paren_count);
parse_terminating_parens(paren_count, &local_count);
return node;
}
static qli_syntax* parse_copy(void)
{
/**************************************
*
* p a r s e _ c o p y
*
**************************************
*
* Functional description
* Parse a copy command, which copies
* one procedure to another.
*
**************************************/
PAR_real_token();
if (PAR_match(KW_PROCEDURE)) {
qli_syntax* node = syntax_node(nod_copy_proc, 2);
node->syn_arg[0] = (qli_syntax*) parse_qualified_procedure();
PAR_match(KW_TO);
node->syn_arg[1] = (qli_syntax*) parse_qualified_procedure();
return node;
}
ERRQ_print_error(157, QLI_token->tok_string); // Msg157 Expected PROCEDURE encountered %s
return NULL;
}
static DBB parse_database(void)
{
/**************************************
*
* p a r s e _ d a t a b a s e
*
**************************************
*
* Functional description
* Pick up a database for a meta-data or
* procedure update. Return NULL if the
* token is not a database name.
*
**************************************/
qli_symbol* db_symbol = QLI_token->tok_symbol;
if (db_symbol && db_symbol->sym_type == SYM_database)
{
DBB database = (DBB) db_symbol->sym_object;
PAR_real_token();
if (!PAR_match(KW_DOT))
ERRQ_syntax(158); // Msg158 period in qualified name
PAR_real();
return database;
}
if (!QLI_databases)
IBERROR(159); // Msg159 no databases are ready
return NULL;
}
static qli_syntax* parse_declare(void)
{
/**************************************
*
* p a r s e _ d e c l a r e
*
**************************************
*
* Functional description
* Parse a variable declaration.
*
**************************************/
PAR_token();
PAR_real();
USHORT dtype = 0, length = 0, scale = 0;
SSHORT sub_type = 0;
SSHORT sub_type_missing = 1;
qli_syntax* field_node = NULL;
qli_symbol* query_name = NULL;
const TEXT* edit_string = NULL;
const TEXT* query_header = NULL;
qli_symbol* name = parse_symbol();
/*if (global_flag) PAR_real();*/
while (!KEYWORD(KW_SEMI) && !KEYWORD(KW_COMMA)) {
PAR_real();
switch (QLI_token->tok_keyword) {
case KW_SHORT:
case KW_LONG:
case KW_FLOAT:
case KW_DOUBLE:
case KW_DATE:
case KW_CHAR:
case KW_VARYING:
if (dtype)
ERRQ_syntax(164); // Msg164 variable definition clause
dtype = parse_dtype(&length, &scale);
break;
case KW_BLOB:
IBERROR(160); // Msg160 blob variables are not supported
break;
case KW_SUB_TYPE:
sub_type = parse_dtype_subtype();
sub_type_missing = 0;
break;
case KW_EDIT_STRING:
PAR_token();
if (QLI_token->tok_type != tok_quoted)
ERRQ_syntax(163); // Msg163 quoted edit string
edit_string =
make_string(QLI_token->tok_string + 1,
QLI_token->tok_length - 2);
PAR_token();
break;
case KW_QUERY_NAME:
PAR_token();
PAR_match(KW_IS);
if (QLI_token->tok_type != tok_ident)
ERRQ_syntax(199); // Msg199 identifier
query_name = parse_symbol();
break;
case KW_QUERY_HEADER:
PAR_token();
query_header = parse_header();
break;
case KW_BASED:
PAR_token();
PAR_match(KW_ON);
field_node = parse_field_name(0);
break;
default:
ERRQ_syntax(164); // Msg164 variable definition clause
break;
}
}
qli_rel* relation = NULL;
if (field_node && field_node->syn_count == 3) {
NAM db_name = (NAM) field_node->syn_arg[0];
NAM rel_name = (NAM) field_node->syn_arg[1];
if (!db_name->nam_symbol)
ERRQ_print_error(165, db_name->nam_string);
// Msg165 %s is not a database
relation = resolve_relation(db_name->nam_symbol, rel_name->nam_symbol);
if (!relation)
{
ERRQ_print_error(166, SafeArg() << rel_name->nam_string <<
db_name->nam_string);
// Msg166 %s is not a relation in database %s
}
}
if (!dtype && !field_node)
ERRQ_syntax(167); // Msg167 variable data type
if (field_node && (dtype || length || scale))
IBERROR(168); // Msg168 no datatype may be specified for a variable based on a field
qli_syntax* node = syntax_node(nod_declare, 2);
// Not global to this unit, misleading name "global..."
qli_fld* global_variable = (qli_fld*) ALLOCDV(type_fld, length);
node->syn_arg[0] = (qli_syntax*) global_variable;
global_variable->fld_name = name;
global_variable->fld_dtype = dtype;
global_variable->fld_scale = scale;
global_variable->fld_sub_type = sub_type;
global_variable->fld_sub_type_missing = sub_type_missing;
global_variable->fld_length = length;
global_variable->fld_edit_string = edit_string;
global_variable->fld_query_name = query_name;
global_variable->fld_query_header = query_header;
global_variable->fld_relation = relation;
node->syn_arg[1] = field_node;
check_end();
return node;
}
static qli_syntax* parse_define(void)
{
/**************************************
*
* p a r s e _ d e f i n e
*
**************************************
*
* Functional description
* Parse a DEFINE command.
* There are, of course, a whole class of define commands.
*
**************************************/
PAR_real_token();
if (PAR_match(KW_PROCEDURE)) {
PAR_real();
qli_syntax* anode = syntax_node(nod_define, 1);
anode->syn_arg[0] = (qli_syntax*) parse_qualified_procedure();
return anode;
}
if (PAR_match(KW_FIELD)) {
PAR_real();
qli_syntax* node = syntax_node(nod_def_field, 2);
node->syn_arg[0] = (qli_syntax*) parse_database();
node->syn_arg[1] = (qli_syntax*) parse_field(true);
return node;
}
if (PAR_match(KW_RELATION))
return parse_def_relation();
if (KEYWORD(KW_DATABASE))
return parse_ready(nod_def_database);
if (PAR_match(KW_INDEX))
return parse_def_index();
ERRQ_syntax(169); // Msg169 object type for DEFINE
return NULL;
}
static qli_syntax* parse_def_index(void)
{
/**************************************
*
* p a r s e _ d e f _ i n d e x
*
**************************************
*
* Functional description
* Parse a DEFINE INDEX command.
*
**************************************/
PAR_real();
qli_syntax* node = syntax_node(nod_def_index, s_dfi_count);
node->syn_arg[s_dfi_name] = (qli_syntax*) parse_symbol();
PAR_real();
PAR_match(KW_FOR);
if (!(node->syn_arg[s_dfi_relation] = (qli_syntax*) parse_qualified_relation()))
ERRQ_syntax(170); // Msg170 relation name
PAR_real();
while (true) {
PAR_real();
if (PAR_match(KW_UNIQUE))
node->syn_flags |= s_dfi_flag_unique;
else if (PAR_match(KW_DUPLICATE))
node->syn_flags &= ~s_dfi_flag_unique;
else if (PAR_match(KW_ACTIVE))
node->syn_flags &= ~s_dfi_flag_inactive;
else if (PAR_match(KW_INACTIVE))
node->syn_flags |= s_dfi_flag_inactive;
else if (PAR_match(KW_DESCENDING))
node->syn_flags |= s_dfi_flag_descending;
else if (PAR_match(KW_ASCENDING))
node->syn_flags &= ~s_dfi_flag_descending;
else
break;
}
qli_lls* stack = NULL;
for (;;) {
ALLQ_push((blk*) parse_name(), &stack);
if (!PAR_match(KW_COMMA))
break;
}
node->syn_arg[s_dfi_fields] = make_list(stack);
command_end();
return node;
}
static qli_syntax* parse_def_relation(void)
{
/**************************************
*
* p a r s e _ d e f _ r e l a t i o n
*
**************************************
*
* Functional description
* Parse a DEFINE RELATION command,
* which include the field definitions
* for a primitive relation definition
* or it may just reference another relation
* whose field definitions we will copy.
*
**************************************/
PAR_real();
qli_syntax* node = syntax_node(nod_def_relation, 2);
qli_rel* relation = (qli_rel*) ALLOCD(type_rel);
node->syn_arg[0] = (qli_syntax*) relation;
relation->rel_database = parse_database();
relation->rel_symbol = parse_symbol();
PAR_real();
if (PAR_match(KW_BASED)) {
PAR_real();
PAR_match(KW_ON);
PAR_real();
PAR_match(KW_RELATION);
PAR_real();
relation = (qli_rel*) ALLOCD(type_rel);
node->syn_arg[1] = (qli_syntax*) relation;
relation->rel_database = parse_database();
relation->rel_symbol = parse_symbol();
}
else {
node->syn_arg[1] = NULL;
qli_fld** ptr = &relation->rel_fields;
for (;;) {
PAR_match(KW_ADD);
PAR_real();
PAR_match(KW_FIELD);
qli_fld* field = parse_field(false);
*ptr = field;
ptr = &field->fld_next;
if (KEYWORD(KW_SEMI))
break;
if (!PAR_match(KW_COMMA))
ERRQ_syntax(171); // Msg171 comma between field definitions
}
}
command_end();
return node;
}
static qli_syntax* parse_delete(void)
{
/**************************************
*
* p a r s e _ d e l e t e
*
**************************************
*
* Functional description
* Parse a SQL DELETE statement.
* (DELETE PROCEDURE is parsed in parse_drop)
*
**************************************/
++sql_flag;
if (!PAR_match(KW_FROM))
ERRQ_syntax(172); // Msg172 FROM
qli_syntax* node = syntax_node(nod_erase, s_era_count);
qli_syntax* rse = syntax_node(nod_rse, (int) s_rse_count + 2);
node->syn_arg[s_era_rse] = rse;
rse->syn_count = 1;
rse->syn_arg[s_rse_count] = parse_sql_relation();
// Pick up boolean, if present
if (PAR_match(KW_WITH))
rse->syn_arg[s_rse_boolean] = parse_boolean(0);
--sql_flag;
return node;
}
static qli_syntax* parse_drop(void)
{
/**************************************
*
* p a r s e _ d r o p
*
**************************************
*
* Functional description
* Parse a DDL DROP/DELETE command. It it isn't one,
* just return NULL.
*
**************************************/
qli_syntax* node;
NOD_T type;
DBB database;
SSHORT l;
const TEXT* q;
PAR_real_token();
if (PAR_match(KW_RELATION) || PAR_match(KW_VIEW) || PAR_match(KW_TABLE)) {
node = syntax_node(nod_del_relation, 1);
if (!(node->syn_arg[0] = (qli_syntax*) parse_qualified_relation()))
ERRQ_syntax(173); // Msg173 relation or view name
return node;
}
switch (QLI_token->tok_keyword) {
case KW_PROCEDURE:
type = nod_delete_proc;
break;
case KW_INDEX:
type = nod_del_index;
break;
case KW_FIELD:
type = nod_del_field;
break;
case KW_DATABASE:
LEX_filename();
if (!(l = QLI_token->tok_length))
ERRQ_error(429); // Msg429 database file name required on DROP DATABASE
q = QLI_token->tok_string;
if (QLI_token->tok_type == tok_quoted) {
l -= 2;
q++;
}
database = (DBB) ALLOCDV(type_dbb, l);
database->dbb_filename_length = l;
memcpy(database->dbb_filename, q, l);
PAR_token();
// parse an optional user name and password if given
for (;;) {
if (PAR_match(KW_USER))
database->dbb_user = parse_literal();
else if (PAR_match(KW_PASSWORD))
database->dbb_password = parse_literal();
else
break;
}
command_end();
node = syntax_node(nod_del_database, 1);
node->syn_arg[0] = (qli_syntax*) database;
return node;
default:
return NULL;
}
PAR_real_token();
node = syntax_node(type, 2);
if (type == nod_delete_proc)
node->syn_arg[0] = (qli_syntax*) parse_qualified_procedure();
else {
node->syn_arg[0] = (qli_syntax*) parse_database();
node->syn_arg[1] = (qli_syntax*) parse_name();
}
return node;
}
static int parse_dtype( USHORT * length, USHORT * scale)
{
/**************************************
*
* p a r s e _ d t y p e
*
**************************************
*
* Functional description
* Parse a datatype clause.
*
**************************************/
USHORT dtype;
const KWWORDS keyword = QLI_token->tok_keyword;
PAR_token();
*scale = 0;
switch (keyword) {
case KW_SHORT:
*length = sizeof(SSHORT);
dtype = dtype_short;
break;
case KW_BIGINT:
*length = sizeof(SINT64);
dtype = dtype_int64;
break;
case KW_LONG:
*length = sizeof(SLONG);
dtype = dtype_long;
break;
case KW_FLOAT:
*length = sizeof(float);
return dtype_real;
case KW_DOUBLE:
*length = sizeof(double);
return dtype_double;
case KW_DATE:
*length = sizeof(gds_quad);
return dtype_timestamp;
case KW_CHAR:
dtype = dtype_text;
break;
case KW_VARYING:
dtype = dtype_varying;
break;
case KW_BLOB:
*length = sizeof(gds_quad);
return dtype_blob;
}
if (dtype == dtype_short || dtype == dtype_long || dtype == dtype_int64 )
{
if (PAR_match(KW_SCALE)) {
const bool m = (PAR_match(KW_MINUS)) ? true : false;
*scale = parse_ordinal();
if (m)
*scale = -(*scale);
}
}
else if (dtype == dtype_text || dtype == dtype_varying)
{
if (!PAR_match(KW_L_BRCKET) && !PAR_match(KW_LT))
ERRQ_syntax(174); /* Msg174 "[" */
USHORT l = parse_ordinal();
if (dtype == dtype_varying)
l += sizeof(SSHORT);
*length = l;
if (!PAR_match(KW_R_BRCKET) && !PAR_match(KW_GT))
ERRQ_syntax(175); /* Msg175 "]" */
}
return dtype;
}
static int parse_dtype_subtype(void)
{
/**************************************
*
* p a r s e _ d t y p e _ s u b t y p e
*
**************************************
*
* Functional description
* Parse a sub-type definition, which can be any of
* SUB_TYPE {IS} [TEXT | FIXED | <n>]
*
* Returns the numeric subtype value,
*
**************************************/
// grab KW_SUB_TYPE
PAR_token();
PAR_match(KW_IS);
if (PAR_match(KW_TEXT) || PAR_match(KW_FIXED))
return 1;
const int sign = (PAR_match(KW_MINUS)) ? -1 : 1;
return (sign * parse_ordinal());
}
static qli_syntax* parse_edit(void)
{
/**************************************
*
* p a r s e _ e d i t
*
**************************************
*
* Functional description
* Parse an edit statement which can
* be any of EDIT <procedure_name>
* EDIT <n>
* EDIT <*>
* EDIT
*
*
**************************************/
LEX_token();
/*
* edit previous statements. The top of the statment list
* is this edit command, which we conveniently ignore.
*/
if (KEYWORD(KW_SEMI) ||
(QLI_token->tok_type == tok_number) || (KEYWORD(KW_ASTERISK)))
{
qli_lls* statement_list = LEX_statement_list();
if (!statement_list)
IBERROR(176); // Msg176 No statements issued yet
if (PAR_match(KW_ASTERISK))
LEX_edit(0, (IPTR) statement_list->lls_object);
else {
int l = 0; // initialize, will catch changes in logic here.
if (KEYWORD(KW_SEMI))
l = 1;
else if (QLI_token->tok_type == tok_number) // redundant for now
l = parse_ordinal();
qli_lls* start = statement_list;
qli_lls* stop = start;
while (l && start->lls_next)
{
--l;
start = start->lls_next;
}
command_end();
LEX_edit((IPTR) start->lls_object, (IPTR) stop->lls_object);
}
}
else {
const NOD_T type = nod_edit_proc;
qli_syntax* node = syntax_node(type, 2);
node->syn_arg[0] = (qli_syntax*) parse_qualified_procedure();
command_end();
return node;
}
return NULL;
}
static TEXT* parse_edit_string(void)
{
/**************************************
*
* p a r s e _ e d i t _ s t r i n g
*
**************************************
*
* Functional description
* Look for and parse a clause of the form:
* USING <edit_string>
*
**************************************/
if (!KEYWORD(KW_USING))
return NULL;
LEX_edit_string();
return parse_string();
}
static qli_syntax* parse_erase(void)
{
/**************************************
*
* p a r s e _ e r a s e
*
**************************************
*
* Functional description
* Parse an ERASE statement. Erase can be any of the
* following:
*
* ERASE [ALL] [OF <rse>]
*
**************************************/
PAR_token();
PAR_match(KW_ALL);
PAR_match(KW_OF);
qli_syntax* node = syntax_node(nod_erase, s_era_count);
if (PAR_match(KW_ALL) || potential_rse()) {
PAR_match(KW_OF);
node->syn_arg[s_era_rse] = parse_rse();
}
return node;
}
static qli_syntax* parse_extract(void)
{
/**************************************
*
* p a r s e _ e x t r a c t
*
**************************************
*
* Functional description
* Parse a procedure extract statement. Syntax is:
*
* EXTRACT [ON <file>] proc [, ...] [ON <file> ]
*
**************************************/
PAR_real_token();
qli_syntax* node = syntax_node(nod_extract, 2);
node->syn_arg[1] = parse_output();
if (!PAR_match(KW_ALL)) {
qli_lls* stack = NULL;
for (;;) {
ALLQ_push((blk*) parse_qualified_procedure(), &stack);
if (!PAR_match(KW_COMMA))
break;
}
node->syn_arg[0] = make_list(stack);
}
if (!node->syn_arg[1] && !(node->syn_arg[1] = parse_output()))
ERRQ_syntax(177); // Msg177 "ON or TO"
return node;
}
static qli_fld* parse_field( bool global_flag)
{
/**************************************
*
* p a r s e _ f i e l d
*
**************************************
*
* Functional description
* Parse a field description.
*
**************************************/
PAR_real();
USHORT dtype = 0, length = 0, scale = 0;
SSHORT sub_type = 0;
SSHORT sub_type_missing = 1;
qli_symbol* query_name = NULL;
qli_symbol* based_on = NULL;
const TEXT* edit_string = NULL;
const TEXT* query_header = NULL;
qli_symbol* name = parse_symbol();
if (global_flag)
PAR_real();
while (!KEYWORD(KW_SEMI) && !KEYWORD(KW_COMMA)) {
PAR_real();
switch (QLI_token->tok_keyword) {
case KW_SHORT:
case KW_LONG:
case KW_FLOAT:
case KW_DOUBLE:
case KW_DATE:
case KW_CHAR:
case KW_VARYING:
case KW_BLOB:
if (dtype)
ERRQ_syntax(179); // Msg179 field definition clause
dtype = parse_dtype(&length, &scale);
break;
case KW_SUB_TYPE:
sub_type = parse_dtype_subtype();
sub_type_missing = 0;
break;
case KW_EDIT_STRING:
PAR_token();
if (QLI_token->tok_type != tok_quoted)
ERRQ_syntax(178); // Msg178 quoted edit string
edit_string =
make_string(QLI_token->tok_string + 1,
QLI_token->tok_length - 2);
PAR_token();
break;
case KW_QUERY_NAME:
PAR_token();
PAR_match(KW_IS);
if (QLI_token->tok_type != tok_ident)
ERRQ_syntax(199); // Msg199 identifier
query_name = parse_symbol();
break;
case KW_BASED:
PAR_token();
PAR_match(KW_ON);
based_on = parse_symbol();
break;
default:
ERRQ_syntax(179); // Msg179 field definition clause
break;
}
}
qli_fld* field = (qli_fld*) ALLOCDV(type_fld, length);
field->fld_name = name;
field->fld_dtype = dtype;
field->fld_scale = scale;
field->fld_sub_type = sub_type;
field->fld_sub_type_missing = sub_type_missing;
field->fld_length = length;
field->fld_edit_string = edit_string;
field->fld_query_name = query_name;
field->fld_query_header = query_header;
if (!global_flag)
field->fld_based = based_on;
else if (based_on)
IBERROR(180); // Msg180 global fields may not be based on other fields
return field;
}
static qli_syntax* parse_field_name( qli_syntax** fld_ptr)
{
/**************************************
*
* p a r s e _ f i e l d _ n a m e
*
**************************************
*
* Functional description
* Parse a qualified field name, or
* qualified * expression.
*
**************************************/
qli_lls* stack = NULL;
while (true) {
if (PAR_match(KW_ASTERISK)) {
if (!stack)
ERRQ_syntax(181); // Msg181 field name or asterisk expression
qli_syntax* afield = make_list(stack);
afield->syn_type = nod_star;
return afield;
}
ALLQ_push((blk*) parse_name(), &stack);
if (!PAR_match(KW_DOT))
break;
}
qli_syntax* field = make_list(stack);
field->syn_type = nod_field;
if (fld_ptr)
*fld_ptr = field;
if (!(PAR_match(KW_L_BRCKET)))
return field;
// Parse an array reference
stack = NULL;
for (;;) {
ALLQ_push((blk*) parse_value(0, 0), &stack);
if (PAR_match(KW_R_BRCKET))
break;
if (!PAR_match(KW_COMMA))
ERRQ_syntax(183); // Msg183 comma
}
qli_syntax* node = syntax_node(nod_index, s_idx_count);
node->syn_arg[s_idx_field] = field;
node->syn_arg[s_idx_subs] = make_list(stack);
return node;
}
static qli_syntax* parse_for(void)
{
/**************************************
*
* p a r s e _ f o r
*
**************************************
*
* Functional description
* Parse a FOR statement.
*
**************************************/
PAR_token();
qli_syntax* node = syntax_node(nod_for, s_for_count);
node->syn_arg[s_for_rse] = parse_rse();
node->syn_arg[s_for_statement] = parse_statement();
return node;
}
static qli_syntax* parse_from( USHORT * paren_count, bool* bool_flag)
{
/**************************************
*
* p a r s e _ f r o m
*
**************************************
*
* Functional description
* Parse either an explicit or implicit FIRST ... FROM statement.
*
**************************************/
qli_syntax* value;
PAR_real();
if (PAR_match(KW_FIRST)) {
value = parse_primitive_value(0, 0);
PAR_real();
if (!PAR_match(KW_FROM))
ERRQ_syntax(182); // Msg182 FROM rse clause
}
else {
value = parse_primitive_value(paren_count, bool_flag);
if (sql_flag || !PAR_match(KW_FROM))
return value;
}
qli_syntax* node = syntax_node(nod_from, s_stt_count);
node->syn_arg[s_stt_value] = value;
node->syn_arg[s_stt_rse] = parse_rse();
if (PAR_match(KW_ELSE))
node->syn_arg[s_stt_default] = parse_value(0, 0);
return node;
}
static qli_syntax* parse_function(void)
{
/**************************************
*
* p a r s e _ f u n c t i o n
*
**************************************
*
* Functional description
* Parse a function reference.
*
**************************************/
function_count++;
qli_syntax* node = syntax_node(nod_function, s_fun_count);
node->syn_arg[s_fun_function] = (qli_syntax*) QLI_token->tok_symbol;
node->syn_count = 1;
PAR_token();
qli_lls* stack = NULL;
if (PAR_match(KW_LEFT_PAREN))
for (;;) {
ALLQ_push((blk*) parse_value(0, 0), &stack);
if (PAR_match(KW_RIGHT_PAREN))
break;
if (!PAR_match(KW_COMMA))
ERRQ_syntax(183); // Msg183 comma
}
node->syn_arg[s_fun_args] = make_list(stack);
function_count--;
return node;
}
static TEXT* parse_header(void)
{
/**************************************
*
* p a r s e _ h e a d e r
*
**************************************
*
* Functional description
* Parse and store headers of the form:
* "quoted_string" [/ "more_string"]...
* or even the non-header -
*
**************************************/
TEXT header[1024];
TEXT* p = header;
const TEXT* end = p + sizeof(header);
while (true) {
PAR_real();
if ((QLI_token->tok_keyword != KW_MINUS) &&
(QLI_token->tok_type != tok_quoted))
{
ERRQ_syntax(184); // Msg184 quoted header segment
}
const TEXT* q = QLI_token->tok_string;
while (*q && p < end)
*p++ = *q++;
if (p == end && *q)
ERRQ_syntax(184); // Msg184 quoted header segment
PAR_real_token();
if (!PAR_match(KW_SLASH))
break;
}
return make_string(header, p - header);
}
static qli_syntax* parse_help(void)
{
/**************************************
*
* p a r s e _ h e l p
*
**************************************
*
* Functional description
* Parse HELP statement. Unreasonable, but the masses
* must be appeased. Bread, circuses, help.
*
**************************************/
qli_lls* stack = NULL;
PAR_token();
while (!KEYWORD(KW_SEMI)) {
ALLQ_push((blk*) parse_name(), &stack);
PAR_match(KW_COMMA);
}
qli_syntax* node = make_list(stack);
node->syn_type = nod_help;
command_end();
return node;
}
static qli_syntax* parse_if(void)
{
/**************************************
*
* p a r s e _ i f
*
**************************************
*
* Functional description
* Parse an IF THEN ELSE statement.
*
**************************************/
PAR_token();
qli_syntax* node = syntax_node(nod_if, s_if_count);
node->syn_arg[s_if_boolean] = parse_boolean(0);
PAR_real();
PAR_match(KW_THEN);
++else_count;
node->syn_arg[s_if_true] = parse_statement();
--else_count;
if (PAR_match(KW_ELSE))
node->syn_arg[s_if_false] = parse_statement();
return node;
}
static qli_syntax* parse_in( qli_syntax* value, NOD_T operatr, bool all_flag)
{
/**************************************
*
* p a r s e _ i n
*
**************************************
*
* Functional description
* Parse a SQL "IN" clause. This can have two forms:
*
* value IN (exp1, exp2...)
*
* value IN (column <select expression>)
*
**************************************/
PAR_real();
if (!PAR_match(KW_LEFT_PAREN))
ERRQ_syntax(185); // Msg185 left parenthesis
// Time to chose between two forms of the expression
if (!PAR_match(KW_SELECT)) {
qli_syntax* node1 = syntax_node(operatr, 2);
node1->syn_arg[0] = value;
node1->syn_arg[1] = parse_primitive_value(0, 0);
while (PAR_match(KW_COMMA)) {
qli_syntax* node2 = node1;
node1 = syntax_node(nod_or, 2);
node1->syn_arg[0] = node2;
node1->syn_arg[1] = node2 = syntax_node(nod_eql, 2);
node2->syn_arg[0] = value;
node2->syn_arg[1] = parse_value(0, 0);
}
parse_matching_paren();
return node1;
}
qli_syntax* value2 = parse_value(0, 0);
// We have the "hard" -- an implicit ANY
qli_syntax* rse = parse_sql_rse();
parse_matching_paren();
rse->syn_arg[s_rse_outer] = value;
rse->syn_arg[s_rse_inner] = value2;
rse->syn_arg[s_rse_op] = INT_CAST operatr;
rse->syn_arg[s_rse_all_flag] = INT_CAST (all_flag ? TRUE: FALSE);
// Finally, construct an ANY node
qli_syntax* node = syntax_node(nod_any, 1);
node->syn_arg[0] = rse;
return (all_flag) ? negate(node) : node;
}
static qli_syntax* parse_insert(void)
{
/**************************************
*
* p a r s e _ i n s e r t
*
**************************************
*
* Functional description
* Parse a STORE statement.
*
**************************************/
++sql_flag;
PAR_real_token();
PAR_match(KW_INTO);
qli_syntax* node = syntax_node(nod_store, s_sto_count);
// Pick up relation name for insert
node->syn_arg[s_sto_relation] = parse_sql_relation();
// Pick up field list, provided one is present
PAR_real();
qli_lls* fields = NULL;
if (PAR_match(KW_LEFT_PAREN))
while (true) {
ALLQ_push((blk*) parse_field_name(0), &fields);
if (PAR_match(KW_RIGHT_PAREN))
break;
if (!PAR_match(KW_COMMA))
ERRQ_syntax(186); // Msg186 comma or terminating right parenthesis
}
// Pick up value list or SELECT statement
PAR_real();
bool select_flag;
if (PAR_match(KW_VALUES)) {
select_flag = false;
if (!PAR_match(KW_LEFT_PAREN))
ERRQ_syntax(187); // Msg187 left parenthesis
}
else if (PAR_match(KW_SELECT))
select_flag = true;
else
ERRQ_syntax(188); // Msg188 VALUES list or SELECT clause
qli_lls* values = NULL;
qli_lls* distinct = NULL;
while (true) {
if (distinct || PAR_match(KW_DISTINCT)) {
ALLQ_push((blk*) parse_value(0, 0), &distinct);
ALLQ_push(distinct->lls_object, &values);
ALLQ_push(0, &distinct);
}
else
ALLQ_push((blk*) parse_value(0, 0), &values);
if (!PAR_match(KW_COMMA))
break;
}
if (select_flag)
node->syn_arg[s_sto_rse] = parse_sql_rse();
else
parse_matching_paren();
if (distinct)
node->syn_arg[s_sto_rse]->syn_arg[s_rse_reduced] =
make_list(distinct);
node->syn_arg[s_sto_fields] = (qli_syntax*) fields;
node->syn_arg[s_sto_values] = (qli_syntax*) values;
--sql_flag;
return node;
}
static NOD_T parse_join_type(void)
{
/**************************************
*
* p a r s e _ j o i n _ t y p e
*
**************************************
*
* Functional description
* Parse a join type.
*
**************************************/
NOD_T operatr;
if (PAR_match(KW_INNER))
operatr = nod_join_inner;
else if (PAR_match(KW_LEFT))
operatr = nod_join_left;
else if (PAR_match(KW_RIGHT))
operatr = nod_join_right;
else if (PAR_match(KW_FULL))
operatr = nod_join_full;
else if (PAR_match(KW_JOIN))
return nod_join_inner;
else
return (NOD_T) 0;
if (operatr != nod_join_inner)
PAR_match(KW_OUTER);
if (!PAR_match(KW_JOIN))
ERRQ_syntax(489); // Msg489 JOIN
return operatr;
}
static qli_syntax* parse_list_fields(void)
{
/**************************************
*
* p a r s e _ l i s t _ f i e l d s
*
**************************************
*
* Functional description
* Parse a LIST statement. LIST is like PRINT, but does vertical
* formatting.
*
**************************************/
qli_syntax* node = syntax_node(nod_list_fields, s_prt_count);
PAR_token();
if (!PAR_match(KW_ALL) && PAR_match(KW_DISTINCT))
node->syn_arg[s_prt_distinct] = INT_CAST TRUE;
if (node->syn_arg[s_prt_output] = parse_output())
return node;
if (test_end())
return node;
/* If there is a potential record selection expression, there obviously
can't be a print list. Get the rse. Otherwise, pick up the print
list. */
if (potential_rse())
node->syn_arg[s_prt_rse] = parse_rse();
else {
if (!test_end()) {
qli_lls* stack = NULL;
for (;;) {
qli_syntax* item = syntax_node(nod_print_item, s_itm_count);
item->syn_arg[s_itm_value] = parse_value(0, 0);
item->syn_arg[s_itm_edit_string] = (qli_syntax*) parse_edit_string();
ALLQ_push((blk*) item, &stack);
if (!PAR_match(KW_COMMA) && !PAR_match(KW_AND))
break;
PAR_real();
if (PAR_match(KW_AND))
PAR_real();
}
node->syn_arg[s_prt_list] = make_list(stack);
}
if (PAR_match(KW_OF))
node->syn_arg[s_prt_rse] = parse_rse();
}
node->syn_arg[s_prt_output] = parse_output();
return node;
}
static qli_const* parse_literal(void)
{
/**************************************
*
* p a r s e _ l i t e r a l
*
**************************************
*
* Functional description
* Parse a literal expression.
*
**************************************/
qli_const* constant;
PAR_real();
const UCHAR* q = (UCHAR *) QLI_token->tok_string;
USHORT l = QLI_token->tok_length;
if (QLI_token->tok_type == tok_quoted) {
q++;
l -= 2;
constant = (qli_const*) ALLOCDV(type_con, l);
constant->con_desc.dsc_dtype = dtype_text;
UCHAR* p = constant->con_desc.dsc_address = constant->con_data;
if (constant->con_desc.dsc_length = l)
memcpy(p, q, l);
}
else if (QLI_token->tok_type == tok_number)
constant = make_numeric_constant(QLI_token->tok_string,
QLI_token->tok_length);
else
ERRQ_syntax(190); // Msg190 value expression
PAR_token();
return constant;
}
static qli_syntax* parse_matches(void)
{
/**************************************
*
* p a r s e _ m a t c h e s
*
**************************************
*
* Functional description
* Parse a matching expressing, including
* the preset matching language template.
*
**************************************/
PAR_token();
qli_syntax* node = syntax_node(nod_sleuth, 3);
node->syn_arg[1] = parse_value(0, 0);
if (PAR_match(KW_USING))
node->syn_arg[2] = parse_value(0, 0);
else if (QLI_matching_language) {
qli_syntax* language = syntax_node(nod_constant, 1);
node->syn_arg[2] = language;
language->syn_arg[0] = (qli_syntax*) QLI_matching_language;
}
else {
node->syn_type = nod_matches;
node->syn_count = 2;
}
return node;
}
static void parse_matching_paren(void)
{
/**************************************
*
* p a r s e _ m a t c h i n g _ p a r e n
*
**************************************
*
* Functional description
* Check for a trailing (right) parenthesis. Complain if
* its not there.
*
**************************************/
PAR_real();
if (PAR_match(KW_RIGHT_PAREN))
return;
ERRQ_syntax(191); // Msg191 right parenthesis
}
static qli_syntax* parse_modify(void)
{
/**************************************
*
* p a r s e _ m o d i f y
*
**************************************
*
* Functional description
* Parse a MODIFY statement. Modify can be any of the
* following:
*
* MODIFY [ALL] [<field> [, <field>]...] [OF <rse> ]
* MODIFY [ALL] USING <statement> [OF <rse>]
*
**************************************/
PAR_token();
// If this is a meta-data change, handle it elsewhere
if (PAR_match(KW_INDEX))
return parse_modify_index();
if (PAR_match(KW_FIELD)) {
qli_syntax* anode = syntax_node(nod_mod_field, 1);
anode->syn_arg[0] = (qli_syntax*) parse_database();
anode->syn_arg[1] = (qli_syntax*) parse_field(true);
return anode;
}
if (PAR_match(KW_RELATION))
return parse_modify_relation();
// Not a meta-data modification, just a simple data modify
PAR_match(KW_ALL);
qli_syntax* node = syntax_node(nod_modify, s_mod_count);
if (PAR_match(KW_USING))
node->syn_arg[s_mod_statement] = parse_statement();
else if (!KEYWORD(KW_SEMI)) {
qli_lls* stack = NULL;
while (true) {
ALLQ_push((blk*) parse_field_name(0), &stack);
if (!PAR_match(KW_COMMA))
break;
}
node->syn_arg[s_mod_list] = make_list(stack);
}
if (PAR_match(KW_OF))
node->syn_arg[s_mod_rse] = parse_rse();
return node;
}
static qli_syntax* parse_modify_index(void)
{
/**************************************
*
* p a r s e _ m o d i f y _ i n d e x
*
**************************************
*
* Functional description
* Parse a MODIFY INDEX statement.
*
**************************************/
qli_syntax* node = syntax_node(nod_mod_index, s_mfi_count);
node->syn_arg[s_mfi_database] = (qli_syntax*) parse_database();
node->syn_arg[s_mfi_name] = (qli_syntax*) parse_name();
PAR_real();
while (true) {
if (PAR_match(KW_UNIQUE))
node->syn_flags |= (s_dfi_flag_selectivity | s_dfi_flag_unique);
else if (PAR_match(KW_DUPLICATE)) {
node->syn_flags |= s_dfi_flag_selectivity;
node->syn_flags &= ~s_dfi_flag_unique;
}
else if (PAR_match(KW_INACTIVE))
node->syn_flags |= (s_dfi_flag_activity | s_dfi_flag_inactive);
else if (PAR_match(KW_ACTIVE)) {
node->syn_flags |= s_dfi_flag_activity;
node->syn_flags &= ~s_dfi_flag_inactive;
}
else if (PAR_match(KW_DESCENDING))
node->syn_flags |= (s_dfi_flag_order | s_dfi_flag_descending);
else if (PAR_match(KW_ASCENDING)) {
node->syn_flags |= s_dfi_flag_order;
node->syn_flags &= ~s_dfi_flag_inactive;
}
else if (PAR_match(KW_STATISTICS))
node->syn_flags |= s_dfi_flag_statistics;
else
break;
}
if (!node->syn_flags)
ERRQ_syntax(195); // Msg195 index state option
command_end();
return node;
}
static qli_syntax* parse_modify_relation(void)
{
/**************************************
*
* p a r s e _ m o d i f y _ r e l a t i o n
*
**************************************
*
* Functional description
* Parse a MODIFY RELATION statement.
*
**************************************/
qli_syntax* node = syntax_node(nod_mod_relation, 2);
qli_rel* relation = parse_qualified_relation();
node->syn_arg[0] = (qli_syntax*) relation;
if (!relation)
ERRQ_syntax(196); // Msg196 relation name
for (;;) {
PAR_real();
qli_fld* field;
if (PAR_match(KW_ADD)) {
PAR_real();
PAR_match(KW_FIELD);
field = parse_field(false);
}
else if (PAR_match(KW_MODIFY)) {
PAR_real();
PAR_match(KW_FIELD);
field = parse_field(false);
field->fld_flags |= FLD_modify;
}
else if (PAR_match(KW_DROP)) {
PAR_real();
PAR_match(KW_FIELD);
field = parse_field(false);
field->fld_flags |= FLD_drop;
}
else
ERRQ_syntax(197); // Msg197 ADD, MODIFY, or DROP
field->fld_next = (qli_fld*) node->syn_arg[1];
node->syn_arg[1] = (qli_syntax*) field;
if (KEYWORD(KW_SEMI))
break;
if (!PAR_match(KW_COMMA))
ERRQ_syntax(198); // Msg198 comma between field definitions
}
command_end();
return node;
}
static qli_syntax* parse_multiply( USHORT * paren_count, bool* bool_flag)
{
/**************************************
*
* p a r s e _ m u l t i p l y
*
**************************************
*
* Functional description
* Parse the operatrs * and /.
*
**************************************/
NOD_T operatr;
qli_syntax* node = parse_from(paren_count, bool_flag);
while (true) {
if (PAR_match(KW_ASTERISK))
operatr = nod_multiply;
else if (PAR_match(KW_SLASH))
operatr = nod_divide;
else
return node;
qli_syntax* arg = node;
node = syntax_node(operatr, 2);
node->syn_arg[0] = arg;
node->syn_arg[1] = parse_from(paren_count, bool_flag);
}
}
static NAM parse_name(void)
{
/**************************************
*
* p a r s e _ n a m e
*
**************************************
*
* Functional description
* Turn current token into a name and get the next token.
*
**************************************/
PAR_real();
const bool isQuoted = QLI_token->tok_type == tok_quoted && sql_flag &&
QLI_token->tok_string[0] == '"';
if (QLI_token->tok_type != tok_ident && !isQuoted)
ERRQ_syntax(199); // Msg199 identifier
SSHORT l = QLI_token->tok_length - 2 * int(isQuoted);
NAM name = (NAM) ALLOCDV(type_nam, l);
name->nam_length = l;
name->nam_symbol = QLI_token->tok_symbol;
const TEXT* q = QLI_token->tok_string + int(isQuoted);
TEXT* p = name->nam_string;
if (isQuoted)
memcpy(p, q, l);
else if (l)
do {
const TEXT c = *q++;
*p++ = UPPER(c);
} while (--l);
PAR_token();
return name;
}
static qli_syntax* parse_not( USHORT * paren_count)
{
/**************************************
*
* p a r s e _ n o t
*
**************************************
*
* Functional description
* Parse a prefix NOT expression.
*
**************************************/
PAR_real();
if (!PAR_match(KW_NOT))
return parse_relational(paren_count);
return negate(parse_not(paren_count));
}
static int parse_ordinal(void)
{
/**************************************
*
* p a r s e _ o r d i n a l
*
**************************************
*
* Functional description
* Pick up a simple number as a number. This is
* used for SKIP [n], SPACE [n], COL n, and SQL
* positions.
*
**************************************/
PAR_real();
if (QLI_token->tok_type != tok_number)
ERRQ_syntax(200); // Msg200 positive number
const int n = atoi(QLI_token->tok_string);
if (n < 0)
ERRQ_syntax(200); // Msg200 positive number
PAR_token();
return n;
}
static qli_syntax* parse_output(void)
{
/**************************************
*
* p a r s e _ o u t p u t
*
**************************************
*
* Functional description
* Parse an output clause the the absence thereof.
*
**************************************/
USHORT flag;
if (PAR_match(KW_ON))
flag = FALSE;
else if (PAR_match(KW_TO))
flag = TRUE;
else
return NULL;
qli_syntax* node = syntax_node(nod_output, s_out_count);
node->syn_arg[s_out_file] = parse_value(0, 0);
node->syn_arg[s_out_pipe] = INT_CAST flag;
return node;
}
static qli_syntax* parse_primitive_value( USHORT* paren_count, bool* bool_flag)
{
/**************************************
*
* p a r s e _ p r i m i t i v e _ v a l u e
*
**************************************
*
* Functional description
* Pick up a value expression. This may be either a field reference
* or a constant value.
*
**************************************/
USHORT local_count;
if (!paren_count) {
local_count = 0;
paren_count = &local_count;
}
PAR_real();
qli_syntax* node;
switch (next_keyword()) {
case KW_LEFT_PAREN:
PAR_token();
(*paren_count)++;
if (bool_flag && *bool_flag)
node = parse_boolean(paren_count);
else
node = parse_value(paren_count, bool_flag);
/*
if (*paren_count && KEYWORD (KW_RIGHT_PAREN))
*/
{
parse_matching_paren();
(*paren_count)--;
}
break;
case KW_PLUS:
PAR_token();
return parse_primitive_value(paren_count, 0);
case KW_MINUS:
{
PAR_token();
qli_syntax* sub = parse_primitive_value(paren_count, 0);
if (sub->syn_type == nod_constant) {
qli_const* constant = (qli_const*) sub->syn_arg[0];
UCHAR* p = constant->con_desc.dsc_address;
switch (constant->con_desc.dsc_dtype) {
case dtype_long:
*(SLONG *) p = -*(SLONG *) p;
return sub;
case dtype_text:
*p = '-';
return sub;
}
}
node = syntax_node(nod_negate, 1);
node->syn_arg[0] = sub;
break;
}
case KW_ASTERISK:
node = parse_prompt();
break;
case KW_EDIT:
PAR_token();
node = syntax_node(nod_edit_blob, 1);
if (!KEYWORD(KW_SEMI))
node->syn_arg[0] = parse_value(0, 0);
break;
case KW_FORMAT:
PAR_token();
node = syntax_node(nod_format, s_fmt_count);
node->syn_arg[s_fmt_value] = parse_value(0, 0);
node->syn_arg[s_fmt_edit] = (qli_syntax*) parse_edit_string();
break;
case KW_NULL:
PAR_token();
node = syntax_node(nod_null, 0);
break;
case KW_USER_NAME:
PAR_token();
node = syntax_node(nod_user_name, 0);
break;
case KW_COUNT:
case KW_MAX:
case KW_MIN:
case KW_AVERAGE:
case KW_TOTAL:
node = parse_statistical();
break;
case KW_RUNNING:
if (function_count > 0)
IBERROR(487); // Msg487 Invalid argument for UDF
PAR_real_token();
node = syntax_node(nod_running_total, s_stt_count);
if (PAR_match(KW_COUNT))
node->syn_type = nod_running_count;
else {
PAR_match(KW_TOTAL);
node->syn_arg[s_stt_value] = parse_value(0, 0);
}
break;
case KW_SELECT:
node = parse_sql_subquery();
break;
default:
{
const qli_symbol* symbol = QLI_token->tok_symbol;
if (symbol && symbol->sym_type == SYM_function)
{
node = parse_function();
break;
}
if (QLI_token->tok_type == tok_ident
|| QLI_token->tok_type == tok_quoted && sql_flag &&
QLI_token->tok_string[0] == '"')
{
node = parse_field_name(0);
break;
}
node = syntax_node(nod_constant, 1);
node->syn_arg[0] = (qli_syntax*) parse_literal();
}
}
return node;
}
static qli_syntax* parse_print_list(void)
{
/**************************************
*
* p a r s e _ p r i n t _ l i s t
*
**************************************
*
* Functional description
* Pick up a print item. The syntax of a print item is:
*
* <value> [ '[ <query_header> '] ] [USING <edit_string> ]
*
**************************************/
qli_syntax* node;
qli_lls* stack = NULL;
while (true) {
NOD_T op;
if (PAR_match(KW_SKIP))
op = nod_skip;
else if (PAR_match(KW_SPACE))
op = nod_space;
else if (PAR_match(KW_TAB))
op = nod_tab;
else if (PAR_match(KW_COLUMN))
op = nod_column;
else if (PAR_match(KW_NEW_PAGE))
op = nod_new_page;
else if (PAR_match(KW_REPORT_HEADER))
op = nod_report_header;
else if (PAR_match(KW_COLUMN_HEADER))
op = nod_column_header;
else {
op = nod_print_item;
node = syntax_node(nod_print_item, s_itm_count);
node->syn_arg[s_itm_value] = parse_value(0, 0);
if (PAR_match(KW_LEFT_PAREN)) {
if (PAR_match(KW_MINUS))
node->syn_arg[s_itm_header] = (qli_syntax*) "-";
else
node->syn_arg[s_itm_header] = (qli_syntax*) parse_header();
parse_matching_paren();
}
node->syn_arg[s_itm_edit_string] = (qli_syntax*) parse_edit_string();
}
if (op != nod_print_item) {
node = syntax_node(op, 1);
node->syn_count = 0;
node->syn_arg[0] = INT_CAST 1;
if (op == nod_column || QLI_token->tok_type == tok_number)
node->syn_arg[0] = INT_CAST parse_ordinal();
if ((op == nod_skip) && ((IPTR) node->syn_arg[0] < 1))
ERRQ_syntax(478); /* Msg478 number > 0 */
}
ALLQ_push((blk*) node, &stack);
if (!PAR_match(KW_COMMA) && !PAR_match(KW_AND))
break;
PAR_real();
if (PAR_match(KW_AND))
PAR_real();
}
node = make_list(stack);
return node;
}
static qli_syntax* parse_print(void)
{
/**************************************
*
* p a r s e _ p r i n t
*
**************************************
*
* Functional description
* Parse the PRINT statement. This is the richest and most general
* Datatrieve statement. Hence this may get a bit long. The syntax is:
*
* [<item> [, <item>]] OF <rse>
* PRINT [ ] [ON <file>]
* <rse>
*
**************************************/
qli_syntax* node = syntax_node(nod_print, s_prt_count);
PAR_token();
if (!PAR_match(KW_ALL) && PAR_match(KW_DISTINCT))
node->syn_arg[s_prt_distinct] = INT_CAST TRUE;
if (node->syn_arg[s_prt_output] = parse_output())
return node;
if (test_end())
return node;
/* If there is a potential record selection expression, there obviously
can't be a print list. Get the rse. Otherwise, pick up the print
list. */
if (potential_rse())
node->syn_arg[s_prt_rse] = parse_rse();
else if (!KEYWORD(KW_USING)) {
if (!KEYWORD(KW_THEN) && !KEYWORD(KW_OF) && !KEYWORD(KW_ON))
node->syn_arg[s_prt_list] = parse_print_list();
if (PAR_match(KW_OF))
node->syn_arg[s_prt_rse] = parse_rse();
}
if (!node->syn_arg[s_prt_list] && PAR_match(KW_USING)) {
IBERROR(484); // FORMs not supported
}
else
node->syn_arg[s_prt_output] = parse_output();
return node;
}
static qli_syntax* parse_prompt(void)
{
/**************************************
*
* p a r s e _ p r o m p t
*
**************************************
*
* Functional description
* Parse a prompt expression.
*
**************************************/
PAR_token();
qli_syntax* node = syntax_node(nod_prompt, 1);
// If there is a period, get the prompt string and make a string node
if (PAR_match(KW_DOT)) {
PAR_real();
USHORT l = QLI_token->tok_length;
const TEXT* q = QLI_token->tok_string;
if (QLI_token->tok_type == tok_quoted) {
q++;
l -= 2;
}
node->syn_arg[0] = (qli_syntax*) make_string(q, l);
PAR_token();
}
return node;
}
static QFL parse_qualified_filter(void)
{
/**************************************
*
* p a r s e _ q u a l i f i e d _ f i l t e r
*
**************************************
*
* Functional description
* This token ought to be a filter, possibly qualified.
* Return a qualified filter block, containing the
* filter name in a NAM block and the database in a
* DBB block if a database was specified. Somebody
* else will decide what to do if the database was not
* specified.
*
**************************************/
QFL filter = (QFL) ALLOCD(type_qfl);
filter->qfl_database = parse_database();
filter->qfl_name = parse_name();
return filter;
}
static QFN parse_qualified_function(void)
{
/**************************************
*
* p a r s e _ q u a l i f i e d _ f u n c t i o n
*
**************************************
*
* Functional description
* This token ought to be a function, possibly qualified.
* Return a qualified function block, containing the
* function name in a NAM block and the database in a
* DBB block if a database was specified. Somebody
* else will decide what to do if the database was not
* specified.
*
**************************************/
QFN func = (QFN) ALLOCD(type_qfn);
func->qfn_database = parse_database();
func->qfn_name = parse_name();
return func;
}
static QPR parse_qualified_procedure(void)
{
/**************************************
*
* p a r s e _ q u a l i f i e d _ p r o c e d u r e
*
**************************************
*
* Functional description
* This token ought to be a procedure, possibly qualified.
* Return a qualified procedure block, containing the
* procedure name in a NAM block and the database in a
* DBB block if a database was specified. Somebody
* else will decide what to do if the database was not
* specified.
*
**************************************/
QPR proc = (QPR) ALLOCD(type_qpr);
proc->qpr_database = parse_database();
proc->qpr_name = parse_name();
return proc;
}
static qli_rel* parse_qualified_relation(void)
{
/**************************************
*
* p a r s e _ q u a l i f i e d _ r e l a t i o n
*
**************************************
*
* Functional description
* Check for a relation name, possible qualified. If there
* is a relation name, parse it and return the relation. If
* not, return NULL. Produce a syntax error only if there is
* a partially qualified name.
*
**************************************/
PAR_real();
// If the next token is a database symbol, take it as a qualifier
qli_symbol* db_symbol = QLI_token->tok_symbol;
if (db_symbol && db_symbol->sym_type == SYM_database)
{
PAR_real_token();
if (!PAR_match(KW_DOT))
ERRQ_syntax(202); // Msg202 period in qualified relation name
PAR_real();
qli_rel* relation = resolve_relation(db_symbol, QLI_token->tok_symbol);
if (relation) {
PAR_token();
return relation;
}
ERRQ_print_error(203, SafeArg() << QLI_token->tok_string << db_symbol->sym_string);
// Msg203 %s is not a relation in database %s
}
qli_rel* relation = resolve_relation(0, QLI_token->tok_symbol);
if (relation)
PAR_token();
return relation;
}
static qli_syntax* parse_ready( NOD_T node_type)
{
/**************************************
*
* p a r s e _ r e a d y
*
**************************************
*
* Functional description
* Parse a READY statement:
*
* READY <filename> [AS <symbol>] [,...];
*
**************************************/
qli_lls* stack = NULL;
while (true) {
LEX_filename();
SSHORT l = QLI_token->tok_length;
if (!l)
ERRQ_error(204);
// Msg204 database file name required on READY
const TEXT* q = QLI_token->tok_string;
if (QLI_token->tok_type == tok_quoted) {
l -= 2;
q++;
}
DBB database = (DBB) ALLOCDV(type_dbb, l);
database->dbb_filename_length = l;
memcpy(database->dbb_filename, q, l);
PAR_token();
if (node_type == nod_def_database || node_type == nod_ready) {
if (PAR_match(KW_AS)) {
NAM name = parse_name();
database->dbb_symbol = (qli_symbol*) name;
if (HSH_lookup(name->nam_string, name->nam_length))
ERRQ_error(408, name->nam_string);
// Database handle is not unique
}
else
database->dbb_symbol = (qli_symbol*) make_name();
}
else if (node_type == nod_sql_database) {
if (PAR_match(KW_PAGESIZE)) {
if (database->dbb_pagesize)
ERRQ_syntax(390); // Msg390 Multiple page size specifications
if (!PAR_match(KW_EQUALS))
ERRQ_syntax(396); // Msg396 = (equals)
database->dbb_pagesize = parse_ordinal();
}
database->dbb_symbol = (qli_symbol*) make_name();
}
for (;;) {
if (PAR_match(KW_USER))
database->dbb_user = parse_literal();
else if (PAR_match(KW_PASSWORD))
database->dbb_password = parse_literal();
else
break;
}
ALLQ_push((blk*) database, &stack);
if (!KEYWORD(KW_COMMA) || (node_type == nod_sql_database))
break;
}
command_end();
qli_syntax* node = make_list(stack);
node->syn_type = node_type;
return node;
}
static qli_syntax* parse_relational( USHORT * paren_count)
{
/**************************************
*
* p a r s e _ r e l a t i o n a l
*
**************************************
*
* Functional description
* Parse a relational expression.
*
**************************************/
if (PAR_match(KW_ANY)) {
qli_syntax* anode = syntax_node(nod_any, 1);
anode->syn_arg[0] = parse_rse();
return anode;
}
NOD_T operatr = (NOD_T) 0;
if (PAR_match(KW_EXISTS))
operatr = nod_any;
else if (PAR_match(KW_SINGULAR))
operatr = nod_unique;
if (operatr != (NOD_T) 0) {
PAR_real();
if (PAR_match(KW_LEFT_PAREN)) {
PAR_real();
if (PAR_match(KW_SELECT)) {
PAR_real();
qli_syntax* node = syntax_node(operatr, 2);
if (!PAR_match(KW_ASTERISK))
node->syn_arg[1] = parse_value(0, 0);
node->syn_arg[0] = parse_sql_rse();
parse_matching_paren();
return node;
}
}
if (operatr == nod_any)
ERRQ_syntax(205); /* Msg205 EXISTS (SELECT * <sql rse>) */
else
ERRQ_syntax(488); /* Msg488 SINGULAR (SELECT * <sql rse>) */
}
if (PAR_match(KW_UNIQUE)) {
qli_syntax* node = syntax_node(nod_unique, 1);
node->syn_arg[0] = parse_rse();
return node;
}
bool local_flag = true;
qli_syntax* expr1 = parse_value(paren_count, &local_flag);
if (KEYWORD(KW_RIGHT_PAREN))
return expr1;
const nod_t* rel_ops;
if (KEYWORD(KW_SEMI))
{
for (rel_ops = relationals; *rel_ops != (NOD_T) 0; rel_ops++)
{
if (expr1->syn_type == *rel_ops)
return expr1;
}
}
bool negation = false;
qli_syntax* node = NULL;
PAR_match(KW_IS);
PAR_real();
if (PAR_match(KW_NOT)) {
negation = true;
PAR_real();
}
switch (next_keyword()) {
case KW_IN:
PAR_token();
node = parse_in(expr1, nod_eql, false);
break;
case KW_EQUALS:
case KW_EQ:
operatr = (negation) ? nod_neq : nod_eql;
negation = false;
break;
case KW_NE:
operatr = (negation) ? nod_eql : nod_neq;
negation = false;
break;
case KW_GT:
operatr = (negation) ? nod_leq : nod_gtr;
negation = false;
break;
case KW_GE:
operatr = (negation) ? nod_lss : nod_geq;
negation = false;
break;
case KW_LE:
operatr = (negation) ? nod_gtr : nod_leq;
negation = false;
break;
case KW_LT:
operatr = (negation) ? nod_geq : nod_lss;
negation = false;
break;
case KW_CONTAINING:
operatr = nod_containing;
break;
case KW_MATCHES:
node = parse_matches();
node->syn_arg[0] = expr1;
operatr = node->syn_type;
break;
case KW_LIKE:
PAR_token();
node = syntax_node(nod_like, 3);
node->syn_arg[0] = expr1;
node->syn_arg[1] = parse_value(0, 0);
if (PAR_match(KW_ESCAPE))
node->syn_arg[2] = parse_value(0, 0);
else
node->syn_count = 2;
break;
case KW_STARTS:
PAR_token();
PAR_match(KW_WITH);
node = syntax_node(nod_starts, 2);
node->syn_arg[0] = expr1;
node->syn_arg[1] = parse_value(0, 0);
break;
case KW_NULL:
case KW_MISSING:
PAR_token();
node = syntax_node(nod_missing, 1);
node->syn_arg[0] = expr1;
break;
case KW_BETWEEN:
PAR_token();
node = syntax_node(nod_between, 3);
node->syn_arg[0] = expr1;
node->syn_arg[1] = parse_value(0, 0);
PAR_match(KW_AND);
node->syn_arg[2] = parse_value(0, 0);
break;
default:
for (rel_ops = relationals; *rel_ops != (NOD_T) 0; rel_ops++)
{
if (expr1->syn_type == *rel_ops)
return expr1;
}
ERRQ_syntax(206); // Msg206 relational operatr
}
/* If we haven't already built a node, it must be an ordinary binary operatr.
Build it. */
if (!node) {
PAR_token();
if (PAR_match(KW_ANY))
return parse_in(expr1, operatr, false);
if (PAR_match(KW_ALL))
return parse_in(expr1, operatr, true);
node = syntax_node(operatr, 2);
node->syn_arg[0] = expr1;
node->syn_arg[1] = parse_value(paren_count, &local_flag);
}
// If a negation remains to be handled, zap the node under a NOT.
if (negation)
node = negate(node);
/* If the node isn't an equality, we've done. Since equalities can be
structured as implicit ORs, build them here. */
if (operatr != nod_eql)
return node;
/* We have an equality operation, which can take a number of values. Generate
implicit ORs */
while (PAR_match(KW_COMMA)) {
PAR_real();
PAR_match(KW_OR);
qli_syntax* or_node = syntax_node(nod_or, 2);
or_node->syn_arg[0] = node;
or_node->syn_arg[1] = node = syntax_node(nod_eql, 2);
node->syn_arg[0] = expr1;
node->syn_arg[1] = parse_value(paren_count, &local_flag);
node = or_node;
}
return node;
}
static qli_syntax* parse_relation(void)
{
/**************************************
*
* p a r s e _ r e l a t i o n
*
**************************************
*
* Functional description
* Parse a relation expression. Syntax is:
*
* [ <context_variable> IN ] <relation>
*
**************************************/
qli_syntax* node = syntax_node(nod_relation, s_rel_count);
// Token wasn't a relation name, maybe it's a context variable.
if (!(node->syn_arg[s_rel_relation] = (qli_syntax*) parse_qualified_relation())) {
qli_symbol* context = parse_symbol();
node->syn_arg[s_rel_context] = (qli_syntax*) context;
if (sql_flag || !PAR_match(KW_IN)) {
if (!QLI_databases)
IBERROR(207); // Msg207 a database has not been readied
ERRQ_print_error(208, context->sym_string);
// Msg208 expected \"relation_name\", encountered \"%s\"
}
if (!
(node->syn_arg[s_rel_relation] = (qli_syntax*) parse_qualified_relation()))
{
ERRQ_syntax(209); // Msg209 relation name
}
}
return node;
}
static qli_syntax* parse_rename(void)
{
/**************************************
*
* p a r s e _ r e n a m e
*
**************************************
*
* Functional description
* Parse a PROCEDURE rename statement.
*
**************************************/
PAR_real_token();
if (!PAR_match(KW_PROCEDURE))
ERRQ_syntax(210); // Msg210 PROCEDURE
qli_syntax* node = syntax_node(nod_rename_proc, 2);
node->syn_arg[0] = (qli_syntax*) parse_qualified_procedure();
PAR_match(KW_TO);
node->syn_arg[1] = (qli_syntax*) parse_qualified_procedure();
return node;
}
static qli_syntax* parse_repeat(void)
{
/**************************************
*
* p a r s e _ r e p e a t
*
**************************************
*
* Functional description
* Parse REPEAT statement.
*
**************************************/
PAR_token();
qli_syntax* node = syntax_node(nod_repeat, s_rpt_count);
node->syn_arg[s_rpt_value] = parse_value(0, 0);
node->syn_arg[s_rpt_statement] = parse_statement();
return node;
}
static qli_syntax* parse_report(void)
{
/**************************************
*
* p a r s e _ r e p o r t
*
**************************************
*
* Functional description
* Parse a report specification.
*
**************************************/
++sw_report;
PAR_token();
qli_rpt* report = (qli_rpt*) ALLOCD(type_rpt);
qli_syntax* node = syntax_node(nod_report, s_prt_count);
node->syn_arg[s_prt_list] = (qli_syntax*) report;
// Pick up record select expression
qli_syntax* rse = node->syn_arg[s_prt_rse] = parse_rse();
node->syn_arg[s_prt_output] = parse_output();
// Pick up report clauses
bool top;
for (;;) {
PAR_real();
if (PAR_match(KW_END_REPORT))
break;
switch (next_keyword()) {
case KW_PRINT:
PAR_token();
report->rpt_detail_line = (qli_nod*) parse_print_list();
break;
case KW_AT:
PAR_token();
if (PAR_match(KW_TOP))
top = true;
else if (PAR_match(KW_BOTTOM))
top = false;
else
ERRQ_syntax(382); // Msg382 TOP or BOTTOM
PAR_match(KW_OF);
if (PAR_match(KW_REPORT)) {
qli_brk* control = (qli_brk*) ALLOCD(type_brk);
qli_brk** ptr =
(top) ? &report->rpt_top_rpt : &report->rpt_bottom_rpt;
control->brk_next = *ptr;
*ptr = control;
PAR_match(KW_PRINT);
control->brk_line = parse_print_list();
}
else if (PAR_match(KW_PAGE)) {
qli_brk* control = (qli_brk*) ALLOCD(type_brk);
qli_brk** ptr =
(top) ? &report->rpt_top_page : &report->rpt_bottom_page;
control->brk_next = *ptr;
*ptr = control;
PAR_match(KW_PRINT);
control->brk_line = parse_print_list();
}
else {
qli_brk** ptr =
(top) ? &report->rpt_top_breaks : &report->rpt_bottom_breaks;
if (!*ptr) {
/* control breaks should only be on sorted fields, set up list
of control breaks based on sorted fields and then add action (print)
items to that list. */
qli_syntax* flds = rse->syn_arg[s_rse_sort];
if (!flds)
ERRQ_syntax(383); // Msg383 sort field
qli_brk* tmpptr = *ptr;
for (USHORT i = 0; i < flds->syn_count; i += 2) {
qli_brk* control = (qli_brk*) ALLOCD(type_brk);
control->brk_field = flds->syn_arg[i];
control->brk_line = NULL;
control->brk_statisticals = NULL;
control->brk_next = tmpptr;
tmpptr = control;
}
if (!top) {
/* reverse the 'at bottom' control break list as the
lower control breaks should be performed prior to the higher ones. */
qli_brk* control = 0;
for (qli_brk* tmpptr1 = tmpptr->brk_next; tmpptr;) {
tmpptr->brk_next = control;
control = tmpptr;
if (tmpptr = tmpptr1)
tmpptr1 = tmpptr->brk_next;
}
tmpptr = control;
}
*ptr = tmpptr;
}
qli_syntax* qli_fld = parse_field_name(0);
qli_brk* control;
for (control = *ptr; control; control = control->brk_next) {
qli_syntax* rse_fld = (qli_syntax*) control->brk_field;
if (rse_fld->syn_type != qli_fld->syn_type)
continue;
/* if number of field qualifiers on sort field and control field
are not equal test match of rightmost set */
const USHORT syn_count = MIN(rse_fld->syn_count, qli_fld->syn_count);
USHORT srt_syn = 0, ctl_syn = 0;
if (syn_count != rse_fld->syn_count)
srt_syn = rse_fld->syn_count - syn_count;
if (syn_count != qli_fld->syn_count)
ctl_syn = qli_fld->syn_count - syn_count;
USHORT i;
for (i = 0; i < syn_count; i++) {
const nam* name1 = (NAM) rse_fld->syn_arg[i + srt_syn];
const nam* name2 = (NAM) qli_fld->syn_arg[i + ctl_syn];
if (strcmp(name1->nam_string, name2->nam_string))
break;
}
if (i == qli_fld->syn_count)
break;
}
if (!control)
ERRQ_syntax(383); // Msg383 sort field
PAR_match(KW_PRINT);
control->brk_field = qli_fld;
control->brk_line = parse_print_list();
}
break;
case KW_SET:
PAR_token();
if (PAR_match(KW_COLUMNS)) {
PAR_match(KW_EQUALS);
report->rpt_columns = parse_ordinal();
}
else if (PAR_match(KW_LINES)) {
PAR_match(KW_EQUALS);
report->rpt_lines = parse_ordinal();
}
else if (PAR_match(KW_REPORT_NAME)) {
PAR_match(KW_EQUALS);
report->rpt_name = parse_header();
}
else
ERRQ_syntax(212); // Msg212 report writer SET option
break;
default:
ERRQ_syntax(213); // Msg213 report item
}
PAR_match(KW_SEMI);
}
if (!node->syn_arg[s_prt_output])
node->syn_arg[s_prt_output] = parse_output();
check_end();
--sw_report;
return node;
}
static qli_syntax* parse_rse(void)
{
/**************************************
*
* p a r s e _ r s e
*
**************************************
*
* Functional description
* Parse a record selection expression.
*
**************************************/
PAR_real();
if (PAR_match(KW_ALL))
PAR_real();
qli_syntax* first = NULL;
if (PAR_match(KW_FIRST))
first = parse_value(0, 0);
USHORT count = 0;
qli_lls* stack = NULL;
while (true) {
count++;
ALLQ_push((blk*) parse_relation(), &stack);
qli_syntax* over = NULL;
if (PAR_match(KW_OVER)) {
qli_lls* field_stack = NULL;
while (true) {
ALLQ_push((blk*) parse_field_name(0), &field_stack);
if (!PAR_match(KW_COMMA))
break;
}
over = make_list(field_stack);
}
ALLQ_push((blk*) over, &stack);
if (!PAR_match(KW_CROSS))
break;
}
qli_syntax* node = syntax_node(nod_rse, (int) s_rse_count + 2 * count);
node->syn_count = count;
node->syn_arg[s_rse_first] = first;
qli_syntax** ptr = &node->syn_arg[(int) s_rse_count + 2 * count];
while (stack)
*--ptr = (qli_syntax*) ALLQ_pop(&stack);
// Pick up various other clauses
USHORT sw_with = 0;
while (true) {
if (PAR_match(KW_WITH)) {
if (!sw_with) {
sw_with++;
node->syn_arg[s_rse_boolean] = parse_boolean(0);
}
else
IBERROR(384); // Msg384 Too many WITHs
}
else if (PAR_match(KW_SORTED)) {
PAR_real();
PAR_match(KW_BY);
node->syn_arg[s_rse_sort] = parse_sort();
}
#ifdef PC_ENGINE
else if (PAR_match(KW_USING)) {
PAR_real();
PAR_match(KW_INDEX);
node->syn_arg[s_rse_index] = (qli_syntax*) parse_name();
}
#endif
else if (PAR_match(KW_REDUCED)) {
PAR_real();
PAR_match(KW_TO);
node->syn_arg[s_rse_reduced] = parse_sort();
}
else if (PAR_match(KW_GROUP)) {
PAR_real();
PAR_match(KW_BY);
stack = NULL;
while (true) {
ALLQ_push((blk*) parse_udf_or_field(), &stack);
if (!PAR_match(KW_COMMA))
break;
}
node->syn_arg[s_rse_group_by] = make_list(stack);
}
else
break;
}
return node;
}
static qli_syntax* parse_select(void)
{
/**************************************
*
* p a r s e _ s e l e c t
*
**************************************
*
* Functional description
* Parse a SQL select statement.
*
**************************************/
++sql_flag;
PAR_token();
qli_syntax* node = syntax_node(nod_print, s_prt_count);
if (!PAR_match(KW_ALL) && PAR_match(KW_DISTINCT))
node->syn_arg[s_prt_distinct] = INT_CAST TRUE;
// Get list of items
if (!PAR_match(KW_ASTERISK)) {
qli_lls* stack = NULL;
while (true) {
qli_syntax* item = syntax_node(nod_print_item, s_itm_count);
item->syn_arg[s_itm_value] = parse_value(0, 0);
ALLQ_push((blk*) item, &stack);
if (!PAR_match(KW_COMMA))
break;
}
node->syn_arg[s_prt_list] = make_list(stack);
}
qli_syntax* rse = parse_sql_rse();
node->syn_arg[s_prt_rse] = rse;
rse->syn_arg[s_rse_list] = node->syn_arg[s_prt_list];
if (PAR_match(KW_ORDER)) {
PAR_real();
PAR_match(KW_BY);
node->syn_arg[s_prt_order] = parse_sort();
}
--sql_flag;
return node;
}
static qli_syntax* parse_set(void)
{
/**************************************
*
* p a r s e _ s e t
*
**************************************
*
* Functional description
* Parse a SET statement.
*
**************************************/
PAR_token();
qli_lls* stack = NULL;
USHORT count = 0;
while (true) {
PAR_real();
U_IPTR value = TRUE;
if (PAR_match(KW_NO)) {
value = FALSE;
PAR_real();
}
enum set_t sw;
switch (QLI_token->tok_keyword) {
case KW_BLR:
sw = set_blr;
PAR_token();
break;
case KW_STATISTICS:
sw = set_statistics;
PAR_token();
break;
case KW_COLUMNS:
sw = set_columns;
PAR_token();
PAR_match(KW_EQUALS);
value = parse_ordinal();
break;
case KW_LINES:
sw = set_lines;
PAR_token();
PAR_match(KW_EQUALS);
value = parse_ordinal();
break;
case KW_SEMICOLON:
sw = set_semi;
PAR_token();
break;
case KW_ECHO:
sw = set_echo;
PAR_token();
break;
case KW_MATCHING_LANGUAGE:
sw = set_matching_language;
PAR_token();
PAR_match(KW_EQUALS);
if (value)
value = (U_IPTR) parse_literal();
break;
case KW_PASSWORD:
sw = set_password;
PAR_token();
value = (U_IPTR) parse_literal();
break;
case KW_PROMPT:
sw = set_prompt;
PAR_token();
value = (U_IPTR) parse_literal();
break;
case KW_CONT_PROMPT:
sw = set_continuation;
PAR_token();
value = (U_IPTR) parse_literal();
break;
case KW_USER:
sw = set_user;
PAR_token();
value = (U_IPTR) parse_literal();
break;
case KW_COUNT:
sw = set_count;
PAR_token();
break;
case KW_CHAR:
sw = set_charset;
PAR_token();
PAR_match(KW_SET);
if (value) { // allow for NO
PAR_match(KW_EQUALS);
value = (U_IPTR) parse_name();
}
break;
case KW_NAMES:
sw = set_charset;
PAR_token();
if (value) {
value = (U_IPTR) parse_name();
}
break;
#ifdef DEV_BUILD
case KW_EXPLAIN:
sw = set_explain;
PAR_token();
break;
case KW_HEXOUT:
sw = set_hex_output;
PAR_token();
break;
#endif
default:
ERRQ_syntax(214); // Msg214 set option
}
ALLQ_push((blk*) sw, &stack);
ALLQ_push((blk*) value, &stack);
count++;
if (!PAR_match(KW_COMMA))
break;
}
command_end();
qli_syntax* node = make_list(stack);
node->syn_count = count;
node->syn_type = nod_set;
return node;
}
static qli_syntax* parse_shell(void)
{
/**************************************
*
* p a r s e _ s h e l l
*
**************************************
*
* Functional description
* Parse SHELL command.
*
**************************************/
PAR_token();
qli_syntax* node = syntax_node(nod_shell, 1);
if (!KEYWORD(KW_SEMI))
node->syn_arg[0] = (qli_syntax*) parse_literal();
command_end();
return node;
}
static qli_syntax* parse_show(void)
{
/**************************************
*
* p a r s e _ s h o w
*
**************************************
*
* Functional description
* Parse a SHOW statement. The first
* group are individual things (one
* named relation, field, form, ...)
*
* the second group are sets of things
* and can be qualified with a FOR
* [DATABASE] <handle>
*
**************************************/
PAR_token();
qli_lls* stack = NULL;
USHORT count = 0;
while (true) {
PAR_real();
BLK value = NULL;
qli_symbol* symbol = QLI_token->tok_symbol;
enum show_t sw;
if (PAR_match(KW_ALL))
sw = show_all;
else if (PAR_match(KW_MATCHING_LANGUAGE))
sw = show_matching_language;
else if (PAR_match(KW_VERSION))
sw = show_version;
else if (PAR_match(KW_RELATION))
{
if (!(value = (BLK) parse_qualified_relation()))
ERRQ_syntax(216); // Msg216 relation name
else
sw = show_relation;
}
else if (PAR_match(KW_FILTER)) {
sw = show_filter;
value = (BLK) parse_qualified_filter();
}
else if (PAR_match(KW_FUNCTION)) {
sw = show_function;
value = (BLK) parse_qualified_function();
}
else if ((PAR_match(KW_DATABASES)) || (PAR_match(KW_READY)))
sw = show_databases;
else if (PAR_match(KW_DATABASE)) {
sw = show_database;
if (value = (BLK) get_dbb(QLI_token->tok_symbol))
PAR_token();
}
else if (PAR_match(KW_FIELD)) {
sw = show_field;
value = (BLK) parse_field_name(0);
}
else if (PAR_match(KW_PROCEDURE)) {
sw = show_procedure;
value = (BLK) parse_qualified_procedure();
}
else if (PAR_match(KW_VARIABLE)) {
sw = show_variable;
value = (BLK) parse_name();
}
else if (PAR_match(KW_VARIABLES))
sw = show_variables;
else if (PAR_match(KW_FIELDS)) {
if (PAR_match(KW_FOR)) {
if (PAR_match(KW_DATABASE)) {
if (value = (BLK) get_dbb(QLI_token->tok_symbol))
PAR_token();
else
ERRQ_syntax(221); // Msg221 database name
sw = show_db_fields;
}
else {
PAR_match(KW_RELATION);
if (!(value = (BLK) parse_qualified_relation()))
ERRQ_syntax(218); // Msg218 relation name
else
sw = show_relation;
}
}
else
sw = show_all;
}
else if (PAR_match(KW_INDICES)) {
sw = show_indices;
if (PAR_match(KW_FOR))
if (PAR_match(KW_DATABASE)) {
if (value = (BLK) get_dbb(QLI_token->tok_symbol))
PAR_token();
else
ERRQ_syntax(221); // Msg221 database name
sw = show_db_indices;
}
else if (!(value = (BLK) parse_qualified_relation()))
ERRQ_syntax(220); // Msg220 relation name
}
else if (PAR_match(KW_SECURITY_CLASS)) {
sw = show_security_class;
value = (BLK) parse_name();
}
else if (PAR_match(KW_TRIGGERS)) {
sw = show_triggers;
if (PAR_match(KW_FOR)) {
if (PAR_match(KW_DATABASE)) {
if (value = (BLK) get_dbb(QLI_token->tok_symbol))
PAR_token();
else
ERRQ_syntax(221); // Msg221 database name
}
else {
PAR_match(KW_RELATION);
if (!(value = (BLK) parse_qualified_relation()))
ERRQ_syntax(222); // Msg222 relation_name
sw = show_trigger;
}
}
}
else if (PAR_match(KW_RELATIONS))
sw = show_relations;
else if (PAR_match(KW_VIEWS))
sw = show_views;
else if (PAR_match(KW_SECURITY_CLASSES))
sw = show_security_classes;
else if (PAR_match(KW_SYSTEM)) {
if (PAR_match(KW_TRIGGERS))
sw = show_system_triggers;
else if (PAR_match(KW_RELATIONS) ||
QLI_token->tok_type == tok_eol ||
KEYWORD(KW_SEMI) || KEYWORD(KW_FOR))
sw = show_system_relations;
else
ERRQ_syntax(215); // Msg215 RELATIONS or TRIGGERS
}
else if (PAR_match(KW_PROCEDURES))
sw = show_procedures;
else if (PAR_match(KW_FILTERS))
sw = show_filters;
else if (PAR_match(KW_FUNCTIONS))
sw = show_functions;
else if (PAR_match(KW_GLOBAL)) {
PAR_real();
if (PAR_match(KW_FIELD)) {
sw = show_global_field;
value = (BLK) parse_field_name(0);
}
else if (PAR_match(KW_FIELDS))
sw = show_global_fields;
}
else if (symbol && symbol->sym_type == SYM_relation) {
sw = show_relation;
value = symbol->sym_object;
PAR_token();
}
else if (value = (BLK) get_dbb(symbol)) {
sw = show_database;
PAR_token();
if (PAR_match(KW_DOT)) {
if (PAR_match(KW_RELATIONS))
sw = show_relations;
else if (PAR_match(KW_FIELDS))
sw = show_db_fields;
else if (PAR_match(KW_INDICES))
sw = show_db_indices;
else if (PAR_match(KW_SECURITY_CLASS))
sw = show_security_class;
else if (PAR_match(KW_TRIGGERS))
sw = show_triggers;
else if (PAR_match(KW_VIEWS))
sw = show_views;
else if (PAR_match(KW_SECURITY_CLASSES))
sw = show_security_classes;
else if (PAR_match(KW_SYSTEM)) {
if (PAR_match(KW_TRIGGERS))
sw = show_system_triggers;
else if (PAR_match(KW_RELATIONS) ||
QLI_token->tok_type == tok_eol ||
KEYWORD(KW_SEMI) || KEYWORD(KW_FOR))
{
sw = show_system_relations;
}
else
ERRQ_syntax(215); // Msg215 RELATIONS or TRIGGERS
}
else if (PAR_match(KW_PROCEDURES))
sw = show_procedures;
else if (PAR_match(KW_FILTERS))
sw = show_filters;
else if (PAR_match(KW_FUNCTIONS))
sw = show_functions;
else if (PAR_match(KW_GLOBAL)) {
PAR_real();
if (PAR_match(KW_FIELD)) {
sw = show_global_field;
value = (BLK) parse_field_name(0);
}
else if (PAR_match(KW_FIELDS))
sw = show_global_fields;
}
else {
qli_rel* relation =
resolve_relation(symbol, QLI_token->tok_symbol);
if (relation) {
sw = show_relation;
value = relation->rel_symbol->sym_object;
PAR_token();
}
else {
sw = show_procedure;
QPR proc = (QPR) ALLOCD(type_qpr);
proc->qpr_database = (DBB) value;
proc->qpr_name = parse_name();
value = (BLK) proc;
}
}
}
}
else {
sw = show_procedure;
value = (BLK) parse_qualified_procedure();
}
ALLQ_push((blk*) sw, &stack);
if (!value && (sw == show_relations || sw == show_views ||
sw == show_security_classes
|| sw == show_system_triggers
|| sw == show_system_relations || sw == show_procedures
|| sw == show_filters
|| sw == show_functions || sw == show_global_fields))
{
if (PAR_match(KW_FOR)) {
PAR_match(KW_DATABASE);
if (value = (BLK) get_dbb(QLI_token->tok_symbol))
PAR_token();
else
ERRQ_syntax(221); // Msg221 database name
}
}
ALLQ_push(value, &stack);
count++;
if (!PAR_match(KW_COMMA))
break;
}
command_end();
qli_syntax* node = make_list(stack);
node->syn_count = count;
node->syn_type = nod_show;
return node;
}
static qli_syntax* parse_sort(void)
{
/**************************************
*
* p a r s e _ s o r t
*
**************************************
*
* Functional description
* Parse a sort list.
*
**************************************/
USHORT direction = 0;
bool sensitive = false;
qli_lls* stack = NULL;
while (true) {
PAR_real();
if (!sql_flag)
{
if (PAR_match(KW_ASCENDING)) {
direction = 0;
continue;
}
if (PAR_match(KW_DESCENDING)) {
direction = 1;
continue;
}
if (PAR_match(KW_EXACTCASE)) {
sensitive = false;
continue;
}
if (PAR_match(KW_ANYCASE)) {
sensitive = true;
continue;
}
}
qli_syntax* node;
if (sql_flag && QLI_token->tok_type == tok_number) {
node = syntax_node(nod_position, 1);
node->syn_arg[0] = INT_CAST parse_ordinal();
}
else
node = parse_value(0, 0);
if (sensitive) {
qli_syntax* upcase = syntax_node(nod_upcase, 1);
upcase->syn_arg[0] = node;
ALLQ_push((blk*) upcase, &stack);
}
else
ALLQ_push((blk*) node, &stack);
if (sql_flag)
if (PAR_match(KW_ASCENDING))
direction = 0;
else if (PAR_match(KW_DESCENDING))
direction = 1;
ALLQ_push((blk*) (IPTR) direction, &stack);
if (!PAR_match(KW_COMMA))
break;
}
return make_list(stack);
}
static qli_syntax* parse_sql_alter(void)
{
/**************************************
*
* p a r s e _ s q l _ a l t e r
*
**************************************
*
* Functional description
* Parse the leading clauses of a SQL ALTER statement.
*
**************************************/
PAR_real_token();
if (!PAR_match(KW_TABLE))
ERRQ_syntax(407); // Msg407 TABLE
qli_syntax* node = syntax_node(nod_sql_al_table, 2);
qli_rel* relation = parse_qualified_relation();
node->syn_arg[0] = (qli_syntax*) relation;
for (;;) {
qli_fld* field;
if (PAR_match(KW_ADD)) {
field = parse_sql_field();
field->fld_flags |= FLD_add;
}
else if (PAR_match(KW_DROP)) {
field = parse_field(false);
field->fld_flags |= FLD_drop;
}
else
ERRQ_syntax(405); // Msg405 ADD or DROP
field->fld_next = (qli_fld*) node->syn_arg[1];
node->syn_arg[1] = (qli_syntax*) field;
if (!PAR_match(KW_COMMA))
break;
}
command_end();
return node;
}
static qli_syntax* parse_sql_create(void)
{
/**************************************
*
* p a r s e _ s q l _ c r e a t e
*
**************************************
*
* Functional description
* Parse the leading clauses of a SQL CREATE statement.
*
**************************************/
PAR_real_token();
if (KEYWORD(KW_DATABASE))
return parse_ready(nod_sql_database);
if (KEYWORD(KW_UNIQUE) || KEYWORD(KW_ASCENDING)
|| KEYWORD(KW_DESCENDING) || KEYWORD(KW_INDEX))
{
bool unique = false, descending = false;
while (true) {
if (PAR_match(KW_UNIQUE))
unique = true;
else if (PAR_match(KW_ASCENDING))
descending = false;
else if (PAR_match(KW_DESCENDING))
descending = true;
else if (PAR_match(KW_INDEX))
return parse_sql_index_create(unique, descending);
else
ERRQ_syntax(389); // Msg389 INDEX
}
}
if (PAR_match(KW_TABLE))
return parse_sql_table_create();
#ifdef NOT_USED_OR_REPLACED
/***
if (PAR_match (KW_VIEW))
return parse_sql_view_create();
***/
#endif
ERRQ_syntax(386); // Msg386 object type for CREATE
return NULL;
}
static int parse_sql_dtype( USHORT* length, USHORT* scale, USHORT* precision,
USHORT* sub_type)
{
/**************************************
*
* p a r s e _ s q l _ d t y p e
*
**************************************
*
* Functional description
* Parse a SQL datatype clause.
*
**************************************/
USHORT dtype = dtype_unknown;
const KWWORDS keyword = QLI_token->tok_keyword;
PAR_token();
*scale = 0;
*length = 1;
*precision = 0;
*sub_type = 0;
switch (keyword) {
case KW_DATE:
*length = 8;
return dtype_timestamp;
case KW_CHAR:
dtype = dtype_text;
break;
case KW_VARCHAR:
dtype = dtype_varying;
break;
case KW_SMALLINT:
*length = sizeof(SSHORT);
return dtype_short;
case KW_INTEGER:
*length = sizeof(SLONG);
return dtype_long;
case KW_BIGINT:
*length = sizeof(SINT64);
return dtype_int64;
case KW_REAL:
case KW_FLOAT:
*length = sizeof(float);
dtype = dtype_real;
break;
case KW_LONG:
if (!PAR_match(KW_FLOAT))
ERRQ_syntax(388); // Msg388 "FLOAT"
*length = sizeof(double);
dtype = dtype_double;
break;
case KW_DOUBLE:
if (!PAR_match(KW_PRECISION))
ERRQ_syntax(509); // Msg509 "PRECISION"
*length = sizeof(double);
dtype = dtype_double;
break;
case KW_DECIMAL:
*length = sizeof(SLONG);
dtype = dtype_long;
*sub_type = dsc_num_type_decimal;
break;
case KW_NUMERIC:
*length = sizeof(SLONG);
dtype = dtype_long;
*sub_type = dsc_num_type_numeric;
break;
}
// CVC: SQL doesn't accept arbitrary types with scale specification.
//if (dtype == dtype_long || dtype == dtype_real || dtype == dtype_double) {
if (keyword == KW_DECIMAL || keyword == KW_NUMERIC)
{
if (PAR_match(KW_LEFT_PAREN))
{
const USHORT logLength = parse_ordinal();
if (logLength < 1)
ERRQ_syntax(512); // Msg512 "Field length should be greater than zero"
else if (logLength < 5)
{
*length = sizeof(SSHORT);
dtype = dtype_short;
}
else if (logLength > 18)
ERRQ_syntax(511); // Msg511 "Field length exceeds allowed range"
else if (logLength > 9)
{
*length = sizeof(SINT64);
dtype = dtype_int64;
}
if (PAR_match(KW_COMMA))
{
const bool l = (PAR_match(KW_MINUS)) ? true : false;
*scale = parse_ordinal();
if (*scale > logLength)
ERRQ_syntax(510); // Msg510 "Field scale exceeds allowed range"
if (l || *scale > 0) // We need to have it negative in system tables.
*scale = -(*scale);
}
*precision = logLength;
parse_matching_paren();
}
}
else if (dtype == dtype_text || dtype == dtype_varying)
{
if (PAR_match(KW_LEFT_PAREN))
{
USHORT l = parse_ordinal();
if (l > MAX_COLUMN_SIZE)
ERRQ_syntax(511); // Msg511 "Field length exceeds allowed range"
if (dtype == dtype_varying)
{
if (l > MAX_COLUMN_SIZE - sizeof(SSHORT))
ERRQ_syntax(511); // Msg511 "Field length exceeds allowed range"
l += sizeof(SSHORT);
}
*length = l;
parse_matching_paren();
}
}
return dtype;
}
static qli_fld* parse_sql_field(void)
{
/**************************************
*
* p a r s e _ s q l _ f i e l d
*
**************************************
*
* Functional description
* Parse a field description.
*
**************************************/
PAR_real();
USHORT dtype, length, scale, precision, sub_type;
dtype = length = scale = precision = sub_type = 0;
qli_symbol* name = parse_symbol();
PAR_real();
switch (QLI_token->tok_keyword) {
case KW_DOUBLE:
PAR_match(KW_PRECISION);
case KW_NUMERIC:
case KW_REAL:
case KW_DATE:
case KW_CHAR:
case KW_VARCHAR:
case KW_SMALLINT:
case KW_INTEGER:
case KW_FLOAT:
case KW_LONG:
case KW_DECIMAL:
case KW_BIGINT:
dtype = parse_sql_dtype(&length, &scale, &precision, &sub_type);
break;
default:
ERRQ_syntax(179); // Msg179 field definition clause
break;
}
qli_fld* field = (qli_fld*) ALLOCDV(type_fld, length);
field->fld_name = name;
field->fld_dtype = dtype;
field->fld_scale = scale;
field->fld_length = length;
field->fld_precision = precision;
field->fld_sub_type = sub_type;
if (PAR_match(KW_NOT))
if (PAR_match(KW_NULL)) {
field->fld_flags |= FLD_not_null;
}
else {
ERRQ_syntax(393); // Msg393 NULL
}
return field;
}
static qli_syntax* parse_sql_grant_revoke( USHORT type)
{
/**************************************
*
* p a r s e _ s q l _ g r a n t _ r e v o k e
*
**************************************
*
* Functional description
* Parse a SQL GRANT/REVOKE statement.
*
**************************************/
PAR_real_token();
qli_syntax* node = syntax_node((NOD_T) type, s_grant_count);
qli_lls* stack = NULL;
USHORT privileges = 0;
if (PAR_match(KW_ALL)) {
PAR_match(KW_PRIVILEGES);
privileges |= PRV_all;
}
else
{
while (true) {
PAR_real();
if (PAR_match(KW_SELECT)) {
privileges |= PRV_select;
continue;
}
if (PAR_match(KW_INSERT)) {
privileges |= PRV_insert;
continue;
}
if (PAR_match(KW_DELETE)) {
privileges |= PRV_delete;
continue;
}
if (PAR_match(KW_UPDATE))
{
privileges |= PRV_update;
if (PAR_match(KW_COMMA))
continue;
if (KEYWORD(KW_ON))
break;
if (!PAR_match(KW_LEFT_PAREN))
ERRQ_syntax(187); // Msg187 left parenthesis
do {
if (KEYWORD(KW_SELECT) || KEYWORD(KW_INSERT)
|| KEYWORD(KW_DELETE) || KEYWORD(KW_UPDATE))
{
break;
}
PAR_real();
ALLQ_push((blk*) parse_name(), &stack);
} while (PAR_match(KW_COMMA));
if (!PAR_match(KW_RIGHT_PAREN))
ERRQ_syntax(191); // Msg191 right parenthesis
continue;
}
if (!PAR_match(KW_COMMA))
break;
}
}
node->syn_arg[s_grant_fields] = make_list(stack);
PAR_real();
if (!PAR_match(KW_ON))
ERRQ_syntax(397); // Msg397 ON
PAR_real();
if (!(node->syn_arg[s_grant_relation] = (qli_syntax*) parse_qualified_relation()))
ERRQ_syntax(170); // Msg170 relation name
if (type == (USHORT) nod_sql_grant) {
if (!PAR_match(KW_TO))
ERRQ_syntax(404); // Msg404 TO
}
else {
if (!PAR_match(KW_FROM))
ERRQ_syntax(403); // Msg403 FROM
}
stack = NULL;
while (true) {
PAR_real();
ALLQ_push((blk*) parse_name(), &stack);
if (!PAR_match(KW_COMMA))
break;
}
node->syn_arg[s_grant_users] = make_list(stack);
if (type == (USHORT) nod_sql_grant)
if (PAR_match(KW_WITH)) {
PAR_real();
if (!PAR_match(KW_GRANT))
ERRQ_syntax(401); // Msg401 GRANT
PAR_match(KW_OPTION);
privileges |= PRV_grant_option;
}
node->syn_arg[s_grant_privileges] = INT_CAST privileges;
return node;
}
static qli_syntax* parse_sql_index_create(const bool unique, const bool descending)
{
/**************************************
*
* p a r s e _ s q l _ i n d e x _ c r e a t e
*
**************************************
*
* Functional description
* Parse the SQL CREATE INDEX statement.
*
**************************************/
PAR_real();
qli_syntax* node = syntax_node(nod_def_index, s_dfi_count);
if (unique)
node->syn_flags |= s_dfi_flag_unique;
if (descending)
node->syn_flags |= s_dfi_flag_descending;
node->syn_arg[s_dfi_name] = (qli_syntax*) parse_symbol();
PAR_real();
if (!PAR_match(KW_ON))
ERRQ_syntax(397); // Msg397 ON
if (!(node->syn_arg[s_dfi_relation] = (qli_syntax*) parse_qualified_relation()))
ERRQ_syntax(170); // Msg170 relation name
PAR_real();
if (!PAR_match(KW_LEFT_PAREN))
ERRQ_syntax(185); // Msg185 left parenthesis
qli_lls* stack = NULL;
for (;;) {
ALLQ_push((blk*) parse_name(), &stack);
if (PAR_match(KW_RIGHT_PAREN))
break;
if (!PAR_match(KW_COMMA))
ERRQ_syntax(171); // Msg171 comma between field definitions
}
node->syn_arg[s_dfi_fields] = make_list(stack);
command_end();
return node;
}
static qli_syntax* parse_sql_joined_relation( qli_syntax* prior_context)
{
/**************************************
*
* p a r s e _ s q l _ j o i n e d _ r e l a t i o n
*
**************************************
*
* Functional description
* Parse a join relation clause.
*
**************************************/
qli_syntax* left;
if (PAR_match(KW_LEFT_PAREN)) {
left = parse_sql_joined_relation(0);
parse_matching_paren();
}
else if (!(left = parse_sql_relation()))
return NULL;
return parse_sql_join_clause(left);
}
static qli_syntax* parse_sql_join_clause( qli_syntax* left)
{
/**************************************
*
* p a r s e _ s q l _ j o i n _ c l a u s e
*
**************************************
*
* Functional description
* Parse a join relation clause.
*
**************************************/
const NOD_T join_type = parse_join_type();
if (join_type == (NOD_T) 0)
return left;
qli_syntax* right = parse_sql_joined_relation(left);
if (!right)
ERRQ_syntax(490); // Msg490 joined relation clause
if (!PAR_match(KW_ON))
ERRQ_syntax(492); // Msg492 ON
qli_syntax* node = syntax_node(nod_rse, (int) s_rse_count + 2 * 2);
node->syn_count = 2;
node->syn_arg[s_rse_count] = left;
node->syn_arg[s_rse_count + 2] = right;
node->syn_arg[s_rse_join_type] = (qli_syntax*) join_type;
node->syn_arg[s_rse_boolean] = parse_boolean(0);
return parse_sql_join_clause(node);
}
static qli_syntax* parse_sql_table_create(void)
{
/**************************************
*
* p a r s e _ s q l _ t a b l e _ c r e a t e
*
**************************************
*
* Functional description
* Parse the SQL CREATE TABLE statement.
*
**************************************/
PAR_real();
qli_syntax* node = syntax_node(nod_sql_cr_table, 1);
qli_rel* relation = (qli_rel*) ALLOCD(type_rel);
node->syn_arg[0] = (qli_syntax*) relation;
relation->rel_database = parse_database();
relation->rel_symbol = parse_symbol();
qli_fld** ptr = &relation->rel_fields;
if (!PAR_match(KW_LEFT_PAREN))
ERRQ_syntax(185); // Msg185 left parenthesis
PAR_real();
for (;;) {
qli_fld* field = parse_sql_field();
*ptr = field;
ptr = &field->fld_next;
if (PAR_match(KW_RIGHT_PAREN))
break;
if (!PAR_match(KW_COMMA))
ERRQ_syntax(171); // Msg171 comma between field definitions
}
command_end();
return node;
}
#ifdef NOT_USED_OR_REPLACED
static qli_syntax* parse_sql_view_create(void)
{
/**************************************
*
* p a r s e _ s q l _ v i e w _ c r e a t e
*
**************************************
*
* Functional description
* Parse the SQL CREATE VIEW statement.
*
**************************************/
PAR_real();
sw_sql_view = true;
qli_syntax* node = syntax_node(nod_sql_cr_view, s_crv_count);
qli_lls* stack = NULL;
qli_rel* relation = (qli_rel*) ALLOCD(type_rel);
node->syn_arg[s_crv_name] = (qli_syntax*) relation;
relation->rel_database = parse_database();
relation->rel_symbol = parse_symbol();
// if field list is present parse it and create corresponding field blocks
if (PAR_match(KW_LEFT_PAREN)) {
for (;;) {
ALLQ_push(parse_name(), &stack);
if (PAR_match(KW_RIGHT_PAREN))
break;
if (!PAR_match(KW_COMMA))
ERRQ_syntax(171); // Msg171 comma between field definitions
}
}
/* node->syn_arg [s_crv_fields] = make_list (stack); */
if (!PAR_match(KW_AS))
ERRQ_syntax(394); // Msg394 As
if (!KEYWORD(KW_SELECT))
ERRQ_syntax(395); // Msg395 Select
node->syn_arg[s_crv_rse] = parse_select();
sw_sql_view = false;
return node;
}
#endif
static qli_syntax* parse_sql_relation(void)
{
/**************************************
*
* p a r s e _ s q l _ r e l a t i o n
*
**************************************
*
* Functional description
* Parse a SQL relation clause.
*
**************************************/
qli_syntax* node = syntax_node(nod_relation, s_rel_count);
if (!(node->syn_arg[s_rel_relation] = (qli_syntax*) parse_qualified_relation()))
ERRQ_syntax(223); // Msg223 relation name
if (!QLI_token->tok_symbol)
node->syn_arg[s_rel_context] = (qli_syntax*) parse_symbol();
return node;
}
static qli_syntax* parse_sql_rse(void)
{
/**************************************
*
* p a r s e _ s q l _ r s e
*
**************************************
*
* Functional description
* Parse the trailing clauses of a SQL SELECT statement.
*
**************************************/
qli_lls* stack = NULL;
USHORT count = 0;
PAR_real();
if (!PAR_match(KW_FROM))
ERRQ_syntax(224); // Msg224 FROM clause
// Parse FROM list of relations
while (true) {
count++;
ALLQ_push((blk*) parse_sql_joined_relation(0), &stack);
if (!PAR_match(KW_COMMA))
break;
}
/* Build a syntax node. Since SQL doesn't support OVER, only every
other slot will be used in the RSE. */
qli_syntax* node = syntax_node(nod_rse, (int) s_rse_count + 2 * count);
node->syn_count = count;
qli_syntax** ptr = &node->syn_arg[(int) s_rse_count + 2 * count];
while (stack) {
--ptr;
*--ptr = (qli_syntax*) ALLQ_pop(&stack);
}
if (PAR_match(KW_WITH))
node->syn_arg[s_rse_boolean] = parse_boolean(0);
if (PAR_match(KW_GROUP)) {
if (sw_sql_view)
ERRQ_syntax(391); // Msg391 No group by in view def
PAR_real();
PAR_match(KW_BY);
stack = NULL;
while (true) {
ALLQ_push((blk*) parse_udf_or_field(), &stack);
if (!PAR_match(KW_COMMA))
break;
}
node->syn_arg[s_rse_group_by] = make_list(stack);
if (PAR_match(KW_HAVING))
node->syn_arg[s_rse_having] = parse_boolean(0);
}
return node;
}
static qli_syntax* parse_sql_singleton_select(void)
{
/**************************************
*
* p a r s e _ s q l _ s i n g l e t o n _ s e l e c t
*
**************************************
*
* Functional description
* Finish parsing an SQL singleton select and
* turn it into a FIRST ... FROM --- not exactly
* kosher, but a start.
*
**************************************/
qli_syntax* value = parse_primitive_value(0, 0);
PAR_real();
qli_syntax* node = syntax_node(nod_from, s_stt_count);
node->syn_arg[s_stt_value] = value;
node->syn_arg[s_stt_rse] = parse_sql_rse();
--sql_flag; // The increment was done in parse_sql_subquery, the only caller.
return node;
}
static qli_syntax* parse_sql_subquery(void)
{
/**************************************
*
* p a r s e _ s q l _ s u b q u e r y
*
**************************************
*
* Functional description
* Parse an sql subquery that should
* return a single value.
*
**************************************/
if (sw_sql_view)
ERRQ_syntax(392); // Msg392 No aggregates in view def
PAR_token();
KWWORDS keyword = next_keyword();
++sql_flag;
const nod_types* ntypes;
const nod_types* const endtypes = statisticals + FB_NELEM(statisticals);
for (ntypes = statisticals; ntypes < endtypes; ntypes++)
{
if (ntypes->nod_t_keyword == KW_none)
return parse_sql_singleton_select();
if (ntypes->nod_t_keyword == keyword)
break;
}
fb_assert(ntypes < endtypes);
if (ntypes >= endtypes)
return NULL;
PAR_token();
qli_syntax* node = syntax_node(ntypes->nod_t_node, s_stt_count);
PAR_match(KW_LEFT_PAREN);
if (node->syn_type != nod_count || !PAR_match(KW_ASTERISK)) {
if (PAR_match(KW_DISTINCT))
node->syn_arg[s_prt_distinct] = INT_CAST TRUE;
node->syn_arg[s_stt_value] = parse_value(0, 0);
}
parse_matching_paren();
node->syn_arg[s_stt_rse] = parse_sql_rse();
--sql_flag;
return node;
}
static qli_syntax* parse_statement(void)
{
/**************************************
*
* p a r s e _ s t a t e m e n t
*
**************************************
*
* Functional description
* Parse a statement. (Set statement switch
* to true here as well as in PARQ_parse to
* avoid confusion with linked statements
* e.g. THEN conjuncts )
*
**************************************/
qli_syntax* node;
PAR_real();
sw_statement = true;
function_count = 0;
switch (next_keyword()) {
case KW_ABORT:
node = parse_abort();
break;
case KW_ACCEPT:
node = parse_accept();
break;
case KW_COMMIT:
node = parse_transaction(nod_commit_retaining);
break;
case KW_DECLARE:
node = parse_declare();
break;
case KW_DELETE:
PAR_match(KW_DELETE);
node = parse_delete();
break;
case KW_ERASE:
node = parse_erase();
break;
case KW_FOR:
node = parse_for();
break;
case KW_IF:
node = parse_if();
break;
case KW_INSERT:
node = parse_insert();
break;
case KW_LIST:
node = parse_list_fields();
break;
case KW_MODIFY:
node = parse_modify();
break;
case KW_PRINT:
node = parse_print();
break;
case KW_REPEAT:
node = parse_repeat();
break;
case KW_REPORT:
node = parse_report();
break;
case KW_SELECT:
node = parse_select();
break;
case KW_STORE:
node = parse_store();
break;
case KW_UPDATE:
node = parse_update();
break;
case KW_BEGIN:
{
qli_lls* stack = NULL;
PAR_token();
while (true) {
PAR_real();
if (PAR_match(KW_END))
break;
ALLQ_push((blk*) parse_statement(), &stack);
PAR_match(KW_SEMI);
}
node = make_list(stack);
}
break;
default:
node = parse_assignment();
}
check_end();
// Check for the "THEN" connective. If found, make a list of statements.
if (QLI_token->tok_type != tok_eol || (QLI_semi && !KEYWORD(KW_SEMI)))
PAR_match(KW_SEMI);
if (!PAR_match(KW_THEN))
return node;
qli_lls* stack = NULL;
ALLQ_push((blk*) node, &stack);
ALLQ_push((blk*) parse_statement(), &stack);
return make_list(stack);
}
static qli_syntax* parse_statistical(void)
{
/**************************************
*
* p a r s e _ s t a t i s t i c a l
*
**************************************
*
* Functional description
* Parse statistical expression.
*
**************************************/
const KWWORDS keyword = next_keyword();
PAR_token();
const nod_types* ntypes;
const nod_types* const endtypes = statisticals + FB_NELEM(statisticals);
for (ntypes = statisticals; ntypes < endtypes; ntypes++)
if (ntypes->nod_t_keyword == keyword)
break;
fb_assert(ntypes < endtypes);
if (ntypes >= endtypes)
return NULL;
// Handle SQL statisticals a little differently
if (sql_flag) {
qli_syntax* anode = syntax_node(ntypes->nod_t_sql_node, s_stt_count);
if (!PAR_match(KW_LEFT_PAREN))
ERRQ_syntax(227); // Msg227 left parenthesis
if (anode->syn_type != nod_agg_count || !PAR_match(KW_ASTERISK)) {
if (PAR_match(KW_DISTINCT))
anode->syn_arg[s_prt_distinct] = INT_CAST TRUE;
anode->syn_arg[s_stt_value] = parse_value(0, 0);
}
parse_matching_paren();
return anode;
}
// Handle GDML statisticals
qli_syntax* node = syntax_node(ntypes->nod_t_node, s_stt_count);
if (node->syn_type != nod_count)
node->syn_arg[s_stt_value] = parse_value(0, 0);
if (!PAR_match(KW_OF)) {
if (sw_report) {
if (function_count > 0)
IBERROR(487); // Msg487 Invalid argument for UDF
node->syn_type = ntypes->nod_t_rpt_node;
return node;
}
PAR_real();
if (!PAR_match(KW_OF))
ERRQ_syntax(228); // Msg 228 OF
}
node->syn_arg[s_stt_rse] = parse_rse();
return node;
}
static qli_syntax* parse_store(void)
{
/**************************************
*
* p a r s e _ s t o r e
*
**************************************
*
* Functional description
* Parse a STORE statement.
*
**************************************/
PAR_token();
qli_syntax* node = syntax_node(nod_store, s_sto_count);
node->syn_arg[s_sto_relation] = parse_relation();
if (test_end())
return node;
PAR_match(KW_USING);
node->syn_arg[s_sto_statement] = parse_statement();
return node;
}
static TEXT *parse_string(void)
{
/**************************************
*
* p a r s e _ s t r i n g
*
**************************************
*
* Functional description
* Save the current token as a string, advance to the next
* token, and return a pointer to the string.
*
**************************************/
TEXT* string = make_string(QLI_token->tok_string, QLI_token->tok_length);
PAR_token();
return string;
}
static qli_symbol* parse_symbol(void)
{
/**************************************
*
* p a r s e _ s y m b o l
*
**************************************
*
* Functional description
* Parse the next token as a context symbol.
*
**************************************/
USHORT l = QLI_token->tok_length;
qli_symbol* context = (qli_symbol*) ALLOCDV(type_sym, l);
context->sym_type = SYM_context;
context->sym_length = l;
const TEXT* q = QLI_token->tok_string;
context->sym_string = context->sym_name;
TEXT* p = context->sym_name;
if (l)
do {
const TEXT c = *q++;
*p++ = UPPER(c);
} while (--l);
PAR_token();
return context;
}
static void parse_terminating_parens( USHORT * paren_count, USHORT * local_count)
{
/**************************************
*
* p a r s e _ t e r m i n a t i n g _ p a r e n s
*
**************************************
*
* Functional description
* Check for balancing parenthesis. If missing, barf.
*
**************************************/
if (*paren_count && paren_count == local_count)
do {
parse_matching_paren();
} while (--(*paren_count));
}
static qli_syntax* parse_transaction( NOD_T node_type)
{
/**************************************
*
* p a r s e _ t r a n s a c t i o n
*
**************************************
*
* Functional description
* Parse the FINISH, COMMIT, ROLLBACK,
* and PREPARE commands and the COMMIT statement.
*
**************************************/
qli_lls* stack = NULL;
PAR_token();
if (!KEYWORD(KW_SEMI))
while (true) {
qli_symbol* symbol;
for (symbol = QLI_token->tok_symbol; symbol;
symbol = symbol->sym_homonym)
{
if (symbol->sym_type == SYM_database)
break;
}
if (!symbol)
ERRQ_syntax(229); // Msg229 database name
ALLQ_push(symbol->sym_object, &stack);
PAR_token();
if (!PAR_match(KW_COMMA))
break;
}
command_end();
qli_syntax* node = make_list(stack);
node->syn_type = node_type;
return node;
}
static qli_syntax* parse_udf_or_field(void)
{
/**************************************
*
* p a r s e _ u d f _ o r _ f i e l d
*
**************************************
*
* Functional description
* Parse a function or field reference.
*
**************************************/
const qli_symbol* symbol = QLI_token->tok_symbol;
if (symbol && symbol->sym_type == SYM_function)
return parse_function();
return parse_field_name(0);
}
static qli_syntax* parse_update(void)
{
/**************************************
*
* p a r s e _ u p d a t e
*
**************************************
*
* Functional description
* Parse a SQL UPDATE statement.
*
**************************************/
++sql_flag;
PAR_token();
qli_syntax* node = syntax_node(nod_modify, s_mod_count);
qli_syntax* rse = syntax_node(nod_rse, (int) s_rse_count + 2);
node->syn_arg[s_mod_rse] = rse;
rse->syn_count = 1;
rse->syn_arg[s_rse_count] = parse_sql_relation();
if (!PAR_match(KW_SET))
ERRQ_syntax(230); // Msg230 SET
// Pick up assignments
qli_lls* stack = NULL;
while (true) {
ALLQ_push((blk*) parse_assignment(), &stack);
if (!PAR_match(KW_COMMA))
break;
}
// Pick up boolean, if present
if (PAR_match(KW_WITH))
rse->syn_arg[s_rse_boolean] = parse_boolean(0);
node->syn_arg[s_mod_statement] = make_list(stack);
--sql_flag;
return node;
}
static qli_syntax* parse_value( USHORT* paren_count, bool* bool_flag)
{
/**************************************
*
* p a r s e _ v a l u e
*
**************************************
*
* Functional description
* Parse a general value expression. In practice, this means parse the
* lowest precedence operator CONCATENATE.
*
**************************************/
USHORT local_count;
if (!paren_count) {
local_count = 0;
paren_count = &local_count;
}
bool local_flag;
if (!bool_flag) {
local_flag = false;
bool_flag = &local_flag;
}
qli_syntax* node = parse_add(paren_count, bool_flag);
while (true) {
if (!PAR_match(KW_BAR)) {
parse_terminating_parens(paren_count, &local_count);
return node;
}
qli_syntax* arg = node;
node = syntax_node(nod_concatenate, 2);
node->syn_arg[0] = arg;
node->syn_arg[1] = parse_add(paren_count, bool_flag);
}
}
static bool potential_rse(void)
{
/**************************************
*
* p o t e n t i a l _ r s e
*
**************************************
*
* Functional description
* Test to see if the current token is likely (sic!) to be part of
* a record selection expression.
*
**************************************/
for (const qli_symbol* symbol = QLI_token->tok_symbol; symbol;
symbol = symbol->sym_homonym)
{
if ((symbol->sym_type == SYM_keyword &&
symbol->sym_keyword == (int) KW_FIRST) ||
symbol->sym_type == SYM_relation ||
symbol->sym_type == SYM_database)
{
return true;
}
}
return false;
}
static qli_rel* resolve_relation( qli_symbol* db_symbol, qli_symbol* relation_symbol)
{
/**************************************
*
* r e s o l v e _ r e l a t i o n
*
**************************************
*
* Functional description
* Given symbols for a database and a relation (either may be null),
* resolve the relation. If the relation can't be resolved, return
* NULL (don't error!).
*
**************************************/
// If we don't recognize the relation, punt.
if (!relation_symbol)
return NULL;
/* If a database symbol is present, resolve the relation against the
the given database. */
if (db_symbol) { /* && db_symbol->sym_type == SYM_database ? */
for (; db_symbol; db_symbol = db_symbol->sym_homonym)
{
for (qli_symbol* temp = relation_symbol; temp; temp = temp->sym_homonym)
if (temp->sym_type == SYM_relation)
{
qli_rel* relation = (qli_rel*) temp->sym_object;
if (relation->rel_database == (DBB) db_symbol->sym_object)
return relation;
}
}
return NULL;
}
// No database qualifier, so search all databases.
for (DBB dbb = QLI_databases; dbb; dbb = dbb->dbb_next)
{
for (qli_symbol* temp = relation_symbol; temp; temp = temp->sym_homonym)
if (temp->sym_type == SYM_relation)
{
qli_rel* relation = (qli_rel*) temp->sym_object;
if (relation->rel_database == dbb)
return relation;
}
}
return NULL;
}
static qli_syntax* syntax_node( NOD_T type, USHORT count)
{
/**************************************
*
* s y n t a x _ n o d e
*
**************************************
*
* Functional description
* Allocate and initialize a syntax node of given type.
*
**************************************/
qli_syntax* node = (qli_syntax*) ALLOCDV(type_syn, count);
node->syn_type = type;
node->syn_count = count;
return node;
}
static bool test_end(void)
{
/**************************************
*
* t e s t _ e n d
*
**************************************
*
* Functional description
* Test for end of a statement. In specific, test for one of
* THEN, ELSE, ON, or a semi-colon.
*
**************************************/
if (KEYWORD(KW_THEN) ||
KEYWORD(KW_ON) || KEYWORD(KW_ELSE) || KEYWORD(KW_SEMI))
{
return true;
}
return false;
}