8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-31 23:23:04 +01:00
firebird-mirror/src/qli/expand.cpp

3016 lines
77 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: JRD Command Oriented Query Language
* MODULE: expand.cpp
2001-05-23 15:26:42 +02:00
* DESCRIPTION: Expand syntax tree -- first phase of compiler
*
* 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"
2001-05-23 15:26:42 +02:00
#include <string.h>
#include "../qli/dtr.h"
#include "../qli/parse.h"
#include "../qli/compile.h"
#include "../qli/exe.h"
#include "../qli/report.h"
#include "../qli/all_proto.h"
#include "../qli/comma_proto.h"
#include "../qli/compi_proto.h"
#include "../qli/err_proto.h"
#include "../qli/expan_proto.h"
#include "../qli/help_proto.h"
#include "../qli/meta_proto.h"
#include "../qli/show_proto.h"
static bool compare_names(const nam*, const qli_symbol*);
static bool compare_symbols(const qli_symbol*, const qli_symbol*);
static qli_symbol* copy_symbol(const qli_symbol*);
2004-03-07 08:58:55 +01:00
static void declare_global(qli_fld*, qli_syntax*);
static qli_syntax* decompile_field(qli_fld*, qli_ctx*);
2004-02-02 12:02:12 +01:00
static NAM decompile_symbol(qli_symbol*);
2004-03-07 08:58:55 +01:00
static qli_nod* expand_assignment(qli_syntax*, qli_lls*, qli_lls*);
static qli_nod* expand_any(qli_syntax*, qli_lls*);
static qli_nod* expand_boolean(qli_syntax*, qli_lls*);
2004-02-02 12:02:12 +01:00
static void expand_control_break(qli_brk**, qli_lls*);
static void expand_distinct(qli_nod*, qli_nod*);
static void expand_edit_string(qli_nod*, qli_print_item*);
2004-03-07 08:58:55 +01:00
static qli_nod* expand_erase(qli_syntax*, qli_lls*, qli_lls*);
static qli_nod* expand_expression(qli_syntax*, qli_lls*);
static qli_nod* expand_field(qli_syntax*, qli_lls*, qli_syntax*);
static qli_nod* expand_for(qli_syntax*, qli_lls*, qli_lls*);
static qli_nod* expand_function(qli_syntax*, qli_lls*);
static qli_nod* expand_group_by(qli_syntax*, qli_lls*, qli_ctx*);
static qli_nod* expand_modify(qli_syntax*, qli_lls*, qli_lls*);
static qli_nod* expand_output(qli_syntax*, qli_lls*, qli_prt**);
static qli_nod* expand_print(qli_syntax*, qli_lls*, qli_lls*);
static qli_print_item* expand_print_item(qli_syntax*, qli_lls*);
static qli_nod* expand_print_list(qli_syntax*, qli_lls*);
static qli_nod* expand_report(qli_syntax*, qli_lls*, qli_lls*);
static qli_nod* expand_restructure(qli_syntax*, qli_lls*, qli_lls*);
static qli_nod* expand_rse(qli_syntax*, qli_lls**);
static qli_nod* expand_sort(qli_syntax*, qli_lls*, qli_nod*);
static qli_nod* expand_statement(qli_syntax*, qli_lls*, qli_lls*);
static qli_nod* expand_store(qli_syntax*, qli_lls*, qli_lls*);
static void expand_values(qli_syntax*, qli_lls*);
static qli_ctx* find_context(const nam*, qli_lls*);
2004-03-07 08:58:55 +01:00
static int generate_fields(qli_ctx*, qli_lls*, qli_syntax*);
static int generate_items(qli_syntax*, qli_lls*, qli_lls*, qli_nod*);
static bool global_agg(const qli_syntax*, const qli_syntax*);
static bool invalid_nod_field(const qli_nod*, const qli_nod*);
2004-03-07 08:58:55 +01:00
static bool invalid_syn_field(const qli_syntax*, const qli_syntax*);
2004-02-02 12:02:12 +01:00
static qli_nod* make_and(qli_nod*, qli_nod*);
static qli_nod* make_assignment(qli_nod*, qli_nod*, qli_lls*);
static qli_nod* make_field(qli_fld*, qli_ctx*);
static qli_nod* make_list(qli_lls*);
static qli_nod* make_node(NOD_T, USHORT);
static qli_nod* negate(qli_nod*);
2004-03-07 08:58:55 +01:00
static qli_nod* possible_literal(qli_syntax*, qli_lls*, bool);
2004-02-02 12:02:12 +01:00
static qli_nod* post_map(qli_nod*, qli_ctx*);
2004-03-07 08:58:55 +01:00
static qli_fld* resolve(qli_syntax*, qli_lls*, qli_ctx**);
static void resolve_really(qli_fld*, const qli_syntax*);
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
static qli_lls* global_output_stack;
2001-05-23 15:26:42 +02:00
2004-03-07 08:58:55 +01:00
qli_nod* EXP_expand( qli_syntax* node)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E X P _ e x p a n d
*
**************************************
*
* Functional description
* Expand a syntax tree into something richer and more complete.
*
**************************************/
switch (node->syn_type) {
case nod_commit:
case nod_prepare:
case nod_rollback:
CMD_transaction(node);
return NULL;
case nod_copy_proc:
CMD_copy_procedure(node);
return NULL;
case nod_declare:
2004-02-02 12:02:12 +01:00
declare_global((qli_fld*) node->syn_arg[0], node->syn_arg[1]);
2001-05-23 15:26:42 +02:00
return NULL;
case nod_define:
CMD_define_procedure(node);
return NULL;
case nod_delete_proc:
CMD_delete_proc(node);
return NULL;
case nod_def_database:
case nod_sql_database:
MET_ready(node, TRUE);
return NULL;
case nod_def_field:
2004-02-02 12:02:12 +01:00
MET_define_field((DBB) node->syn_arg[0], (qli_fld*) node->syn_arg[1]);
2001-05-23 15:26:42 +02:00
return NULL;
case nod_def_index:
MET_define_index(node);
return NULL;
case nod_def_relation:
2004-02-02 12:02:12 +01:00
MET_define_relation((qli_rel*)node->syn_arg[0], (qli_rel*) node->syn_arg[1]);
2001-05-23 15:26:42 +02:00
return NULL;
case nod_del_relation:
2004-02-02 12:02:12 +01:00
MET_delete_relation((qli_rel*)node->syn_arg[0]);
2001-05-23 15:26:42 +02:00
return NULL;
case nod_del_field:
2001-07-12 07:46:06 +02:00
MET_delete_field((DBB)node->syn_arg[0], (NAM) node->syn_arg[1]);
2001-05-23 15:26:42 +02:00
return NULL;
case nod_del_index:
2001-07-12 07:46:06 +02:00
MET_delete_index((DBB)node->syn_arg[0], (NAM) node->syn_arg[1]);
2001-05-23 15:26:42 +02:00
return NULL;
case nod_del_database:
2001-07-12 07:46:06 +02:00
MET_delete_database((DBB)node->syn_arg[0]);
2001-05-23 15:26:42 +02:00
return NULL;
case nod_edit_proc:
CMD_edit_proc(node);
return NULL;
case nod_extract:
2004-03-07 08:58:55 +01:00
node->syn_arg[1] = (qli_syntax*) expand_output(node->syn_arg[1], 0, 0);
2001-05-23 15:26:42 +02:00
CMD_extract(node);
return NULL;
case nod_finish:
CMD_finish(node);
return NULL;
case nod_help:
HELP_help(node);
return NULL;
case nod_mod_field:
2004-02-02 12:02:12 +01:00
MET_modify_field((DBB)node->syn_arg[0], (qli_fld*) node->syn_arg[1]);
2001-05-23 15:26:42 +02:00
return NULL;
case nod_mod_relation:
2004-02-02 12:02:12 +01:00
MET_modify_relation((qli_rel*) node->syn_arg[0], (qli_fld*) node->syn_arg[1]);
2001-05-23 15:26:42 +02:00
return NULL;
case nod_mod_index:
MET_modify_index(node);
return NULL;
case nod_ready:
MET_ready(node, FALSE);
return NULL;
case nod_rename_proc:
CMD_rename_proc(node);
return NULL;
case nod_set:
CMD_set(node);
return NULL;
case nod_show:
SHOW_stuff(node);
return NULL;
case nod_shell:
CMD_shell(node);
return NULL;
case nod_sql_grant:
MET_sql_grant(node);
return NULL;
case nod_sql_revoke:
MET_sql_revoke(node);
return NULL;
case nod_sql_cr_table:
2004-02-02 12:02:12 +01:00
MET_define_sql_relation((qli_rel*) node->syn_arg[0]);
2001-05-23 15:26:42 +02:00
return NULL;
/****
case nod_sql_cr_view:
MET_sql_cr_view (node);
GEN_release();
return NULL;
****/
case nod_sql_al_table:
2004-02-02 12:02:12 +01:00
MET_sql_alter_table((qli_rel*) node->syn_arg[0], (qli_fld*) node->syn_arg[1]);
2001-05-23 15:26:42 +02:00
return NULL;
} // end switch, no default case for error
2001-05-23 15:26:42 +02:00
// If there are any variables, make up a context now
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
global_output_stack = NULL;
qli_lls* right = NULL;
qli_lls* left = NULL;
2001-05-23 15:26:42 +02:00
if (QLI_variables) {
2004-02-02 12:02:12 +01:00
qli_ctx* context = (qli_ctx*) ALLOCD(type_ctx);
2001-05-23 15:26:42 +02:00
context->ctx_type = CTX_VARIABLE;
context->ctx_variable = QLI_variables;
2004-05-16 03:42:11 +02:00
ALLQ_push((blk*) context, &right);
ALLQ_push((blk*) context, &left);
2001-05-23 15:26:42 +02:00
}
2004-02-02 12:02:12 +01:00
qli_nod* expanded = expand_statement(node, right, left);
if (!expanded)
2001-05-23 15:26:42 +02:00
return NULL;
while (global_output_stack) {
2004-05-16 03:42:11 +02:00
qli_nod* output = (qli_nod*) ALLQ_pop(&global_output_stack);
2001-05-23 15:26:42 +02:00
output->nod_arg[e_out_statement] = expanded;
expanded = output;
}
return expanded;
}
static bool compare_names( const nam* name, const qli_symbol* symbol)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* c o m p a r e _ n a m e s
*
**************************************
*
* Functional description
2003-09-10 19:52:12 +02:00
* Compare a name node to a symbol. If they are equal, return true.
2001-05-23 15:26:42 +02:00
*
**************************************/
USHORT l;
if (!symbol || (l = name->nam_length) != symbol->sym_length)
2003-09-10 19:52:12 +02:00
return false;
2001-05-23 15:26:42 +02:00
if (l)
{
const TEXT* p = symbol->sym_string;
const TEXT* q = name->nam_string;
do {
2001-05-23 15:26:42 +02:00
if (*p++ != *q++)
2003-09-10 19:52:12 +02:00
return false;
} while (--l);
}
2001-05-23 15:26:42 +02:00
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
static bool compare_symbols( const qli_symbol* symbol1, const qli_symbol* symbol2)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* c o m p a r e _ s y m b o l s
*
**************************************
*
* Functional description
* Compare two symbols (either may be 0).
*
**************************************/
if (!symbol1 || !symbol2)
2003-09-10 19:52:12 +02:00
return false;
2001-05-23 15:26:42 +02:00
USHORT l = symbol1->sym_length;
if (l != symbol2->sym_length)
2003-09-10 19:52:12 +02:00
return false;
2001-05-23 15:26:42 +02:00
if (l)
{
const TEXT* p = symbol1->sym_string;
const TEXT* q = symbol2->sym_string;
do {
2001-05-23 15:26:42 +02:00
if (*p++ != *q++)
2003-09-10 19:52:12 +02:00
return false;
} while (--l);
}
2001-05-23 15:26:42 +02:00
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
static qli_symbol* copy_symbol( const qli_symbol* old)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* c o p y _ s y m b o l
*
**************************************
*
* Functional description
* Copy a symbol into the permanent pool.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_symbol* new_sym = (qli_symbol*) ALLOCPV(type_sym, old->sym_length);
2001-07-12 07:46:06 +02:00
new_sym->sym_length = old->sym_length;
new_sym->sym_type = old->sym_type;
new_sym->sym_string = new_sym->sym_name;
strcpy(new_sym->sym_name, old->sym_name);
2001-05-23 15:26:42 +02:00
2001-07-12 07:46:06 +02:00
return new_sym;
2001-05-23 15:26:42 +02:00
}
2004-03-07 08:58:55 +01:00
static void declare_global( qli_fld* variable, qli_syntax* field_node)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* d e c l a r e _ g l o b a l
*
**************************************
*
* Functional description
* Copy a variable block into the permanent pool as a field.
* Resolve BASED_ON references.
2001-05-23 15:26:42 +02:00
* Allocate all strings off the field block.
*
**************************************/
// If it's based_on, flesh it out & check datatype.
2001-05-23 15:26:42 +02:00
if (field_node) {
if (field_node->syn_type == nod_index)
field_node = field_node->syn_arg[s_idx_field];
resolve_really(variable, field_node);
if (variable->fld_dtype == dtype_blob)
IBERROR(137); // Msg137 variables may not be based on blob fields.
2001-05-23 15:26:42 +02:00
}
// Get rid of any other variables of the same name
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
qli_fld* field;
for (qli_fld** ptr = &QLI_variables; field = *ptr; ptr = &field->fld_next)
2001-05-23 15:26:42 +02:00
if (!strcmp
(field->fld_name->sym_string, variable->fld_name->sym_string))
{
2001-05-23 15:26:42 +02:00
*ptr = field->fld_next;
2001-07-12 07:46:06 +02:00
ALLQ_release((FRB) field->fld_name);
2001-05-23 15:26:42 +02:00
if (field->fld_query_name)
2001-07-12 07:46:06 +02:00
ALLQ_release((FRB) field->fld_query_name);
ALLQ_release((FRB) field);
2001-05-23 15:26:42 +02:00
break;
}
// Next, copy temporary field block into permanent pool. Fold edit_string
// query_header into main block to save space and complexity.
2001-05-23 15:26:42 +02:00
const TEXT* q;
USHORT l = variable->fld_length;
2001-05-23 15:26:42 +02:00
if (q = variable->fld_edit_string)
l += strlen(q);
if (q = variable->fld_query_header)
l += strlen(q);
2004-02-02 12:02:12 +01:00
qli_fld* new_fld = (qli_fld*) ALLOCPV(type_fld, l);
2001-07-12 07:46:06 +02:00
new_fld->fld_name = copy_symbol(variable->fld_name);
new_fld->fld_dtype = variable->fld_dtype;
new_fld->fld_length = variable->fld_length;
new_fld->fld_scale = variable->fld_scale;
new_fld->fld_sub_type = variable->fld_sub_type;
new_fld->fld_sub_type_missing = variable->fld_sub_type_missing;
new_fld->fld_flags = variable->fld_flags | FLD_missing;
2001-05-23 15:26:42 +02:00
// Copy query_name, edit string, query header
2001-05-23 15:26:42 +02:00
TEXT* p = (TEXT*) new_fld->fld_data + new_fld->fld_length;
2001-05-23 15:26:42 +02:00
if (q = variable->fld_edit_string) {
2001-07-12 07:46:06 +02:00
new_fld->fld_edit_string = p;
2001-05-23 15:26:42 +02:00
while (*p++ = *q++);
}
if (variable->fld_query_name)
2001-07-12 07:46:06 +02:00
new_fld->fld_query_name = copy_symbol(variable->fld_query_name);
2001-05-23 15:26:42 +02:00
if (q = variable->fld_query_header) {
2001-07-12 07:46:06 +02:00
new_fld->fld_query_header = p;
2001-05-23 15:26:42 +02:00
while (*p++ = *q++);
}
// Link new variable into variable chain
2001-05-23 15:26:42 +02:00
2001-07-12 07:46:06 +02:00
new_fld->fld_next = QLI_variables;
QLI_variables = new_fld;
2001-05-23 15:26:42 +02:00
}
2004-03-07 08:58:55 +01:00
static qli_syntax* decompile_field( qli_fld* field, qli_ctx* context)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* d e c o m p i l e _ f i e l d
*
**************************************
*
* Functional description
* Take a perfectly good, completely compiled
2004-03-07 08:58:55 +01:00
* field block and regress to a qli_syntax node and
* and a NAM block.
2001-05-23 15:26:42 +02:00
* (Needed to support SQL idiocies)
*
**************************************/
2003-10-16 10:51:06 +02:00
const int args = (context) ? 2 : 1;
2001-05-23 15:26:42 +02:00
2004-03-07 08:58:55 +01:00
qli_syntax* node = (qli_syntax*) ALLOCDV(type_syn, args);
2001-05-23 15:26:42 +02:00
node->syn_type = nod_field;
node->syn_count = args;
NAM name = decompile_symbol(field->fld_name);
2004-03-07 08:58:55 +01:00
node->syn_arg[0] = (qli_syntax*) name;
2001-05-23 15:26:42 +02:00
if (context) {
node->syn_arg[1] = node->syn_arg[0];
if (context->ctx_symbol)
name = decompile_symbol(context->ctx_symbol);
else
name = decompile_symbol(context->ctx_relation->rel_symbol);
2004-03-07 08:58:55 +01:00
node->syn_arg[0] = (qli_syntax*) name;
2001-05-23 15:26:42 +02:00
}
return node;
}
2004-02-02 12:02:12 +01:00
static NAM decompile_symbol( qli_symbol* symbol)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* d e c o m p i l e _ s y m b o l
*
**************************************
*
* Functional description
* Turn a symbol back into a name
* (Needed to support SQL idiocies)
*
**************************************/
2003-10-16 10:51:06 +02:00
int l = symbol->sym_length;
2001-05-23 15:26:42 +02:00
2003-10-16 10:51:06 +02:00
NAM name = (NAM) ALLOCDV(type_nam, l);
2001-05-23 15:26:42 +02:00
name->nam_length = l;
name->nam_symbol = symbol;
2003-10-16 10:51:06 +02:00
TEXT* p = name->nam_string;
const TEXT* q = symbol->sym_string;
2001-05-23 15:26:42 +02:00
if (l)
do {
2003-10-16 10:51:06 +02:00
const TEXT c = *q++;
2001-05-23 15:26:42 +02:00
*p++ = UPPER(c);
} while (--l);
return name;
}
2004-03-07 08:58:55 +01:00
static qli_nod* expand_assignment( qli_syntax* input, qli_lls* right, qli_lls* left)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ a s s i g n m e n t
*
**************************************
*
* Functional description
* Expand an assigment statement. All in all, not too tough.
*
**************************************/
2004-05-16 03:42:11 +02:00
qli_nod* node = make_node(input->syn_type, e_asn_count);
2004-02-02 12:02:12 +01:00
qli_nod* to = expand_expression(input->syn_arg[s_asn_to], left);
2003-10-16 10:51:06 +02:00
node->nod_arg[e_asn_to] = to;
2004-02-02 12:02:12 +01:00
qli_nod* from = expand_expression(input->syn_arg[s_asn_from], right);
2003-10-16 10:51:06 +02:00
node->nod_arg[e_asn_from] = from;
2001-05-23 15:26:42 +02:00
if (to->nod_type == nod_field || to->nod_type == nod_variable) {
2004-02-02 12:02:12 +01:00
qli_fld* field = (qli_fld*) to->nod_arg[e_fld_field];
2001-05-23 15:26:42 +02:00
if (field->fld_flags & FLD_computed) {
ERRQ_print_error(138, field->fld_name->sym_string, NULL, NULL,
NULL, NULL);
// Msg138 can't do assignment to computed field
2001-05-23 15:26:42 +02:00
}
if (from->nod_type == nod_prompt)
from->nod_arg[e_prm_field] = to->nod_arg[e_fld_field];
if (field->fld_validation)
node->nod_arg[e_asn_valid] =
expand_expression(field->fld_validation, left);
}
if (!node->nod_arg[e_asn_valid])
--node->nod_count;
return node;
}
2004-03-07 08:58:55 +01:00
static qli_nod* expand_any( qli_syntax* input, qli_lls* stack)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ a n y
*
**************************************
*
* Functional description
* Expand an any expression. This would be trivial were it not
* for a funny SQL case when an expression needs to be checked
* for existence.
*
**************************************/
2004-05-16 03:42:11 +02:00
qli_nod* node = make_node(input->syn_type, e_any_count);
2001-05-23 15:26:42 +02:00
node->nod_count = 0;
2004-02-02 12:02:12 +01:00
qli_nod* rse = expand_rse(input->syn_arg[0], &stack);
2003-10-16 10:51:06 +02:00
node->nod_arg[e_any_rse] = rse;
2001-05-23 15:26:42 +02:00
if (input->syn_count >= 2 && input->syn_arg[1]) {
2004-05-16 03:42:11 +02:00
qli_nod* boolean = make_node(nod_missing, 1);
2001-05-23 15:26:42 +02:00
boolean->nod_arg[0] = expand_expression(input->syn_arg[1], stack);
2004-05-16 03:42:11 +02:00
qli_nod* negation = make_node(nod_not, 1);
2001-05-23 15:26:42 +02:00
negation->nod_arg[0] = boolean;
rse->nod_arg[e_rse_boolean] =
make_and(rse->nod_arg[e_rse_boolean], negation);
}
return node;
}
2004-03-07 08:58:55 +01:00
static qli_nod* expand_boolean( qli_syntax* input, qli_lls* stack)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ b o o l e a n
*
**************************************
*
* Functional description
* Expand a statement.
*
**************************************/
// Make node and process arguments
2001-05-23 15:26:42 +02:00
2004-05-16 03:42:11 +02:00
qli_nod* node = make_node(input->syn_type, input->syn_count);
2004-02-02 12:02:12 +01:00
qli_nod** ptr = node->nod_arg;
qli_nod* value = expand_expression(input->syn_arg[0], stack);
2003-10-16 10:51:06 +02:00
*ptr++ = value;
2001-05-23 15:26:42 +02:00
2003-10-16 10:51:06 +02:00
SSHORT i;
2001-05-23 15:26:42 +02:00
for (i = 1; i < input->syn_count; i++, ptr++)
{
2003-09-10 19:52:12 +02:00
if (!(*ptr = possible_literal(input->syn_arg[i], stack, true)))
2001-05-23 15:26:42 +02:00
*ptr = expand_expression(input->syn_arg[i], stack);
}
2001-05-23 15:26:42 +02:00
// Try to match any prompts against fields to determine prompt length
2001-05-23 15:26:42 +02:00
if (value->nod_type != nod_field)
return node;
2004-02-02 12:02:12 +01:00
qli_fld* field = (qli_fld*) value->nod_arg[e_fld_field];
2001-05-23 15:26:42 +02:00
ptr = &node->nod_arg[1];
for (i = 1; i < node->nod_count; i++, ptr++)
{
2001-05-23 15:26:42 +02:00
if ((*ptr)->nod_type == nod_prompt)
2004-02-02 12:02:12 +01:00
(*ptr)->nod_arg[e_prm_field] = (qli_nod*) field;
}
2001-05-23 15:26:42 +02:00
return node;
}
2004-02-02 12:02:12 +01:00
static void expand_control_break( qli_brk** ptr, qli_lls* right)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ c o n t r o l _ b r e a k
*
**************************************
*
* Functional description
* Work on a report writer control break. This is called recursively
* to handle multiple breaks.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_brk* list = NULL;
2001-05-23 15:26:42 +02:00
qli_brk* control;
2001-05-23 15:26:42 +02:00
while (control = *ptr) {
*ptr = control->brk_next;
control->brk_next = list;
list = control;
if (control->brk_field)
control->brk_field =
2004-03-07 08:58:55 +01:00
(qli_syntax*) expand_expression(control->brk_field, right);
2001-05-23 15:26:42 +02:00
if (control->brk_line)
control->brk_line =
2004-03-07 08:58:55 +01:00
(qli_syntax*) expand_print_list(control->brk_line, right);
2001-05-23 15:26:42 +02:00
}
*ptr = list;
}
2004-02-02 12:02:12 +01:00
static void expand_distinct( qli_nod* rse, qli_nod* node)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ d i s t i n c t
*
**************************************
*
* Functional description
* We have run into a distinct count. Add a reduced
* clause to it's parent.
*
**************************************/
if (rse->nod_arg[e_rse_reduced])
return;
2004-02-02 12:02:12 +01:00
qli_lls* stack = NULL;
2004-05-16 03:42:11 +02:00
ALLQ_push((blk*) node, &stack);
ALLQ_push(0, &stack);
2004-02-02 12:02:12 +01:00
qli_nod* list = make_list(stack);
2003-10-16 10:51:06 +02:00
rse->nod_arg[e_rse_reduced] = list;
2001-05-23 15:26:42 +02:00
list->nod_count = 1;
}
2004-02-02 12:02:12 +01:00
static void expand_edit_string( qli_nod* node, qli_print_item* item)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ e d i t _ s t r i n g
*
**************************************
*
* Functional description
* Default edit_string and query_header.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_fun* function;
qli_map* map;
2001-05-23 15:26:42 +02:00
switch (node->nod_type) {
case nod_min:
case nod_rpt_min:
case nod_agg_min:
if (!item->itm_query_header)
item->itm_query_header = "MIN";
case nod_total:
case nod_running_total:
case nod_rpt_total:
case nod_agg_total:
if (!item->itm_query_header)
item->itm_query_header = "TOTAL";
case nod_average:
case nod_rpt_average:
case nod_agg_average:
if (!item->itm_query_header)
item->itm_query_header = "AVG";
case nod_max:
case nod_rpt_max:
case nod_agg_max:
if (!item->itm_query_header)
item->itm_query_header = "MAX";
expand_edit_string(node->nod_arg[e_stt_value], item);
return;
case nod_count:
case nod_running_count:
case nod_rpt_count:
case nod_agg_count:
if (!item->itm_edit_string)
item->itm_edit_string = "ZZZ,ZZZ,ZZ9";
if (!item->itm_query_header)
item->itm_query_header = "COUNT";
break;
case nod_map:
2004-02-02 12:02:12 +01:00
map = (qli_map*) node->nod_arg[e_map_map];
2001-05-23 15:26:42 +02:00
expand_edit_string(map->map_node, item);
return;
case nod_field:
case nod_variable:
break;
case nod_function:
2004-02-02 12:02:12 +01:00
function = (qli_fun*) node->nod_arg[e_fun_function];
2001-05-23 15:26:42 +02:00
if (!item->itm_query_header)
item->itm_query_header = function->fun_symbol->sym_string;
return;
default:
return;
}
// Handle fields
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
qli_fld* field = (qli_fld*) node->nod_arg[e_fld_field];
2001-05-23 15:26:42 +02:00
if (!item->itm_edit_string)
item->itm_edit_string = field->fld_edit_string;
if (!item->itm_query_header)
if (!(item->itm_query_header = field->fld_query_header))
item->itm_query_header = field->fld_name->sym_string;
}
2004-03-07 08:58:55 +01:00
static qli_nod* expand_erase( qli_syntax* input, qli_lls* right, qli_lls* left)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ e r a s e
*
**************************************
*
* Functional description
* Expand a statement.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_nod* loop = NULL;
2001-05-23 15:26:42 +02:00
// If there is an rse, make up a FOR loop
2001-05-23 15:26:42 +02:00
if (input->syn_arg[s_era_rse]) {
2004-05-16 03:42:11 +02:00
loop = make_node(nod_for, e_for_count);
2001-05-23 15:26:42 +02:00
loop->nod_arg[e_for_rse] = expand_rse(input->syn_arg[s_era_rse],
&right);
}
// Loop thru contexts counting them.
2003-10-16 10:51:06 +02:00
USHORT count = 0;
2004-02-02 12:02:12 +01:00
qli_ctx* context = NULL;
for (qli_lls* contexts = right; contexts; contexts = contexts->lls_next) {
context = (qli_ctx*) contexts->lls_object;
2001-05-23 15:26:42 +02:00
if (context->ctx_variable)
continue;
count++;
if (context->ctx_rse)
break;
}
if (count == 0)
IBERROR(139); // Msg139 no context for ERASE
2001-05-23 15:26:42 +02:00
else if (count > 1)
IBERROR(140); // Msg140 can't erase from a join
2001-05-23 15:26:42 +02:00
// Make up node big enough to hold fixed fields plus all contexts
2001-05-23 15:26:42 +02:00
2004-05-16 03:42:11 +02:00
qli_nod* node = make_node(nod_erase, e_era_count);
2004-02-02 12:02:12 +01:00
node->nod_arg[e_era_context] = (qli_nod*) context;
2001-05-23 15:26:42 +02:00
if (!loop)
return node;
loop->nod_arg[e_for_statement] = node;
return loop;
}
2004-03-07 08:58:55 +01:00
static qli_nod* expand_expression( qli_syntax* input, qli_lls* stack)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ e x p r e s s i o n
*
**************************************
*
* Functional description
* Expand an expression.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_nod* node;
qli_const* constant;
qli_ctx* context;
2001-05-23 15:26:42 +02:00
NAM name;
2004-03-07 08:58:55 +01:00
qli_syntax* value;
2001-05-23 15:26:42 +02:00
switch (input->syn_type) {
case nod_field:
return expand_field(input, stack, 0);
case nod_null:
case nod_user_name:
2004-05-16 03:42:11 +02:00
return make_node(input->syn_type, 0);
2001-05-23 15:26:42 +02:00
case nod_any:
case nod_unique:
return expand_any(input, stack);
case nod_max:
case nod_min:
case nod_count:
case nod_average:
case nod_total:
case nod_from:
case nod_rpt_max:
case nod_rpt_min:
case nod_rpt_count:
case nod_rpt_average:
case nod_rpt_total:
case nod_running_total:
case nod_running_count:
2004-05-16 03:42:11 +02:00
node = make_node(input->syn_type, e_stt_count);
2001-05-23 15:26:42 +02:00
if (value = input->syn_arg[s_stt_rse])
node->nod_arg[e_stt_rse] = expand_rse(value, &stack);
if (value = input->syn_arg[s_stt_value])
node->nod_arg[e_stt_value] = expand_expression(value, stack);
if (value = input->syn_arg[s_stt_default])
node->nod_arg[e_stt_default] = expand_expression(value, stack);
if (input->syn_arg[s_prt_distinct] && node->nod_arg[e_stt_rse]
&& node->nod_arg[e_stt_value])
expand_distinct(node->nod_arg[e_stt_rse],
node->nod_arg[e_stt_value]);
// count2 next 2 lines go
2001-05-23 15:26:42 +02:00
if (input->syn_type == nod_count)
node->nod_arg[e_stt_value] = 0;
return node;
case nod_agg_max:
case nod_agg_min:
case nod_agg_count:
case nod_agg_average:
case nod_agg_total:
2004-05-16 03:42:11 +02:00
node = make_node(input->syn_type, e_stt_count);
2001-05-23 15:26:42 +02:00
for (; stack; stack = stack->lls_next) {
2004-02-02 12:02:12 +01:00
context = (qli_ctx*) stack->lls_object;
2001-05-23 15:26:42 +02:00
if (context->ctx_type == CTX_AGGREGATE)
break;
}
if (!stack)
ERRQ_print_error(454, NULL, NULL, NULL, NULL, NULL);
// could not resolve context for aggregate
2001-05-23 15:26:42 +02:00
/* count2
if (value = input->syn_arg [s_stt_value])
{
node->nod_arg [e_stt_value] = expand_expression (value, stack->lls_next);
if (input->syn_arg [s_prt_distinct])
expand_distinct (context->ctx_sub_rse, node->nod_arg [e_stt_value]);
}
*/
if ((value = input->syn_arg[s_stt_value]) &&
(input->syn_arg[s_prt_distinct]
|| (input->syn_type != nod_agg_count)))
{
2001-05-23 15:26:42 +02:00
node->nod_arg[e_stt_value] =
expand_expression(value, stack->lls_next);
if (input->syn_arg[s_prt_distinct]
|| (input->syn_type == nod_agg_count && context->ctx_sub_rse))
{
2001-05-23 15:26:42 +02:00
expand_distinct(context->ctx_sub_rse,
node->nod_arg[e_stt_value]);
}
2001-05-23 15:26:42 +02:00
}
return post_map(node, context);
case nod_index:
value = input->syn_arg[s_idx_field];
if (value->syn_type != nod_field)
IBERROR(466); // Msg466 Only fields may be subscripted
2001-05-23 15:26:42 +02:00
return expand_field(value, stack, input->syn_arg[s_idx_subs]);
case nod_list:
case nod_upcase:
2005-05-28 00:45:31 +02:00
case nod_lowcase:
2001-05-23 15:26:42 +02:00
case nod_and:
case nod_or:
case nod_not:
case nod_missing:
case nod_add:
case nod_subtract:
case nod_multiply:
case nod_divide:
case nod_negate:
case nod_concatenate:
case nod_substr:
break;
case nod_eql:
case nod_neq:
case nod_gtr:
case nod_geq:
case nod_leq:
case nod_lss:
case nod_between:
case nod_matches:
case nod_sleuth:
case nod_like:
case nod_starts:
case nod_containing:
return expand_boolean(input, stack);
case nod_edit_blob:
2004-05-16 03:42:11 +02:00
node = make_node(input->syn_type, e_edt_count);
2001-05-23 15:26:42 +02:00
node->nod_count = 0;
if (input->syn_arg[0]) {
node->nod_count = 1;
node->nod_arg[0] = expand_expression(input->syn_arg[0], stack);
}
return node;
case nod_format:
2004-05-16 03:42:11 +02:00
node = make_node(input->syn_type, e_fmt_count);
2001-05-23 15:26:42 +02:00
node->nod_count = 1;
node->nod_arg[e_fmt_value] =
expand_expression(input->syn_arg[s_fmt_value], stack);
2004-02-02 12:02:12 +01:00
node->nod_arg[e_fmt_edit] = (qli_nod*) input->syn_arg[s_fmt_edit];
2001-05-23 15:26:42 +02:00
return node;
case nod_function:
return expand_function(input, stack);
case nod_constant:
2004-05-16 03:42:11 +02:00
node = make_node(input->syn_type, 0);
2004-02-02 12:02:12 +01:00
constant = (qli_const*) input->syn_arg[0];
2001-05-23 15:26:42 +02:00
node->nod_desc = constant->con_desc;
return node;
case nod_prompt:
2004-05-16 03:42:11 +02:00
node = make_node(input->syn_type, e_prm_count);
2004-02-02 12:02:12 +01:00
node->nod_arg[e_prm_prompt] = (qli_nod*) input->syn_arg[0];
2001-05-23 15:26:42 +02:00
return node;
case nod_star:
name = (NAM) input->syn_arg[0];
ERRQ_print_error(141, name->nam_string, NULL, NULL, NULL, NULL);
// Msg141 can't be used when a single element is required
2001-05-23 15:26:42 +02:00
default:
2004-05-16 03:42:11 +02:00
ERRQ_bugcheck(135); // Msg135 expand_expression: not yet implemented
2001-05-23 15:26:42 +02:00
}
2004-05-16 03:42:11 +02:00
node = make_node(input->syn_type, input->syn_count);
2004-02-02 12:02:12 +01:00
qli_nod** ptr = node->nod_arg;
2001-05-23 15:26:42 +02:00
for (SSHORT i = 0; i < input->syn_count; i++)
2001-05-23 15:26:42 +02:00
*ptr++ = expand_expression(input->syn_arg[i], stack);
return node;
}
2004-03-07 08:58:55 +01:00
static qli_nod* expand_field( qli_syntax* input, qli_lls* stack, qli_syntax* subs)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ f i e l d
*
**************************************
*
* Functional description
* Expand a field reference. Error if it can't be resolved.
* If the field belongs to a group by SQL expression, make
* sure it goes there.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_ctx* context;
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
qli_fld* field = resolve(input, stack, &context);
if (!field || (subs && (context->ctx_variable)))
2003-09-11 04:17:05 +02:00
{
TEXT s[160];
TEXT* p = s;
const TEXT* const limit = p + sizeof(s) - 1;
for (USHORT i = 0; i < input->syn_count; i++) {
NAM name = (NAM) input->syn_arg[i];
const TEXT* q = name->nam_string;
USHORT l = name->nam_length;
if (p < limit) {
if (l)
do {
*p++ = *q++;
} while (--l && p < limit);
*p++ = '.';
}
2001-05-23 15:26:42 +02:00
}
*--p = 0;
if (field)
ERRQ_print_error(467, s, NULL, NULL, NULL, NULL);
// Msg467 "%s" is not a field and so may not be subscripted
2001-05-23 15:26:42 +02:00
else
ERRQ_print_error(142, s, NULL, NULL, NULL, NULL);
// Msg142 "%s" is undefined or used out of context
2001-05-23 15:26:42 +02:00
}
2004-02-02 12:02:12 +01:00
qli_nod* node = make_field(field, context);
2001-05-23 15:26:42 +02:00
if (subs)
node->nod_arg[e_fld_subs] = expand_expression(subs, stack);
2004-02-02 12:02:12 +01:00
qli_ctx* parent = NULL;
qli_lls* save_stack;
2001-05-23 15:26:42 +02:00
for (save_stack = stack; stack; stack = stack->lls_next) {
2004-02-02 12:02:12 +01:00
parent = (qli_ctx*) stack->lls_object;
2001-05-23 15:26:42 +02:00
if (parent->ctx_type == CTX_AGGREGATE)
break;
}
if (!parent)
return node;
else if (context->ctx_parent != parent) {
/* The parent context may be hidden because we are part of
a stream context. Check out this possibility. */
for (; save_stack; save_stack = save_stack->lls_next) {
2004-02-02 12:02:12 +01:00
qli_ctx* stream_context = (qli_ctx*) save_stack->lls_object;
2001-05-23 15:26:42 +02:00
if (stream_context->ctx_type != CTX_STREAM ||
stream_context->ctx_stream->nod_type != nod_rse)
{
2001-05-23 15:26:42 +02:00
continue;
}
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
qli_ctx** ptr =
(qli_ctx**) stream_context->ctx_stream->nod_arg + e_rse_count;
const qli_ctx* const* const end =
ptr + stream_context->ctx_stream->nod_count;
2001-05-23 15:26:42 +02:00
for (; ptr < end; ptr++)
if (*ptr == context)
break;
if (ptr < end && stream_context->ctx_parent == parent)
break;
}
if (!save_stack)
return node;
}
return post_map(node, parent);
}
2004-03-07 08:58:55 +01:00
static qli_nod* expand_for( qli_syntax* input, qli_lls* right, qli_lls* left)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ f o r
*
**************************************
*
* Functional description
* Expand a statement.
*
**************************************/
2004-05-16 03:42:11 +02:00
qli_nod* node = make_node(input->syn_type, e_for_count);
2001-05-23 15:26:42 +02:00
node->nod_arg[e_for_rse] = expand_rse(input->syn_arg[s_for_rse], &right);
node->nod_arg[e_for_statement] =
expand_statement(input->syn_arg[s_for_statement], right, left);
return node;
}
2004-03-07 08:58:55 +01:00
static qli_nod* expand_function( qli_syntax* input, qli_lls* stack)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ f u n c t i o n
*
**************************************
*
* Functional description
* Expand a functionn reference.
* For a field or expression reference
* tied to a relation in a database
* use only that database. For a variable
* reference, use any database that matches.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_symbol* symbol = 0;
qli_fun* function = 0;
2001-05-23 15:26:42 +02:00
DBB database;
2004-05-16 03:42:11 +02:00
qli_nod* node = make_node(input->syn_type, e_fun_count);
2001-05-23 15:26:42 +02:00
node->nod_count = 1;
2004-02-02 12:02:12 +01:00
qli_ctx* context;
if (stack && (context = (qli_ctx*) stack->lls_object)
&& (context->ctx_type == CTX_RELATION))
{
2001-05-23 15:26:42 +02:00
if (context->ctx_primary)
context = context->ctx_primary;
database = context->ctx_relation->rel_database;
2004-02-02 12:02:12 +01:00
for (symbol = (qli_symbol*) input->syn_arg[s_fun_function]; symbol;
2003-09-13 13:48:09 +02:00
symbol = symbol->sym_homonym)
{
if (symbol->sym_type == SYM_function) {
2004-02-02 12:02:12 +01:00
function = (qli_fun*) symbol->sym_object;
2001-05-23 15:26:42 +02:00
if (function->fun_database == database)
break;
}
2003-09-13 13:48:09 +02:00
}
2001-05-23 15:26:42 +02:00
}
else
for (database = QLI_databases; database;
database = database->dbb_next)
{
2004-02-02 12:02:12 +01:00
for (symbol = (qli_symbol*) input->syn_arg[s_fun_function]; symbol;
2001-05-23 15:26:42 +02:00
symbol = symbol->sym_homonym)
{
2001-05-23 15:26:42 +02:00
if (symbol->sym_type == SYM_function) {
2004-02-02 12:02:12 +01:00
function = (qli_fun*) symbol->sym_object;
2001-05-23 15:26:42 +02:00
if (function->fun_database == database)
break;
}
}
2001-05-23 15:26:42 +02:00
if (symbol)
break;
}
if (!symbol) {
2004-02-02 12:02:12 +01:00
symbol = (qli_symbol*) input->syn_arg[s_fun_function];
2001-05-23 15:26:42 +02:00
ERRQ_error(412, symbol->sym_string, database->dbb_filename, NULL,
NULL, NULL);
}
2004-02-02 12:02:12 +01:00
node->nod_arg[e_fun_function] = (qli_nod*) function;
2001-05-23 15:26:42 +02:00
node->nod_arg[e_fun_args] =
expand_expression(input->syn_arg[s_fun_args], stack);
return node;
}
2004-03-07 08:58:55 +01:00
static qli_nod* expand_group_by( qli_syntax* input, qli_lls* stack, qli_ctx* context)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ g r o u p _ b y
*
**************************************
*
* Functional description
* Expand a GROUP BY clause.
*
**************************************/
2004-05-16 03:42:11 +02:00
qli_nod* node = make_node(input->syn_type, input->syn_count);
2004-02-02 12:02:12 +01:00
qli_nod** ptr2 = node->nod_arg;
2001-05-23 15:26:42 +02:00
2004-03-07 08:58:55 +01:00
qli_syntax** ptr = input->syn_arg;
for (const qli_syntax* const* const end = ptr + input->syn_count; ptr < end;
ptr++, ptr2++)
{
2001-05-23 15:26:42 +02:00
*ptr2 = expand_expression(*ptr, stack);
post_map(*ptr2, context);
}
return node;
}
2004-03-07 08:58:55 +01:00
static qli_nod* expand_modify( qli_syntax* input, qli_lls* right, qli_lls* left)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ m o d i f y
*
**************************************
*
* Functional description
* Expand a statement.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_nod* loop = NULL;
2001-05-23 15:26:42 +02:00
// If there is an rse, make up a FOR loop
2001-05-23 15:26:42 +02:00
if (input->syn_arg[s_mod_rse]) {
2004-05-16 03:42:11 +02:00
loop = make_node(nod_for, e_for_count);
2001-05-23 15:26:42 +02:00
loop->nod_arg[e_for_rse] = expand_rse(input->syn_arg[s_mod_rse],
&right);
}
2004-02-02 12:02:12 +01:00
qli_lls* contexts;
2001-05-23 15:26:42 +02:00
2003-10-16 10:51:06 +02:00
// Loop thru contexts counting them.
USHORT count = 0;
2001-05-23 15:26:42 +02:00
for (contexts = right; contexts; contexts = contexts->lls_next) {
2004-02-02 12:02:12 +01:00
qli_ctx* context = (qli_ctx*) contexts->lls_object;
2001-05-23 15:26:42 +02:00
if (context->ctx_variable)
continue;
2003-10-16 10:51:06 +02:00
++count;
2001-05-23 15:26:42 +02:00
if (context->ctx_rse)
break;
}
if (!count)
IBERROR(148); // Msg148 no context for modify
2001-05-23 15:26:42 +02:00
// Make up node big enough to hold fixed fields plus all contexts
2001-05-23 15:26:42 +02:00
2004-05-16 03:42:11 +02:00
qli_nod* node = make_node(nod_modify, (int) e_mod_count + count);
2001-05-23 15:26:42 +02:00
node->nod_count = count;
2004-02-02 12:02:12 +01:00
qli_nod** ptr = &node->nod_arg[e_mod_count];
2001-05-23 15:26:42 +02:00
// Loop thru contexts augmenting left context
2001-05-23 15:26:42 +02:00
for (contexts = right; contexts; contexts = contexts->lls_next) {
2004-02-02 12:02:12 +01:00
qli_ctx* context = (qli_ctx*) contexts->lls_object;
2001-05-23 15:26:42 +02:00
if (context->ctx_variable)
continue;
2004-02-02 12:02:12 +01:00
qli_ctx* new_context = (qli_ctx*) ALLOCD(type_ctx);
*ptr++ = (qli_nod*) new_context;
2001-05-23 15:26:42 +02:00
new_context->ctx_type = CTX_RELATION;
new_context->ctx_source = context;
new_context->ctx_symbol = context->ctx_symbol;
new_context->ctx_relation = context->ctx_relation;
2004-05-16 03:42:11 +02:00
ALLQ_push((blk*) new_context, &left);
2001-05-23 15:26:42 +02:00
if (context->ctx_rse)
break;
}
// Process sub-statement, list of fields, or, sigh, none of the above
2001-05-23 15:26:42 +02:00
2004-03-07 08:58:55 +01:00
qli_syntax* syn_list;
2001-05-23 15:26:42 +02:00
if (input->syn_arg[s_mod_statement])
node->nod_arg[e_mod_statement] =
expand_statement(input->syn_arg[s_mod_statement], right, left);
else if (syn_list = input->syn_arg[s_mod_list]) {
2004-05-16 03:42:11 +02:00
qli_nod* list = make_node(nod_list, syn_list->syn_count);
node->nod_arg[e_mod_statement] = list;
2001-05-23 15:26:42 +02:00
ptr = list->nod_arg;
2004-03-07 08:58:55 +01:00
qli_syntax** syn_ptr = syn_list->syn_arg;
for (USHORT i = 0; i < syn_list->syn_count; i++, syn_ptr++)
{
2004-03-07 08:58:55 +01:00
*ptr++ = make_assignment(expand_expression((qli_syntax*) *syn_ptr, left),
2004-02-02 12:02:12 +01:00
(qli_nod*) *syn_ptr, right);
}
2001-05-23 15:26:42 +02:00
}
else
IBERROR(149); // Msg149 field list required for modify
2001-05-23 15:26:42 +02:00
if (!loop)
return node;
loop->nod_arg[e_for_statement] = node;
return loop;
}
2004-03-07 08:58:55 +01:00
static qli_nod* expand_output( qli_syntax* input, qli_lls* right, qli_prt** print)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ o u t p u t
*
**************************************
*
* Functional description
* Handle the presence (or absence) of an output specification clause.
*
**************************************/
if (print)
2004-02-02 12:02:12 +01:00
*print = (qli_prt*) ALLOCD(type_prt);
2001-05-23 15:26:42 +02:00
if (!input)
return NULL;
2004-05-16 03:42:11 +02:00
qli_nod* output = make_node(nod_output, e_out_count);
ALLQ_push((blk*) output, &global_output_stack);
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
qli_nod* node = possible_literal(input->syn_arg[s_out_file], right, false);
if (!node)
2001-05-23 15:26:42 +02:00
node = expand_expression(input->syn_arg[s_out_file], right);
output->nod_arg[e_out_file] = node;
2004-02-02 12:02:12 +01:00
output->nod_arg[e_out_pipe] = (qli_nod*) input->syn_arg[s_out_pipe];
2001-05-23 15:26:42 +02:00
if (print)
2004-02-02 12:02:12 +01:00
output->nod_arg[e_out_print] = (qli_nod*) * print;
2001-05-23 15:26:42 +02:00
return output;
}
2004-03-07 08:58:55 +01:00
static qli_nod* expand_print( qli_syntax* input, qli_lls* right, qli_lls* left)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ p r i n t
*
**************************************
*
* Functional description
* Expand a statement.
*
**************************************/
2004-03-07 08:58:55 +01:00
qli_syntax* syn_rse = input->syn_arg[s_prt_rse];
2004-02-02 12:02:12 +01:00
qli_lls* new_right = right;
2001-05-23 15:26:42 +02:00
// If an output file or pipe is present, make up an output node
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
qli_prt* print;
2001-05-23 15:26:42 +02:00
expand_output(input->syn_arg[s_prt_output], right, &print);
// If a record select expression is present, expand it and build a FOR
// statement.
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
qli_nod* loop = NULL;
qli_nod* rse = NULL;
2001-05-23 15:26:42 +02:00
if (syn_rse) {
2004-05-16 03:42:11 +02:00
loop = make_node(nod_for, e_for_count);
2001-05-23 15:26:42 +02:00
loop->nod_arg[e_for_rse] = rse = expand_rse(syn_rse, &new_right);
}
/* If there were any print items, process them now. Look first for things that
look like items, but are actually lists. If there aren't items of any kind,
pick up all fields in the relations from the record selection expression. */
2004-02-02 12:02:12 +01:00
qli_lls* items = NULL;
2003-10-16 10:51:06 +02:00
USHORT count = 0;
2004-03-07 08:58:55 +01:00
qli_syntax* syn_list = input->syn_arg[s_prt_list];
if (syn_list) {
qli_syntax** sub = syn_list->syn_arg;
for (const qli_syntax* const* const end = sub + syn_list->syn_count;
sub < end; sub++)
{
2004-03-07 08:58:55 +01:00
qli_syntax* syn_item;
2001-05-23 15:26:42 +02:00
if (((*sub)->syn_type == nod_print_item)
&& (syn_item = (*sub)->syn_arg[s_itm_value])
&& (syn_item->syn_type == nod_star))
{
2004-02-02 12:02:12 +01:00
count += generate_items(syn_item, new_right, (qli_lls*) &items, rse);
}
2001-05-23 15:26:42 +02:00
else {
2004-05-16 03:42:11 +02:00
ALLQ_push((blk*) expand_print_item(*sub, new_right), &items);
2001-05-23 15:26:42 +02:00
count++;
}
}
2004-03-07 08:58:55 +01:00
}
else if (syn_rse && (syn_list = syn_rse->syn_arg[s_rse_reduced])) {
qli_syntax** sub = syn_list->syn_arg;
for (const qli_syntax* const* const end = sub + syn_list->syn_count;
sub < end; sub += 2)
{
2004-02-02 12:02:12 +01:00
qli_print_item* item = (qli_print_item*) ALLOCD(type_itm);
2001-05-23 15:26:42 +02:00
item->itm_type = item_value;
item->itm_value = expand_expression(*sub, new_right);
expand_edit_string(item->itm_value, item);
2004-05-16 03:42:11 +02:00
ALLQ_push((blk*) item, &items);
2001-05-23 15:26:42 +02:00
count++;
}
2004-03-07 08:58:55 +01:00
}
2001-05-23 15:26:42 +02:00
else
for (; new_right; new_right = new_right->lls_next) {
2004-02-02 12:02:12 +01:00
qli_ctx* context = (qli_ctx*) new_right->lls_object;
qli_rel* relation = context->ctx_relation;
2001-05-23 15:26:42 +02:00
if (!relation || context->ctx_sub_rse)
continue;
2004-02-02 12:02:12 +01:00
for (qli_fld* field = relation->rel_fields; field;
field = field->fld_next)
{
2001-05-23 15:26:42 +02:00
if (
(field->fld_system_flag
&& field->fld_system_flag != relation->rel_system_flag)
|| field->fld_flags & FLD_array)
{
2001-05-23 15:26:42 +02:00
continue;
}
2004-02-02 12:02:12 +01:00
qli_nod* node = make_field(field, context);
2001-05-23 15:26:42 +02:00
if (rse && rse->nod_arg[e_rse_group_by] &&
invalid_nod_field(node, rse->nod_arg[e_rse_group_by]))
{
2001-05-23 15:26:42 +02:00
continue;
}
2004-02-02 12:02:12 +01:00
qli_print_item* item = (qli_print_item*) ALLOCD(type_itm);
2001-05-23 15:26:42 +02:00
item->itm_type = item_value;
item->itm_value = make_field(field, context);
expand_edit_string(item->itm_value, item);
2004-05-16 03:42:11 +02:00
ALLQ_push((blk*) item, &items);
2001-05-23 15:26:42 +02:00
count++;
}
if (rse = context->ctx_rse)
break;
}
2004-03-07 08:58:55 +01:00
// If no print object showed up, complain!
2001-05-23 15:26:42 +02:00
if (!count)
IBERROR(150); // Msg150 No items in print list
2001-05-23 15:26:42 +02:00
/* Build new print statement. Unlike the syntax node, the print statement
has only print items in it. */
2004-05-16 03:42:11 +02:00
qli_nod* node = make_node(input->syn_type, e_prt_count);
2004-02-02 12:02:12 +01:00
qli_nod* list = make_list(items);
node->nod_arg[e_prt_list] = list;
2004-02-02 12:02:12 +01:00
node->nod_arg[e_prt_output] = (qli_nod*) print;
2001-05-23 15:26:42 +02:00
// If DISTINCT was requested, make up a reduced list.
2001-05-23 15:26:42 +02:00
if (rse && input->syn_arg[s_prt_distinct]) {
2004-05-16 03:42:11 +02:00
qli_nod* reduced = make_node(nod_list, list->nod_count * 2);
2001-05-23 15:26:42 +02:00
reduced->nod_count = 0;
2004-02-02 12:02:12 +01:00
qli_nod** ptr = reduced->nod_arg;
for (USHORT i = 0; i < list->nod_count; i++) {
2004-02-02 12:02:12 +01:00
qli_print_item* item = (qli_print_item*) list->nod_arg[i];
2001-05-23 15:26:42 +02:00
if (item->itm_value) {
*ptr++ = item->itm_value;
ptr++;
reduced->nod_count++;
}
}
if (reduced->nod_count)
rse->nod_arg[e_rse_reduced] = reduced;
}
// If a FOR loop was generated, splice it in here.
2001-05-23 15:26:42 +02:00
if (loop) {
loop->nod_arg[e_for_statement] = node;
node = loop;
if (input->syn_arg[s_prt_order])
rse->nod_arg[e_rse_sort] =
expand_sort(input->syn_arg[s_prt_order], new_right, list);
}
return node;
}
2004-03-07 08:58:55 +01:00
static qli_print_item* expand_print_item( qli_syntax* syn_item, qli_lls* right)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ p r i n t _ i t e m
*
**************************************
*
* Functional description
* Expand a print item. A print item can either be a value or a format
* specifier.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_print_item* item = (qli_print_item*) ALLOCD(type_itm);
2001-05-23 15:26:42 +02:00
switch (syn_item->syn_type)
{
2001-05-23 15:26:42 +02:00
case nod_print_item:
{
item->itm_type = item_value;
2004-03-07 08:58:55 +01:00
qli_syntax* syn_expr = syn_item->syn_arg[s_itm_value];
qli_nod* node = item->itm_value = expand_expression(syn_expr, right);
item->itm_edit_string = (TEXT *) syn_item->syn_arg[s_itm_edit_string];
item->itm_query_header = (TEXT *) syn_item->syn_arg[s_itm_header];
expand_edit_string(node, item);
return item;
}
2001-05-23 15:26:42 +02:00
case nod_column:
item->itm_type = item_column;
break;
case nod_tab:
item->itm_type = item_tab;
break;
case nod_space:
item->itm_type = item_space;
break;
case nod_skip:
item->itm_type = item_skip;
break;
case nod_new_page:
item->itm_type = item_new_page;
break;
case nod_column_header:
item->itm_type = item_column_header;
break;
case nod_report_header:
item->itm_type = item_report_header;
break;
}
2004-03-14 06:51:54 +01:00
item->itm_count = (IPTR) syn_item->syn_arg[0];
2001-05-23 15:26:42 +02:00
return item;
}
2004-03-07 08:58:55 +01:00
static qli_nod* expand_print_list( qli_syntax* input, qli_lls* stack)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ p r i n t _ l i s t
*
**************************************
*
* Functional description
* Expand a print list.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_lls* items = NULL;
2004-03-07 08:58:55 +01:00
qli_syntax** ptr = input->syn_arg;
2001-05-23 15:26:42 +02:00
2004-03-07 08:58:55 +01:00
for (const qli_syntax* const* const end = ptr + input->syn_count;
ptr < end; ptr++)
{
2004-05-16 03:42:11 +02:00
ALLQ_push((blk*) expand_print_item(*ptr, stack), &items);
2004-03-07 08:58:55 +01:00
}
2001-05-23 15:26:42 +02:00
return make_list(items);
}
2004-03-07 08:58:55 +01:00
static qli_nod* expand_report( qli_syntax* input, qli_lls* right, qli_lls* left)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ r e p o r t
*
**************************************
*
* Functional description
* Expand a report specification.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_prt* print;
2001-05-23 15:26:42 +02:00
// Start by processing record selection expression
2001-05-23 15:26:42 +02:00
expand_output(input->syn_arg[s_prt_output], right, &print);
2004-02-02 12:02:12 +01:00
qli_rpt* report = print->prt_report = (qli_rpt*) input->syn_arg[s_prt_list];
2001-05-23 15:26:42 +02:00
if (!(print->prt_lines_per_page = report->rpt_lines))
print->prt_lines_per_page = QLI_lines;
if (!report->rpt_columns)
report->rpt_columns = QLI_columns;
2004-05-16 03:42:11 +02:00
qli_nod* loop = make_node(nod_report_loop, e_for_count);
2001-05-23 15:26:42 +02:00
loop->nod_arg[e_for_rse] = expand_rse(input->syn_arg[s_prt_rse], &right);
2004-05-16 03:42:11 +02:00
qli_nod* node = make_node(nod_report, e_prt_count);
loop->nod_arg[e_for_statement] = node;
2004-02-02 12:02:12 +01:00
node->nod_arg[e_prt_list] = (qli_nod*) report;
node->nod_arg[e_prt_output] = (qli_nod*) print;
2001-05-23 15:26:42 +02:00
// Process clauses where they exist
2001-05-23 15:26:42 +02:00
expand_control_break(&report->rpt_top_rpt, right);
expand_control_break(&report->rpt_top_page, right);
expand_control_break(&report->rpt_top_breaks, right);
2004-03-07 08:58:55 +01:00
qli_syntax* sub = (qli_syntax*) report->rpt_detail_line;
if (sub)
2001-05-23 15:26:42 +02:00
report->rpt_detail_line = expand_print_list(sub, right);
expand_control_break(&report->rpt_bottom_breaks, right);
expand_control_break(&report->rpt_bottom_page, right);
expand_control_break(&report->rpt_bottom_rpt, right);
return loop;
}
2004-03-07 08:58:55 +01:00
static qli_nod* expand_restructure( qli_syntax* input, qli_lls* right, qli_lls* left)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ r e s t r u c t u r e
*
**************************************
*
* Functional description
* Transform a restructure statement into a FOR <rse> STORE.
*
**************************************/
// Make a FOR loop to drive the restructure
2001-05-23 15:26:42 +02:00
2004-05-16 03:42:11 +02:00
qli_nod* loop = make_node(nod_for, e_for_count);
2001-05-23 15:26:42 +02:00
loop->nod_arg[e_for_rse] = expand_rse(input->syn_arg[s_asn_from], &right);
// Make a STORE node.
2001-05-23 15:26:42 +02:00
2004-05-16 03:42:11 +02:00
qli_nod* node = make_node(nod_store, e_sto_count);
loop->nod_arg[e_for_statement] = node;
2004-03-07 08:58:55 +01:00
qli_syntax* rel_node = input->syn_arg[s_asn_to];
2004-02-02 12:02:12 +01:00
qli_ctx* context = (qli_ctx*) ALLOCD(type_ctx);
node->nod_arg[e_sto_context] = (qli_nod*) context;
2001-05-23 15:26:42 +02:00
context->ctx_type = CTX_RELATION;
2004-02-02 12:02:12 +01:00
context->ctx_rse = (qli_nod*) -1;
qli_rel* relation = context->ctx_relation =
(qli_rel*) rel_node->syn_arg[s_rel_relation];
2001-05-23 15:26:42 +02:00
// If we don't already know about the relation, find out now.
2001-05-23 15:26:42 +02:00
if (!(relation->rel_flags & REL_fields))
MET_fields(relation);
/* Match fields in target relation against fields in the input rse. Fields
may match on either name or query name. */
2004-02-02 12:02:12 +01:00
qli_lls* stack = NULL;
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
for (qli_fld* field = relation->rel_fields; field; field = field->fld_next)
if (!(field->fld_flags & FLD_computed))
{
2004-02-02 12:02:12 +01:00
for (qli_lls* search = right; search; search = search->lls_next) {
qli_ctx* ctx = (qli_ctx*) search->lls_object;
2001-05-23 15:26:42 +02:00
// First look for an exact field name match
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
qli_fld* fld;
2001-05-23 15:26:42 +02:00
for (fld = ctx->ctx_relation->rel_fields; fld;
fld = fld->fld_next)
2003-09-10 19:52:12 +02:00
{
if (compare_symbols(field->fld_name, fld->fld_name))
break;
}
// Next try, target field name matching source query name
2001-05-23 15:26:42 +02:00
if (!fld)
for (fld = ctx->ctx_relation->rel_fields; fld;
2003-09-10 19:52:12 +02:00
fld = fld->fld_next)
{
if (compare_symbols(field->fld_name, fld->fld_query_name))
break;
}
// If nothing yet, look for any old match
2001-05-23 15:26:42 +02:00
if (!fld)
for (fld = ctx->ctx_relation->rel_fields; fld;
2003-09-10 19:52:12 +02:00
fld = fld->fld_next)
{
if (compare_symbols(field-> fld_query_name,
fld->fld_name)
|| compare_symbols(field->fld_query_name,
fld->fld_query_name))
{
2001-05-23 15:26:42 +02:00
break;
2003-09-10 19:52:12 +02:00
}
}
2001-05-23 15:26:42 +02:00
if (fld) {
2004-05-16 03:42:11 +02:00
qli_nod* assignment = make_node(nod_assign, e_asn_count);
2001-05-23 15:26:42 +02:00
assignment->nod_count = e_asn_count - 1;
assignment->nod_arg[e_asn_to] =
make_field(field, context);
assignment->nod_arg[e_asn_from] = make_field(fld, ctx);
2004-05-16 03:42:11 +02:00
ALLQ_push((blk*) assignment, &stack);
2001-05-23 15:26:42 +02:00
goto found_field;
}
if (ctx->ctx_rse)
break;
}
found_field:;
2001-05-23 15:26:42 +02:00
}
node->nod_arg[e_sto_statement] = make_list(stack);
return loop;
}
2004-03-07 08:58:55 +01:00
static qli_nod* expand_rse( qli_syntax* input, qli_lls** stack)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ r s e
*
**************************************
*
* Functional description
* Expand a record selection expression, returning an updated context
* stack.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_lls* old_stack = *stack;
qli_lls* new_stack = *stack;
qli_nod* boolean = NULL;
2004-05-16 03:42:11 +02:00
qli_nod* node = make_node(input->syn_type, (int) e_rse_count + input->syn_count);
2001-05-23 15:26:42 +02:00
node->nod_count = input->syn_count;
2004-02-02 12:02:12 +01:00
qli_nod** ptr2 = &node->nod_arg[e_rse_count];
2001-05-23 15:26:42 +02:00
/* Decide whether or not this is a GROUP BY, real or imagined
If it is, disallow normal field type references */
2004-02-02 12:02:12 +01:00
qli_ctx* parent_context = NULL;
qli_nod* parent_rse = NULL;
2001-05-23 15:26:42 +02:00
if (input->syn_arg[s_rse_group_by] || input->syn_arg[s_rse_having])
2004-02-02 12:02:12 +01:00
parent_context = (qli_ctx*) ALLOCD(type_ctx);
2004-03-07 08:58:55 +01:00
qli_syntax* list = input->syn_arg[s_rse_list];
if (list) {
for (USHORT i = 0; i < list->syn_count; i++) {
2004-03-07 08:58:55 +01:00
const qli_syntax* value = list->syn_arg[i];
const qli_syntax* field = value->syn_arg[e_itm_value];
if (!field)
2001-05-23 15:26:42 +02:00
continue;
if (global_agg(field, input->syn_arg[s_rse_group_by])) {
if (!parent_context)
2004-02-02 12:02:12 +01:00
parent_context = (qli_ctx*) ALLOCD(type_ctx);
2001-05-23 15:26:42 +02:00
}
else if (parent_context)
if (invalid_syn_field(field, input->syn_arg[s_rse_group_by]))
IBERROR(451);
}
}
if (parent_context) {
parent_context->ctx_type = CTX_AGGREGATE;
2004-05-16 03:42:11 +02:00
parent_rse = make_node(nod_rse, e_rse_count + 1);
2001-05-23 15:26:42 +02:00
parent_rse->nod_count = 1;
2004-02-02 12:02:12 +01:00
parent_rse->nod_arg[e_rse_count] = (qli_nod*) parent_context;
2001-05-23 15:26:42 +02:00
parent_context->ctx_sub_rse = node;
}
// Process the FIRST clause before the context gets augmented
2001-05-23 15:26:42 +02:00
if (input->syn_arg[s_rse_first])
node->nod_arg[e_rse_first] =
expand_expression(input->syn_arg[e_rse_first], old_stack);
// Process relations
2001-05-23 15:26:42 +02:00
2004-03-07 08:58:55 +01:00
qli_syntax** ptr = input->syn_arg + s_rse_count;
2001-05-23 15:26:42 +02:00
for (USHORT i = 0; i < input->syn_count; i++) {
2004-03-07 08:58:55 +01:00
qli_syntax* rel_node = *ptr++;
qli_syntax* over = *ptr++;
2004-02-02 12:02:12 +01:00
qli_ctx* context = (qli_ctx*) ALLOCD(type_ctx);
*ptr2++ = (qli_nod*) context;
2001-05-23 15:26:42 +02:00
if (i == 0)
context->ctx_rse = node;
if (rel_node->syn_type == nod_rse) {
context->ctx_type = CTX_STREAM;
context->ctx_stream = expand_rse(rel_node, &new_stack);
}
else {
context->ctx_type = CTX_RELATION;
2004-02-02 12:02:12 +01:00
qli_rel* relation = context->ctx_relation =
(qli_rel*) rel_node->syn_arg[s_rel_relation];
2001-05-23 15:26:42 +02:00
if (!(relation->rel_flags & REL_fields))
MET_fields(relation);
2004-02-02 12:02:12 +01:00
qli_symbol* symbol = context->ctx_symbol =
(qli_symbol*) rel_node->syn_arg[s_rel_context];
2001-05-23 15:26:42 +02:00
if (symbol)
symbol->sym_object = (BLK) context;
if (over) {
2004-02-02 12:02:12 +01:00
qli_lls* short_stack = NULL;
2004-05-16 03:42:11 +02:00
ALLQ_push((blk*) context, &short_stack);
for (USHORT j = 0; j < over->syn_count; j++) {
2004-03-07 08:58:55 +01:00
qli_syntax* field = over->syn_arg[j];
2004-05-16 03:42:11 +02:00
qli_nod* eql_node = make_node(nod_eql, 2);
2001-05-23 15:26:42 +02:00
eql_node->nod_arg[0] =
expand_expression(field, short_stack);
eql_node->nod_arg[1] =
expand_expression(field, new_stack);
boolean = make_and(eql_node, boolean);
}
2004-05-16 03:42:11 +02:00
ALLQ_pop(&short_stack);
2001-05-23 15:26:42 +02:00
}
}
2004-05-16 03:42:11 +02:00
ALLQ_push((blk*) context, &new_stack);
2001-05-23 15:26:42 +02:00
}
// Handle explicit boolean
2001-05-23 15:26:42 +02:00
if (input->syn_arg[e_rse_boolean])
boolean = make_and(boolean,
expand_expression(input->syn_arg[e_rse_boolean],
new_stack));
2004-03-07 08:58:55 +01:00
// Handle implicit boolean from SQL xxx IN (yyy FROM relation)
2001-05-23 15:26:42 +02:00
if (input->syn_arg[s_rse_outer]) {
2004-05-16 03:42:11 +02:00
qli_nod* eql_node = make_node((enum nod_t)(IPTR)input->syn_arg[s_rse_op], 2);
2001-05-23 15:26:42 +02:00
eql_node->nod_arg[0] =
expand_expression(input->syn_arg[s_rse_outer], old_stack);
eql_node->nod_arg[1] =
expand_expression(input->syn_arg[s_rse_inner], new_stack);
if (input->syn_arg[s_rse_all_flag])
eql_node = negate(eql_node);
boolean = make_and(eql_node, boolean);
}
node->nod_arg[e_rse_boolean] = boolean;
if (input->syn_arg[s_rse_sort]) {
2004-02-02 12:02:12 +01:00
qli_nod* temp = expand_sort(input->syn_arg[e_rse_sort], new_stack, 0);
2001-05-23 15:26:42 +02:00
if (parent_rse)
parent_rse->nod_arg[e_rse_sort] = temp;
else
node->nod_arg[e_rse_sort] = temp;
}
#ifdef PC_ENGINE
else if (input->syn_arg[s_rse_index])
2004-02-02 12:02:12 +01:00
node->nod_arg[e_rse_index] = (qli_nod*) input->syn_arg[s_rse_index];
2001-05-23 15:26:42 +02:00
#endif
if (input->syn_arg[s_rse_reduced])
node->nod_arg[e_rse_reduced] =
expand_sort(input->syn_arg[e_rse_reduced], new_stack, 0);
if (input->syn_arg[s_rse_group_by])
parent_rse->nod_arg[e_rse_group_by] =
expand_group_by(input->syn_arg[s_rse_group_by], new_stack,
parent_context);
2004-02-02 12:02:12 +01:00
node->nod_arg[e_rse_join_type] = (qli_nod*) input->syn_arg[s_rse_join_type];
2001-05-23 15:26:42 +02:00
// If there is a parent context, set it up here
2001-05-23 15:26:42 +02:00
*stack = new_stack;
if (!parent_context)
return node;
2004-02-02 12:02:12 +01:00
qli_ctx* context = NULL;
ptr2 = node->nod_arg + e_rse_count;
for (const qli_nod* const* const end = ptr2 + node->nod_count;
ptr2 < end; ptr2++)
{
2004-02-02 12:02:12 +01:00
context = (qli_ctx*) *ptr2;
2001-05-23 15:26:42 +02:00
context->ctx_parent = parent_context;
}
if (!(parent_context->ctx_relation = context->ctx_relation))
parent_context->ctx_stream = context->ctx_stream;
2004-05-16 03:42:11 +02:00
ALLQ_push((blk*) parent_context, stack);
2001-05-23 15:26:42 +02:00
if (input->syn_arg[s_rse_having])
parent_rse->nod_arg[e_rse_having] =
expand_expression(input->syn_arg[s_rse_having], *stack);
return parent_rse;
}
2004-03-07 08:58:55 +01:00
static qli_nod* expand_sort( qli_syntax* input, qli_lls* stack, qli_nod* list)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ s o r t
*
**************************************
*
* Functional description
* Expand a sort or reduced clause. This is more than a little
* kludgy. For pure undiscipled, pragmatic reasons, the count for
* a sort/ reduced clause for a syntax node is twice the number of
* actual keys. For node nodes, however, the count is the accurate
* number of keys. So be careful.
*
**************************************/
2004-05-16 03:42:11 +02:00
qli_nod* node = make_node(nod_list, input->syn_count);
2001-05-23 15:26:42 +02:00
node->nod_count = input->syn_count / 2;
2004-02-02 12:02:12 +01:00
qli_nod** ptr = node->nod_arg;
2004-03-07 08:58:55 +01:00
qli_syntax** syn_ptr = input->syn_arg;
2001-05-23 15:26:42 +02:00
for (USHORT i = 0; i < node->nod_count; i++) {
2004-03-07 08:58:55 +01:00
qli_syntax* expr = *syn_ptr++;
if (expr->syn_type == nod_position) {
2004-03-14 06:51:54 +01:00
const IPTR position = (IPTR) expr->syn_arg[0];
2001-05-23 15:26:42 +02:00
if (!list || !position || position > list->nod_count)
IBERROR(152); // Msg152 invalid ORDER BY ordinal
2004-02-02 12:02:12 +01:00
qli_print_item* item = (qli_print_item*) list->nod_arg[position - 1];
2001-05-23 15:26:42 +02:00
*ptr++ = item->itm_value;
}
else
*ptr++ = expand_expression(expr, stack);
2004-02-02 12:02:12 +01:00
*ptr++ = (qli_nod*) * syn_ptr++;
2001-05-23 15:26:42 +02:00
}
return node;
}
2004-03-07 08:58:55 +01:00
static qli_nod* expand_statement( qli_syntax* input, qli_lls* right, qli_lls* left)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ s t a t e m e n t
*
**************************************
*
* Functional description
* Expand a statement.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_nod* node;
2004-03-07 08:58:55 +01:00
qli_nod* (*routine) (qli_syntax*, qli_lls*, qli_lls*);
2001-05-23 15:26:42 +02:00
switch (input->syn_type) {
case nod_abort:
2004-05-16 03:42:11 +02:00
node = make_node(input->syn_type, input->syn_count);
2001-05-23 15:26:42 +02:00
if (input->syn_arg[0])
node->nod_arg[0] = expand_expression(input->syn_arg[0], right);
return node;
case nod_assign:
routine = expand_assignment;
break;
case nod_commit_retaining:
{
2004-05-16 03:42:11 +02:00
node = make_node(input->syn_type, input->syn_count);
for (USHORT i = 0; i < input->syn_count; i++)
node->nod_arg[i] = (qli_nod*) input->syn_arg[i];
return node;
}
2001-05-23 15:26:42 +02:00
case nod_erase:
routine = expand_erase;
break;
case nod_for:
routine = expand_for;
break;
case nod_if:
2004-05-16 03:42:11 +02:00
node = make_node(input->syn_type, input->syn_count);
2001-05-23 15:26:42 +02:00
node->nod_arg[e_if_boolean] =
expand_expression(input->syn_arg[s_if_boolean], right);
node->nod_arg[e_if_true] =
expand_statement(input->syn_arg[s_if_true], right, left);
if (input->syn_arg[s_if_false])
node->nod_arg[e_if_false] =
expand_statement(input->syn_arg[s_if_false], right, left);
else
node->nod_count = 2;
return node;
case nod_modify:
routine = expand_modify;
break;
case nod_print:
case nod_list_fields:
routine = expand_print;
break;
case nod_report:
routine = expand_report;
break;
case nod_restructure:
routine = expand_restructure;
break;
case nod_store:
routine = expand_store;
break;
case nod_repeat:
2004-05-16 03:42:11 +02:00
node = make_node(input->syn_type, input->syn_count);
2001-05-23 15:26:42 +02:00
node->nod_arg[e_rpt_value] =
expand_expression(input->syn_arg[s_rpt_value], left);
node->nod_arg[e_rpt_statement] =
expand_statement(input->syn_arg[s_rpt_statement], right, left);
return node;
case nod_list:
{
2004-03-07 08:58:55 +01:00
qli_syntax** syn_ptr = input->syn_arg;
qli_lls* stack = NULL;
for (USHORT i = 0; i < input->syn_count; i++) {
2004-03-07 08:58:55 +01:00
qli_syntax* syn_node = *syn_ptr++;
if (syn_node->syn_type == nod_declare) {
qli_ctx* context = (qli_ctx*) ALLOCD(type_ctx);
context->ctx_type = CTX_VARIABLE;
2004-03-07 08:58:55 +01:00
qli_syntax* field_node = syn_node->syn_arg[1];
if (field_node) {
if (field_node->syn_type == nod_index)
field_node = field_node->syn_arg[s_idx_field];
resolve_really((qli_fld*) syn_node->syn_arg[0], field_node);
}
context->ctx_variable = (qli_fld*) syn_node->syn_arg[0];
2004-05-16 03:42:11 +02:00
ALLQ_push((blk*) context, &right);
ALLQ_push((blk*) context, &left);
2001-05-23 15:26:42 +02:00
}
else if (node = expand_statement(syn_node, right, left))
2004-05-16 03:42:11 +02:00
ALLQ_push((blk*) node, &stack);
2001-05-23 15:26:42 +02:00
}
return make_list(stack);
2001-05-23 15:26:42 +02:00
}
case nod_declare:
return NULL;
default:
2004-05-16 03:42:11 +02:00
ERRQ_bugcheck(136); // Msg136 expand_statement: not yet implemented
2001-05-23 15:26:42 +02:00
}
return (*routine) (input, right, left);
}
2004-03-07 08:58:55 +01:00
static qli_nod* expand_store( qli_syntax* input, qli_lls* right, qli_lls* left)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ s t o r e
*
**************************************
*
* Functional description
* Process, yea expand, on a mere STORE statement. Make us
* something neat if nothing looks obvious.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_nod* loop = NULL;
2001-05-23 15:26:42 +02:00
// If there is an rse, make up a FOR loop
2001-05-23 15:26:42 +02:00
if (input->syn_arg[s_sto_rse]) {
2004-05-16 03:42:11 +02:00
loop = make_node(nod_for, e_for_count);
2001-05-23 15:26:42 +02:00
loop->nod_arg[e_for_rse] = expand_rse(input->syn_arg[s_sto_rse],
&right);
}
2004-05-16 03:42:11 +02:00
qli_nod* node = make_node(input->syn_type, e_sto_count);
2001-05-23 15:26:42 +02:00
2004-03-07 08:58:55 +01:00
qli_syntax* rel_node = input->syn_arg[s_sto_relation];
2004-02-02 12:02:12 +01:00
qli_ctx* context = (qli_ctx*) ALLOCD(type_ctx);
node->nod_arg[e_sto_context] = (qli_nod*) context;
2001-05-23 15:26:42 +02:00
context->ctx_type = CTX_RELATION;
2004-02-02 12:02:12 +01:00
context->ctx_rse = (qli_nod*) -1;
qli_rel* relation = context->ctx_relation =
(qli_rel*) rel_node->syn_arg[s_rel_relation];
2001-05-23 15:26:42 +02:00
if (!(relation->rel_flags & REL_fields))
MET_fields(relation);
2004-02-02 12:02:12 +01:00
qli_symbol* symbol = context->ctx_symbol = (qli_symbol*) rel_node->syn_arg[s_rel_context];
if (symbol)
2001-05-23 15:26:42 +02:00
symbol->sym_object = (BLK) context;
2004-05-16 03:42:11 +02:00
ALLQ_push((blk*) context, &left);
2001-05-23 15:26:42 +02:00
// If there are field and value lists, process them
2001-05-23 15:26:42 +02:00
if (input->syn_arg[s_sto_values]) {
if (!input->syn_arg[s_sto_fields]) {
2004-02-02 12:02:12 +01:00
qli_lls* stack = NULL;
for (qli_fld* field = relation->rel_fields; field;
field = field->fld_next)
{
2004-05-16 03:42:11 +02:00
ALLQ_push((blk*) decompile_field(field, 0), &stack);
}
2004-03-07 08:58:55 +01:00
input->syn_arg[s_sto_fields] = (qli_syntax*) stack;
2001-05-23 15:26:42 +02:00
}
expand_values(input, right);
}
/* Process sub-statement. If there isn't one, make up a series of
assignments. */
if (input->syn_arg[s_sto_statement]) {
2004-02-02 12:02:12 +01:00
qli_ctx* secondary = (qli_ctx*) ALLOCD(type_ctx);
2001-05-23 15:26:42 +02:00
secondary->ctx_type = CTX_RELATION;
secondary->ctx_primary = context;
2004-05-16 03:42:11 +02:00
ALLQ_push((blk*) secondary, &right);
2001-05-23 15:26:42 +02:00
node->nod_arg[e_sto_statement] =
expand_statement(input->syn_arg[s_sto_statement], right, left);
}
else {
2004-02-02 12:02:12 +01:00
qli_lls* stack = NULL;
for (qli_fld* field = relation->rel_fields; field;
field = field->fld_next)
{
2001-05-23 15:26:42 +02:00
if (field->fld_flags & FLD_computed)
continue;
if (
(field->fld_system_flag
&& field->fld_system_flag != relation->rel_system_flag)
|| field->fld_flags & FLD_array)
continue;
2004-02-02 12:02:12 +01:00
qli_nod* assignment = make_assignment(make_field(field, context), 0, 0);
2004-05-16 03:42:11 +02:00
ALLQ_push((blk*) assignment, &stack);
2001-05-23 15:26:42 +02:00
}
node->nod_arg[e_sto_statement] = make_list(stack);
}
if (!loop)
return node;
loop->nod_arg[e_for_statement] = node;
return loop;
}
2004-03-07 08:58:55 +01:00
static void expand_values( qli_syntax* input, qli_lls* right)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x p a n d _ v a l u e s
*
**************************************
*
* Functional description
* We've got a grungy SQL insert, and we have
* to make the value list match the field list.
* On the way in, we got the right number of
2001-05-23 15:26:42 +02:00
* fields. Now all that's needed is the values
* and matching the two lists, and generating
2001-05-23 15:26:42 +02:00
* assignments. If the input is from a select,
* things may be harder, and if there are wild cards
* things will be harder still. Wild cards come in
* two flavors * and <context>.*. The first is
* a nod_prompt, the second a nod_star.
*
**************************************/
// fields have already been checked and expanded. Just count them
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
qli_lls* fields = (qli_lls*) input->syn_arg[s_sto_fields];
qli_lls* stack;
SSHORT field_count = 0;
2001-05-23 15:26:42 +02:00
for (stack = fields; stack; stack = stack->lls_next)
field_count++;
// We're going to want the values in the order listed in the command
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
qli_lls* values = (qli_lls*) input->syn_arg[s_sto_values];
2004-05-16 03:42:11 +02:00
for (; values; ALLQ_push(ALLQ_pop(&values), &stack));
2001-05-23 15:26:42 +02:00
// now go through, count, and expand where needed
2001-05-23 15:26:42 +02:00
SSHORT value_count = 0;
2001-05-23 15:26:42 +02:00
while (stack) {
2004-05-16 03:42:11 +02:00
qli_syntax* value = (qli_syntax*) ALLQ_pop(&stack);
2001-05-23 15:26:42 +02:00
if (input->syn_arg[s_sto_rse] && value->syn_type == nod_prompt) {
if (value->syn_arg[0] == 0) {
2004-02-02 12:02:12 +01:00
qli_lls* temp = NULL;
2001-05-23 15:26:42 +02:00
for (; right; right = right->lls_next)
2004-05-16 03:42:11 +02:00
ALLQ_push(right->lls_object, &temp);
2001-05-23 15:26:42 +02:00
while (temp) {
2004-05-16 03:42:11 +02:00
qli_ctx* context = (qli_ctx*) ALLQ_pop(&temp);
2001-05-23 15:26:42 +02:00
value_count +=
2004-02-02 12:02:12 +01:00
generate_fields(context, (qli_lls*) &values,
2001-05-23 15:26:42 +02:00
input->syn_arg[s_sto_rse]);
}
}
else
IBERROR(542); // this was a prompting expression. won't do at all
2001-05-23 15:26:42 +02:00
}
else if (input->syn_arg[s_sto_rse] && (value->syn_type == nod_star)) {
qli_ctx* context = find_context((const nam*) value->syn_arg[0], right);
if (!context)
IBERROR(154); // Msg154 unrecognized context
2001-05-23 15:26:42 +02:00
value_count +=
2004-02-02 12:02:12 +01:00
generate_fields(context, (qli_lls*) &values, input->syn_arg[s_sto_rse]);
2001-05-23 15:26:42 +02:00
}
else {
2004-05-16 03:42:11 +02:00
ALLQ_push((blk*) value, &values);
2001-05-23 15:26:42 +02:00
value_count++;
}
}
// Make assignments from values to fields
2001-05-23 15:26:42 +02:00
if (field_count != value_count)
IBERROR(189);
// Msg189 the number of values do not match the number of fields
2001-05-23 15:26:42 +02:00
2004-03-07 08:58:55 +01:00
qli_syntax* list = (qli_syntax*) ALLOCDV(type_syn, value_count);
2001-05-23 15:26:42 +02:00
list->syn_type = nod_list;
list->syn_count = value_count;
input->syn_arg[s_sto_statement] = list;
2004-03-07 08:58:55 +01:00
qli_syntax** ptr = list->syn_arg + value_count;
2001-05-23 15:26:42 +02:00
while (values) {
2004-03-07 08:58:55 +01:00
qli_syntax* assignment = (qli_syntax*) ALLOCDV(type_syn, s_asn_count);
*--ptr = assignment;
2001-05-23 15:26:42 +02:00
assignment->syn_type = nod_assign;
assignment->syn_count = s_asn_count;
2004-05-16 03:42:11 +02:00
assignment->syn_arg[s_asn_to] = (qli_syntax*) ALLQ_pop(&fields);
assignment->syn_arg[s_asn_from] = (qli_syntax*) ALLQ_pop(&values);
2001-05-23 15:26:42 +02:00
}
}
static qli_ctx* find_context( const nam* name, qli_lls* contexts)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* f i n d _ c o n t e x t
*
**************************************
*
* Functional description
* We've got a context name and we need to return
* the context block implicated.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_ctx* context;
2001-05-23 15:26:42 +02:00
for (; contexts; contexts = contexts->lls_next) {
2004-02-02 12:02:12 +01:00
context = (qli_ctx*) contexts->lls_object;
const qli_rel* relation = context->ctx_relation;
2001-05-23 15:26:42 +02:00
if (compare_names(name, relation->rel_symbol))
break;
if (compare_names(name, context->ctx_symbol))
break;
}
return (contexts) ? context : NULL;
}
2004-03-07 08:58:55 +01:00
static int generate_fields( qli_ctx* context, qli_lls* values, qli_syntax* rse)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g e n e r a t e _ f i e l d s
*
**************************************
*
* Functional description
* Expand an asterisk expression, which
* could be <relation>.* or <alias>.* or <context>.*
* into a list of non-expanded field blocks for
* input to a store or update.
*
**************************************/
if (context->ctx_type == CTX_AGGREGATE)
return 0;
2004-03-07 08:58:55 +01:00
qli_syntax* group_list = rse->syn_arg[s_rse_group_by];
2004-02-02 12:02:12 +01:00
qli_rel* relation = context->ctx_relation;
int count = 0;
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
for (qli_fld* field = relation->rel_fields; field; field = field->fld_next) {
2001-05-23 15:26:42 +02:00
if (
(field->fld_system_flag
&& field->fld_system_flag != relation->rel_system_flag)
|| field->fld_flags & FLD_array)
{
2001-05-23 15:26:42 +02:00
continue;
}
2004-03-07 08:58:55 +01:00
qli_syntax* value = decompile_field(field, context);
2001-05-23 15:26:42 +02:00
if (group_list && invalid_syn_field(value, group_list))
continue;
2004-05-16 03:42:11 +02:00
ALLQ_push((blk*) value, (qli_lls**) values);
2001-05-23 15:26:42 +02:00
count++;
}
return count;
}
2004-03-07 08:58:55 +01:00
static int generate_items( qli_syntax* symbol, qli_lls* right, qli_lls* items, qli_nod* rse)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g e n e r a t e _ i t e m s
*
**************************************
*
* Functional description
* Expand an asterisk expression, which
* could be <relation>.* or <alias>.* or <context>.*
* into a list of reasonable print items.
2001-05-23 15:26:42 +02:00
*
* If the original request included a group by,
* include only the grouping fields.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_nod* group_list = (rse) ? rse->nod_arg[e_rse_group_by] : NULL;
2001-05-23 15:26:42 +02:00
// first identify the relation or context
2001-05-23 15:26:42 +02:00
const nam* name;
2001-05-23 15:26:42 +02:00
if (symbol->syn_count == 1)
name = (NAM) symbol->syn_arg[0];
else
IBERROR(153);
// Msg153 asterisk expressions require exactly one qualifying context
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
qli_ctx* context = find_context(name, right);
if (!context)
IBERROR(154); // Msg154 unrecognized context
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
qli_rel* relation = context->ctx_relation;
2003-10-16 10:51:06 +02:00
int count = 0;
2004-02-02 12:02:12 +01:00
for (qli_fld* field = relation->rel_fields; field; field = field->fld_next) {
2001-05-23 15:26:42 +02:00
if (
(field->fld_system_flag
&& field->fld_system_flag != relation->rel_system_flag)
|| field->fld_flags & FLD_array)
2003-10-16 10:51:06 +02:00
{
2001-05-23 15:26:42 +02:00
continue;
2003-10-16 10:51:06 +02:00
}
2004-02-02 12:02:12 +01:00
qli_nod* node = make_field(field, context);
2001-05-23 15:26:42 +02:00
if (group_list && invalid_nod_field(node, group_list))
continue;
2004-02-02 12:02:12 +01:00
qli_print_item* item = (qli_print_item*) ALLOCD(type_itm);
2001-05-23 15:26:42 +02:00
item->itm_type = item_value;
item->itm_value = make_field(field, context);
expand_edit_string(item->itm_value, item);
2004-05-16 03:42:11 +02:00
ALLQ_push((blk*) item, (qli_lls**) items);
2003-10-16 10:51:06 +02:00
++count;
2001-05-23 15:26:42 +02:00
}
return count;
}
2004-03-07 08:58:55 +01:00
static bool global_agg( const qli_syntax* item, const qli_syntax* group_list)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g l o b a l _ a g g
*
**************************************
*
* Functional description
* We've got a print list item that may contain
* a sql global aggregate. If it does, we're
* going to make the whole thing a degenerate
* group by. Anyway. Look for aggregates buried
* deep within printable things.
2001-05-23 15:26:42 +02:00
*
* This recurses. If it finds a mixture of normal
* and aggregates it complains.
*
**************************************/
2003-09-10 19:52:12 +02:00
bool normal_field = false;
bool aggregate = false;
2001-05-23 15:26:42 +02:00
switch (item->syn_type) {
case nod_agg_average:
case nod_agg_max:
case nod_agg_min:
case nod_agg_total:
case nod_agg_count:
case nod_running_total:
case nod_running_count:
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
case nod_upcase:
2005-05-28 00:45:31 +02:00
case nod_lowcase:
2001-05-23 15:26:42 +02:00
case nod_add:
case nod_subtract:
case nod_multiply:
case nod_divide:
case nod_negate:
case nod_concatenate:
case nod_substr:
{
2004-03-07 08:58:55 +01:00
const qli_syntax* const* ptr = item->syn_arg;
for (const qli_syntax* const* const end = ptr + item->syn_count; ptr < end;
ptr++)
{
2001-05-23 15:26:42 +02:00
if ((*ptr)->syn_type == nod_constant)
continue;
if (global_agg(*ptr, group_list))
2003-09-10 19:52:12 +02:00
aggregate = true;
2001-05-23 15:26:42 +02:00
else if (!group_list || invalid_syn_field(*ptr, group_list))
2003-09-10 19:52:12 +02:00
normal_field = true;
2001-05-23 15:26:42 +02:00
}
}
default:
break;
}
if (normal_field && aggregate)
IBERROR(451);
return aggregate;
}
static bool invalid_nod_field( const qli_nod* node, const qli_nod* list)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* i n v a l i d _ n o d _ f i e l d
*
**************************************
*
* Functional description
*
* Validate that an expanded field / context
* pair is in a specified list. Thus is used
* in one instance to check that a simple field selected
* through a grouping rse is a grouping field -
2001-05-23 15:26:42 +02:00
* thus a valid field reference.
*
**************************************/
if (!list)
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
bool invalid = false;
2001-05-23 15:26:42 +02:00
if (node->nod_type == nod_field) {
const qli_fld* field = (qli_fld*) node->nod_arg[e_fld_field];
const qli_ctx* context = (qli_ctx*) node->nod_arg[e_fld_context];
const qli_nod* const* ptr = list->nod_arg;
2004-02-02 12:02:12 +01:00
for (const qli_nod* const* const end = ptr + list->nod_count;
ptr < end; ptr++)
{
if (field == (qli_fld*) (*ptr)->nod_arg[e_fld_field]
&& context == (qli_ctx*) (*ptr)->nod_arg[e_fld_context])
2003-09-10 19:52:12 +02:00
{
return false;
}
2004-02-02 12:02:12 +01:00
}
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
2004-02-02 12:02:12 +01:00
else {
const qli_nod* const* ptr = node->nod_arg;
2004-02-02 12:02:12 +01:00
for (const qli_nod* const* const end = ptr + node->nod_count; ptr < end;
2001-05-23 15:26:42 +02:00
ptr++)
{
2001-05-23 15:26:42 +02:00
switch ((*ptr)->nod_type) {
case nod_field:
case nod_add:
case nod_subtract:
case nod_multiply:
case nod_divide:
case nod_negate:
case nod_concatenate:
case nod_substr:
case nod_format:
case nod_choice:
case nod_function:
case nod_upcase:
2005-05-28 00:45:31 +02:00
case nod_lowcase:
2001-05-23 15:26:42 +02:00
invalid |= invalid_nod_field(*ptr, list);
}
}
2004-02-02 12:02:12 +01:00
}
2001-05-23 15:26:42 +02:00
return invalid;
}
2004-03-07 08:58:55 +01:00
static bool invalid_syn_field( const qli_syntax* syn_node, const qli_syntax* list)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* i n v a l i d _ s y n _ f i e l d
*
**************************************
*
* Functional description
* Make sure an unexpanded simple field selected
* through a grouping rse is a grouping field -
2001-05-23 15:26:42 +02:00
* thus a valid field reference. For the sake of
* argument, we'll match qualified to unqualified
* reference, but qualified reference must match
* completely.
2001-05-23 15:26:42 +02:00
*
* One more thought. If this miserable thing is
* a wild card, let it through and expand it
2001-05-23 15:26:42 +02:00
* correctly later.
*
**************************************/
if (syn_node->syn_type == nod_star)
2003-09-10 19:52:12 +02:00
return false;
2001-05-23 15:26:42 +02:00
if (!list)
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
bool invalid = false;
2001-05-23 15:26:42 +02:00
if (syn_node->syn_type == nod_field) {
const nam* fctx = NULL;
const nam* fname = (NAM) syn_node->syn_arg[0];
2001-05-23 15:26:42 +02:00
if (syn_node->syn_count == 2) {
fctx = fname;
fname = (NAM) syn_node->syn_arg[1];
}
for (SSHORT count = list->syn_count; count;) {
const nam* gctx = NULL;
2004-03-07 08:58:55 +01:00
const qli_syntax* element = list->syn_arg[--count];
const nam* gname = (NAM) element->syn_arg[0];
2001-05-23 15:26:42 +02:00
if (element->syn_count == 2) {
gctx = gname;
gname = (NAM) element->syn_arg[1];
}
if (!strcmp(fname->nam_string, gname->nam_string))
if (!gctx || !fctx
|| !strcmp(fctx->nam_string, gctx->nam_string))
2003-09-10 19:52:12 +02:00
{
return false;
}
2001-05-23 15:26:42 +02:00
}
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
else {
2004-03-07 08:58:55 +01:00
const qli_syntax* const* ptr = syn_node->syn_arg;
for (const qli_syntax* const* const end = ptr + syn_node->syn_count;
2001-05-23 15:26:42 +02:00
ptr < end; ptr++)
{
2001-05-23 15:26:42 +02:00
switch ((*ptr)->syn_type) {
case nod_field:
case nod_add:
case nod_subtract:
case nod_multiply:
case nod_divide:
case nod_negate:
case nod_concatenate:
case nod_substr:
case nod_format:
case nod_choice:
case nod_function:
case nod_upcase:
2005-05-28 00:45:31 +02:00
case nod_lowcase:
2001-05-23 15:26:42 +02:00
invalid |= invalid_syn_field(*ptr, list);
}
}
}
2001-05-23 15:26:42 +02:00
return invalid;
}
2004-02-02 12:02:12 +01:00
static qli_nod* make_and( qli_nod* expr1, qli_nod* expr2)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* m a k e _ a n d
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Combine two expressions, each possible null, into at most
* a single boolean.
*
**************************************/
if (!expr1)
return expr2;
if (!expr2)
return expr1;
2004-05-16 03:42:11 +02:00
qli_nod* node = make_node(nod_and, 2);
2001-05-23 15:26:42 +02:00
node->nod_arg[0] = expr1;
node->nod_arg[1] = expr2;
return node;
}
2004-02-02 12:02:12 +01:00
static qli_nod* make_assignment( qli_nod* target, qli_nod* initial, qli_lls* right)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* m a k e _ a s s i g n m e n t
*
**************************************
*
* Functional description
* Generate a prompt and assignment to a field.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_fld* field = (qli_fld*) target->nod_arg[e_fld_field];
qli_lls* stack = NULL;
2004-05-16 03:42:11 +02:00
ALLQ_push((blk*) target->nod_arg[e_fld_context], &stack);
2001-05-23 15:26:42 +02:00
qli_nod* prompt;
2001-05-23 15:26:42 +02:00
if (field->fld_dtype == dtype_blob) {
2004-05-16 03:42:11 +02:00
prompt = make_node(nod_edit_blob, e_edt_count);
2001-05-23 15:26:42 +02:00
prompt->nod_count = 0;
2004-02-02 12:02:12 +01:00
prompt->nod_arg[e_edt_name] = (qli_nod*) field->fld_name->sym_string;
2001-05-23 15:26:42 +02:00
if (initial) {
prompt->nod_count = 1;
2004-03-07 08:58:55 +01:00
prompt->nod_arg[e_edt_input] = expand_expression((qli_syntax*) initial, right);
2001-05-23 15:26:42 +02:00
}
}
else {
2004-05-16 03:42:11 +02:00
prompt = make_node(nod_prompt, e_prm_count);
2004-02-02 12:02:12 +01:00
prompt->nod_arg[e_prm_prompt] = (qli_nod*) field->fld_name->sym_string;
prompt->nod_arg[e_prm_field] = (qli_nod*) field;
2001-05-23 15:26:42 +02:00
}
2004-05-16 03:42:11 +02:00
qli_nod* assignment = make_node(nod_assign, e_asn_count);
2001-05-23 15:26:42 +02:00
assignment->nod_arg[e_asn_to] = target;
assignment->nod_arg[e_asn_from] = prompt;
if (field->fld_validation)
assignment->nod_arg[e_asn_valid] =
expand_expression(field->fld_validation, stack);
else
--assignment->nod_count;
2004-05-16 03:42:11 +02:00
ALLQ_pop(&stack);
2001-05-23 15:26:42 +02:00
return assignment;
}
2004-02-02 12:02:12 +01:00
static qli_nod* make_field( qli_fld* field, qli_ctx* context)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* m a k e _ f i e l d
*
**************************************
*
* Functional description
* Make a field block. Not too tough.
*
**************************************/
2004-05-16 03:42:11 +02:00
qli_nod* node = make_node(nod_field, e_fld_count);
2001-05-23 15:26:42 +02:00
node->nod_count = 0;
2004-02-02 12:02:12 +01:00
node->nod_arg[e_fld_field] = (qli_nod*) field;
node->nod_arg[e_fld_context] = (qli_nod*) context;
2001-05-23 15:26:42 +02:00
if (context->ctx_variable)
node->nod_type = nod_variable;
return node;
}
2004-02-02 12:02:12 +01:00
static qli_nod* make_list( qli_lls* stack)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* m a k e _ l i s t
*
**************************************
*
* Functional description
* Dump a stack of junk into a list node. Best count
* them first.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_lls* temp = stack;
USHORT count = 0;
2001-05-23 15:26:42 +02:00
while (temp) {
count++;
temp = temp->lls_next;
}
2004-05-16 03:42:11 +02:00
qli_nod* node = make_node(nod_list, count);
2004-02-02 12:02:12 +01:00
qli_nod** ptr = &node->nod_arg[count];
2001-05-23 15:26:42 +02:00
while (stack)
2004-05-16 03:42:11 +02:00
*--ptr = (qli_nod*) ALLQ_pop(&stack);
2001-05-23 15:26:42 +02:00
return node;
}
2004-02-02 12:02:12 +01:00
static qli_nod* make_node( NOD_T type, USHORT count)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* m a k e _ n o d e
*
**************************************
*
* Functional description
* Allocate a node and fill in some basic stuff.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_nod* node = (qli_nod*) ALLOCDV(type_nod, count);
2001-05-23 15:26:42 +02:00
node->nod_type = type;
node->nod_count = count;
return node;
}
2004-02-02 12:02:12 +01:00
static qli_nod* negate( qli_nod* expr)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* n e g a t e
*
**************************************
*
* Functional description
* Build negation of expression.
*
**************************************/
2004-05-16 03:42:11 +02:00
qli_nod* node = make_node(nod_not, 1);
2001-05-23 15:26:42 +02:00
node->nod_arg[0] = expr;
return node;
}
2004-03-07 08:58:55 +01:00
static qli_nod* possible_literal(qli_syntax* input,
2004-02-02 12:02:12 +01:00
qli_lls* stack,
2003-09-10 19:52:12 +02:00
bool upper_flag)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* p o s s i b l e _ l i t e r a l
*
**************************************
*
* Functional description
* Check to see if a value node is an unresolved name. If so,
* transform it into a constant expression. This is used to
* correct "informalities" in relational expressions.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_ctx* context;
2001-05-23 15:26:42 +02:00
/* If the value isn't a field, is qualified, or can be resolved,
it doesn't qualify for conversion. Return NULL. */
if (input->syn_type != nod_field ||
input->syn_count != 1 || resolve(input, stack, &context))
{
2001-05-23 15:26:42 +02:00
return NULL;
}
2001-05-23 15:26:42 +02:00
const nam* name = (NAM) input->syn_arg[0];
USHORT l = name->nam_length;
2004-02-02 12:02:12 +01:00
qli_const* constant = (qli_const*) ALLOCDV(type_con, l);
2001-05-23 15:26:42 +02:00
constant->con_desc.dsc_dtype = dtype_text;
constant->con_desc.dsc_length = l;
constant->con_desc.dsc_address = constant->con_data;
TEXT* p = (TEXT*) constant->con_data;
const TEXT* q = name->nam_string;
2001-05-23 15:26:42 +02:00
if (upper_flag) {
if (l)
do {
const TEXT c = *q++;
2001-05-23 15:26:42 +02:00
*p++ = UPPER(c);
} while (--l);
}
else if (l)
do {
const TEXT c = *q++;
2001-05-23 15:26:42 +02:00
*p++ = (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c;
} while (--l);
2004-05-16 03:42:11 +02:00
qli_nod* node = make_node(nod_constant, 0);
2001-05-23 15:26:42 +02:00
node->nod_desc = constant->con_desc;
return node;
}
2004-02-02 12:02:12 +01:00
static qli_nod* post_map( qli_nod* node, qli_ctx* context)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* p o s t _ m a p
*
**************************************
*
* Functional description
* Post an item to a map for a context.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_map* map;
2001-05-23 15:26:42 +02:00
// Check to see if the item has already been posted
2001-05-23 15:26:42 +02:00
for (map = context->ctx_map; map; map = map->map_next)
if (CMP_node_match(node, map->map_node))
break;
if (!map) {
2004-02-02 12:02:12 +01:00
map = (qli_map*) ALLOCD(type_map);
2001-05-23 15:26:42 +02:00
map->map_next = context->ctx_map;
context->ctx_map = map;
map->map_node = node;
}
2004-05-16 03:42:11 +02:00
qli_nod* new_node = make_node(nod_map, e_map_count);
2001-05-23 15:26:42 +02:00
new_node->nod_count = 0;
2004-02-02 12:02:12 +01:00
new_node->nod_arg[e_map_context] = (qli_nod*) context;
new_node->nod_arg[e_map_map] = (qli_nod*) map;
2001-05-23 15:26:42 +02:00
new_node->nod_desc = node->nod_desc;
return new_node;
}
2004-03-07 08:58:55 +01:00
static qli_fld* resolve( qli_syntax* node, qli_lls* stack, qli_ctx** out_context)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* r e s o l v e
*
**************************************
*
* Functional description
* Resolve a field node against a context stack. Return both the
* field block (by value) and the corresponding context block (by
* reference). Return NULL if field can't be resolved.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_rel* relation;
qli_fld* field;
2001-05-23 15:26:42 +02:00
/* Look thru context stack looking for a context that will resolve
all name segments. If the context is a secondary context, require
that the context name be given explicitly (used for special STORE
context). */
2003-10-16 10:51:06 +02:00
NAM* base = (NAM*) node->syn_arg;
2001-05-23 15:26:42 +02:00
for (; stack; stack = stack->lls_next) {
2004-02-02 12:02:12 +01:00
qli_ctx* context = (qli_ctx*) stack->lls_object;
*out_context = context;
NAM* ptr = base + node->syn_count;
const nam* name = *--ptr;
2001-05-23 15:26:42 +02:00
switch (context->ctx_type) {
case CTX_VARIABLE:
if (ptr == base)
for (field = context->ctx_variable; field;
field = field->fld_next)
2003-09-10 19:52:12 +02:00
{
if (compare_names(name, field->fld_name)
|| compare_names(name, field->fld_query_name))
{
return field;
}
}
2001-05-23 15:26:42 +02:00
break;
case CTX_RELATION:
if (context->ctx_primary) {
*out_context = context = context->ctx_primary;
2001-07-12 07:46:06 +02:00
if (!compare_names((NAM) node->syn_arg[0], context->ctx_symbol))
2001-05-23 15:26:42 +02:00
break;
}
relation = context->ctx_relation;
for (field = relation->rel_fields; field; field = field->fld_next)
if (compare_names(name, field->fld_name) ||
compare_names(name, field->fld_query_name))
2003-09-10 19:52:12 +02:00
{
2001-05-23 15:26:42 +02:00
if (ptr == base)
return field;
name = *--ptr;
if (compare_names(name, relation->rel_symbol))
if (ptr == base)
return field;
else
name = *--ptr;
if (compare_names(name, context->ctx_symbol))
if (ptr == base)
return field;
break;
}
break;
}
}
// We didn't resolve all name segments. Let somebody else worry about it.
2001-05-23 15:26:42 +02:00
return NULL;
}
2004-03-07 08:58:55 +01:00
static void resolve_really( qli_fld* variable, const qli_syntax* field_node)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* r e s o l v e _ r e a l l y
*
**************************************
*
* Functional description
* Resolve a field reference entirely.
2001-05-23 15:26:42 +02:00
*
**************************************/
/* For ease, break down the syntax block.
It should contain at least one name; two names are a potential ambiguity:
check for a dbb (<db>.<glo_fld>), then for a rel (<rel>.<fld>). */
USHORT offset = field_node->syn_count;
const nam* fld_name = (NAM) field_node->syn_arg[--offset];
2001-05-23 15:26:42 +02:00
NAM rel_name = NULL;
NAM db_name = NULL;
2001-05-23 15:26:42 +02:00
if (offset) {
rel_name = (NAM) field_node->syn_arg[--offset];
if (offset)
db_name = (NAM) field_node->syn_arg[--offset];
}
bool resolved = false;
bool local = false;
2004-02-02 12:02:12 +01:00
qli_fld* field = NULL;
2001-05-23 15:26:42 +02:00
if (field_node->syn_count == 1)
resolved = MET_declare(0, variable, fld_name);
2001-05-23 15:26:42 +02:00
else if (field_node->syn_count == 2) {
2004-02-21 10:24:14 +01:00
qli_symbol *symbol;
for (symbol = rel_name->nam_symbol; symbol;
2003-09-13 13:48:09 +02:00
symbol = symbol->sym_homonym)
{
if (symbol->sym_type == SYM_database) {
DBB dbb = (DBB) symbol->sym_object;
resolved = MET_declare(dbb, variable, fld_name);
2003-09-10 19:52:12 +02:00
break; // should be only one db in homonym list
2001-05-23 15:26:42 +02:00
}
2003-09-13 13:48:09 +02:00
}
2001-05-23 15:26:42 +02:00
if (!resolved) {
for (DBB dbb = QLI_databases; dbb && !resolved; dbb = dbb->dbb_next)
2001-05-23 15:26:42 +02:00
for (symbol = rel_name->nam_symbol; symbol;
symbol = symbol->sym_homonym)
2003-09-10 19:52:12 +02:00
{
qli_rel* relation;
2003-09-10 19:52:12 +02:00
if (symbol->sym_type == SYM_relation
2004-02-02 12:02:12 +01:00
&& (relation = (qli_rel*) symbol->sym_object)
&& relation->rel_database == dbb)
2003-09-10 19:52:12 +02:00
{
2001-05-23 15:26:42 +02:00
if (!relation->rel_fields)
MET_fields(relation);
for (field = relation->rel_fields; field;
field = field->fld_next)
2003-09-10 19:52:12 +02:00
{
resolved = local = compare_names(fld_name,
field->fld_name);
if (resolved)
2003-09-10 19:52:12 +02:00
break;
}
break; // should be only one rel in homonym list for each db
2001-05-23 15:26:42 +02:00
}
2003-09-10 19:52:12 +02:00
}
2001-05-23 15:26:42 +02:00
}
}
else {
qli_rel* relation = variable->fld_relation;
2001-05-23 15:26:42 +02:00
if (!relation->rel_fields)
MET_fields(relation);
for (field = relation->rel_fields; field; field = field->fld_next)
{
resolved = local = compare_names(fld_name, field->fld_name);
if (resolved)
2001-05-23 15:26:42 +02:00
break;
}
2001-05-23 15:26:42 +02:00
}
if (!resolved)
IBERROR(155);
// Msg155 field referenced in BASED ON can not be resolved against readied databases
2001-05-23 15:26:42 +02:00
if (local) {
variable->fld_dtype = field->fld_dtype;
variable->fld_length = field->fld_length;
variable->fld_scale = field->fld_scale;
variable->fld_sub_type = field->fld_sub_type;
variable->fld_sub_type_missing = field->fld_sub_type_missing;
if (!variable->fld_edit_string)
variable->fld_edit_string = field->fld_edit_string;
if (!variable->fld_query_header)
variable->fld_query_header = field->fld_query_header;
if (!variable->fld_query_name)
variable->fld_query_name = field->fld_query_name;
}
}