mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-02-02 02:40:43 +01:00
08f9f20489
It is possible now to copy table contents between databases, insert bigints via qli and show works nearly as expected. Displaying bigints and evaluation of expressions containing bigints still doesn't work.
5460 lines
120 KiB
C++
5460 lines
120 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"
|
|
|
|
#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 *, USHORT *);
|
|
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, NULL, NULL, NULL, NULL, NULL); // 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';
|
|
while (--length)
|
|
*p++ = *string++;
|
|
}
|
|
|
|
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);
|
|
TEXT* p = string->str_data;
|
|
if (length)
|
|
do {
|
|
*p++ = *address++;
|
|
} while (--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 operatr 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, NULL, NULL, NULL, NULL); // 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, NULL, NULL, NULL, NULL); // 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, NULL, NULL, NULL, NULL);
|
|
// Msg165 %s is not a database
|
|
|
|
relation = resolve_relation(db_name->nam_symbol, rel_name->nam_symbol);
|
|
if (!relation)
|
|
{
|
|
ERRQ_print_error(166, rel_name->nam_string,
|
|
db_name->nam_string, NULL, NULL, NULL);
|
|
// 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 relatio
|
|
* 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;
|
|
TEXT *p, *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, NULL, NULL, NULL, NULL, NULL); // 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;
|
|
p = database->dbb_filename;
|
|
do {
|
|
*p++ = *q++;
|
|
} while (--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;
|
|
|
|
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;
|
|
|
|
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++ = *q++;
|
|
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)
|
|
do {
|
|
*p++ = *q++;
|
|
} while (--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();
|
|
|
|
if (QLI_token->tok_type != tok_ident)
|
|
ERRQ_syntax(199); // Msg199 identifier
|
|
|
|
SSHORT l = QLI_token->tok_length;
|
|
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;
|
|
TEXT* p = name->nam_string;
|
|
|
|
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) {
|
|
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, QLI_token->tok_string, db_symbol->sym_string,
|
|
NULL, NULL, NULL); // 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, NULL, NULL, NULL, NULL, NULL);
|
|
// 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;
|
|
TEXT* p = database->dbb_filename;
|
|
do {
|
|
*p++ = *q++;
|
|
} while (--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, NULL, NULL, NULL, NULL);
|
|
// 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, NULL, NULL, NULL, NULL);
|
|
// 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 = (TEXT *) 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;
|
|
}
|
|
else if (PAR_match(KW_DESCENDING)) {
|
|
direction = 1;
|
|
continue;
|
|
}
|
|
else if (PAR_match(KW_EXACTCASE)) {
|
|
sensitive = false;
|
|
continue;
|
|
}
|
|
else 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)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p a r s e _ s q l _ d t y p e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse a SQL datatype clause.
|
|
*
|
|
**************************************/
|
|
USHORT dtype;
|
|
|
|
KWWORDS keyword = QLI_token->tok_keyword;
|
|
PAR_token();
|
|
*scale = 0;
|
|
*length = 1;
|
|
|
|
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"
|
|
case KW_DOUBLE:
|
|
case KW_PRECISION:
|
|
*length = sizeof(double);
|
|
dtype = dtype_double;
|
|
break;
|
|
|
|
case KW_DECIMAL:
|
|
*length = sizeof(SLONG);
|
|
dtype = dtype_long;
|
|
break;
|
|
}
|
|
|
|
if (dtype == dtype_long || dtype == dtype_real || dtype == dtype_double) {
|
|
if (PAR_match(KW_LEFT_PAREN)) {
|
|
const bool l = (PAR_match(KW_MINUS)) ? true : false;
|
|
*scale = parse_ordinal();
|
|
if (l)
|
|
*scale = -(*scale);
|
|
parse_matching_paren();
|
|
}
|
|
}
|
|
else if (dtype == dtype_text || dtype == dtype_varying) {
|
|
if (PAR_match(KW_LEFT_PAREN)) {
|
|
USHORT l = parse_ordinal();
|
|
if (dtype == dtype_varying)
|
|
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;
|
|
dtype = length = scale = 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:
|
|
dtype = parse_sql_dtype(&length, &scale);
|
|
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;
|
|
|
|
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;
|
|
}
|
|
else if (PAR_match(KW_INSERT)) {
|
|
privileges |= PRV_insert;
|
|
continue;
|
|
}
|
|
else if (PAR_match(KW_DELETE)) {
|
|
privileges |= PRV_delete;
|
|
continue;
|
|
}
|
|
else 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.
|
|
*
|
|
**************************************/
|
|
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;
|
|
|
|
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();
|
|
else 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.
|
|
*
|
|
**************************************/
|
|
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;
|
|
TEXT* p = context->sym_string = 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 operatr 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!).
|
|
*
|
|
**************************************/
|
|
qli_rel* relation;
|
|
qli_symbol* temp;
|
|
|
|
// 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 (temp = relation_symbol; temp; temp = temp->sym_homonym)
|
|
if (temp->sym_type == SYM_relation) {
|
|
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 (temp = relation_symbol; temp; temp = temp->sym_homonym)
|
|
if (temp->sym_type == SYM_relation) {
|
|
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;
|
|
}
|
|
|
|
|