/* * PROGRAM: JRD Command Oriented Query Language * MODULE: compile.cpp * DESCRIPTION: Compile expanded statement into executable things * * The contents of this file are subject to the Interbase Public * License Version 1.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy * of the License at http://www.Inprise.com/IPL.html * * Software distributed under the License is distributed on an * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express * or implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code was created by Inprise Corporation * and its predecessors. Portions created by Inprise Corporation are * Copyright (C) Inprise Corporation. * * All Rights Reserved. * Contributor(s): ______________________________________. */ #include "firebird.h" #include #include "../qli/dtr.h" #include "../qli/compile.h" #include "../qli/exe.h" #include "../qli/report.h" #include "../jrd/intl.h" #include "../qli/all_proto.h" #include "../qli/compi_proto.h" #include "../qli/err_proto.h" #include "../qli/forma_proto.h" #include "../qli/meta_proto.h" #include "../jrd/dsc_proto.h" const USHORT PROMPT_LENGTH = 80; static qli_nod* compile_any(qli_nod*, qli_req*, bool); static qli_nod* compile_assignment(qli_nod*, qli_req*, bool); static void compile_context(qli_nod*, qli_req*, bool); static void compile_control_break(qli_brk*, qli_req*); static qli_nod* compile_edit(qli_nod*, qli_req*); static qli_nod* compile_erase(qli_nod*, qli_req*); static qli_nod* compile_expression(qli_nod*, qli_req*, bool); static qli_nod* compile_field(qli_nod*, qli_req*, bool); static qli_nod* compile_for(qli_nod*, qli_req*, bool); static qli_nod* compile_function(qli_nod*, qli_req*, bool); static qli_nod* compile_if(qli_nod*, qli_req*, bool); static qli_nod* compile_list_fields(qli_nod*, qli_req*); static qli_nod* compile_modify(qli_nod*, qli_req*, bool); static qli_nod* compile_print(qli_nod*, qli_req*); static qli_nod* compile_print_list(qli_nod*, qli_req*, qli_lls**); static qli_nod* compile_prompt(qli_nod*); static qli_nod* compile_repeat(qli_nod*, qli_req*, bool); static qli_nod* compile_report(qli_nod*, qli_req*); static qli_req* compile_rse(qli_nod*, qli_req*, bool, qli_msg**, qli_msg**, DBB *); static qli_nod* compile_statement(qli_nod*, qli_req*, bool); static qli_nod* compile_statistical(qli_nod*, qli_req*, bool); static qli_nod* compile_store(qli_nod*, qli_req*, bool); static bool computable(qli_nod*, qli_req*); static void make_descriptor(qli_nod*, dsc*); static qli_msg* make_message(qli_req*); static void make_missing_reference(qli_par*); static qli_par* make_parameter(qli_msg*, qli_nod*); static qli_nod* make_reference(qli_nod*, qli_msg*); static qli_req* make_request(DBB); static void release_message(qli_msg*); static int string_length(const dsc*); static qli_lls* print_items; static TEXT** print_header; static qli_brk* report_control_break; qli_nod* CMPQ_compile( qli_nod* node) { /************************************** * * C M P _ c o m p i l e * ************************************** * * Functional description * Compile a statement into something executable. * **************************************/ print_header = NULL; print_items = NULL; QLI_requests = NULL; compile_statement(node, 0, true); if (print_header) *print_header = FMT_format(print_items); return node; } void CMP_alloc_temp(qli_nod* node) { /************************************** * * C M P _ a l l o c _ t e m p * ************************************** * * Functional description * Allocate a data area for a node. * **************************************/ if (node->nod_desc.dsc_address) return; qli_str* string = (qli_str*) ALLOCDV(type_str, node->nod_desc.dsc_length); node->nod_desc.dsc_address = (UCHAR *) string->str_data; } bool CMP_node_match( const qli_nod* node1, const qli_nod* node2) { /************************************** * * C M P _ n o d e _ m a t c h * ************************************** * * Functional description * Compare two nodes for equality of value. * **************************************/ if (!node1 || !node2 || node1->nod_type != node2->nod_type) return false; switch (node1->nod_type) { case nod_field: if (node1->nod_arg[e_fld_field] != node2->nod_arg[e_fld_field] || node1->nod_arg[e_fld_context] != node2->nod_arg[e_fld_context] || node1->nod_arg[e_fld_subs] != node2->nod_arg[e_fld_subs]) { return false; } return true; case nod_constant: { if (node1->nod_desc.dsc_dtype != node2->nod_desc.dsc_dtype || node2->nod_desc.dsc_scale != node2->nod_desc.dsc_scale || node2->nod_desc.dsc_length != node2->nod_desc.dsc_length) { return false; } int l = node1->nod_desc.dsc_length; if (l) return memcmp(node1->nod_desc.dsc_address, node2->nod_desc.dsc_address, l) == 0; return true; } case nod_map: { const qli_map* map1 = (qli_map*) node1->nod_arg[e_map_map]; const qli_map* map2 = (qli_map*) node2->nod_arg[e_map_map]; return CMP_node_match(map1->map_node, map2->map_node); } case nod_agg_average: case nod_agg_max: case nod_agg_min: case nod_agg_total: case nod_agg_count: return CMP_node_match(node1->nod_arg[e_stt_value], node2->nod_arg[e_stt_value]); case nod_function: if (node1->nod_arg[e_fun_function] != node1->nod_arg[e_fun_function]) return false; return CMP_node_match(node1->nod_arg[e_fun_args], node2->nod_arg[e_fun_args]); } const qli_nod* const* ptr1 = node1->nod_arg; const qli_nod* const* ptr2 = node2->nod_arg; for (const qli_nod* const* const end = ptr1 + node1->nod_count; ptr1 < end; ++ptr1, ++ptr2) { if (!CMP_node_match(*ptr1, *ptr2)) return false; } return true; } static qli_nod* compile_any( qli_nod* node, qli_req* old_request, bool internal_flag) { /************************************** * * c o m p i l e _ a n y * ************************************** * * Functional description * Compile either an ANY or UNIQUE boolean expression. * **************************************/ qli_msg* old_send = NULL; qli_msg* old_receive = NULL; if (old_request) { old_send = old_request->req_send; old_receive = old_request->req_receive; } qli_msg* send; qli_msg* receive; qli_req* request = compile_rse(node->nod_arg[e_any_rse], old_request, false, &send, &receive, 0); if (request) node->nod_arg[e_any_request] = (qli_nod*) request; else request = old_request; if (old_request) { old_request->req_send = old_send; old_request->req_receive = old_receive; } if ((send != old_send) && !send->msg_parameters) { release_message(send); send = NULL; if (!receive) return NULL; } if (old_request && request->req_database != old_request->req_database) IBERROR(357); // Msg357 relations from multiple databases in single rse if (old_request && (!receive || !receive->msg_parameters)) { if (receive) release_message(receive); receive = NULL; } if (receive) { qli_par* parameter = make_parameter(receive, 0); node->nod_import = parameter; parameter->par_desc.dsc_dtype = dtype_short; parameter->par_desc.dsc_length = sizeof(SSHORT); } node->nod_arg[e_any_send] = (qli_nod*) send; node->nod_arg[e_any_receive] = (qli_nod*) receive; return node; } static qli_nod* compile_assignment( qli_nod* node, qli_req* request, bool statement_internal) { /************************************** * * c o m p i l e _ a s s i g n m e n t * ************************************** * * Functional description: * Compile an assignment statement, passing * it to the database if possible. Possibility * happens when both sides of the assignment are * computable in the request context. (Following * the logic of such things, the 'internal' flags * mean external to qli, but internal to jrd. * As is well known, the seat of the soul is the * dbms). * **************************************/ /* Start by assuming that the assignment will ultimately take place in the DBMS */ qli_nod* to = node->nod_arg[e_asn_to]; to->nod_flags |= NOD_parameter2; qli_nod* from = node->nod_arg[e_asn_from]; from->nod_flags |= NOD_parameter2; /* If the assignment is to a variable, the assignment is completely local */ if (to->nod_type == nod_variable) { statement_internal = false; node->nod_flags |= NOD_local; } const bool target_internal = computable(to, request); statement_internal = statement_internal && request && target_internal && computable(from, request); qli_nod* target = compile_expression(to, request, target_internal); node->nod_arg[e_asn_to] = target; node->nod_arg[e_asn_from] = compile_expression(from, request, statement_internal); qli_nod* initial = node->nod_arg[e_asn_initial]; if (initial) node->nod_arg[e_asn_initial] = compile_expression(initial, request, false); if (statement_internal) { node->nod_arg[e_asn_valid] = NULL; // Memory reclaimed in the main loop node->nod_flags |= NOD_remote; node = NULL; } else { if (node->nod_arg[e_asn_valid]) compile_expression(node->nod_arg[e_asn_valid], request, false); if (target->nod_type == nod_field) { if (!request) { qli_ctx* context = (qli_ctx*) target->nod_arg[e_fld_context]; request = context->ctx_request; } target->nod_arg[e_fld_reference] = make_reference(target, request->req_send); } } return node; } static void compile_context( qli_nod* node, qli_req* request, bool internal_flag) { /************************************** * * c o m p i l e _ c o n t e x t * ************************************** * * Functional description * Set all of the contexts in a request. * This may require a recursive call. * **************************************/ qli_ctx** ctx_ptr = (qli_ctx**) node->nod_arg + e_rse_count; const qli_ctx* const* const ctx_end = ctx_ptr + node->nod_count; for (; ctx_ptr < ctx_end; ctx_ptr++) { qli_ctx* context = *ctx_ptr; context->ctx_request = request; context->ctx_context = request->req_context++; context->ctx_message = request->req_receive; if (context->ctx_sub_rse) compile_rse(context->ctx_sub_rse, request, internal_flag, 0, 0, 0); if (context->ctx_stream) compile_context(context->ctx_stream, request, internal_flag); } } static void compile_control_break( qli_brk* control, qli_req* request) { /************************************** * * c o m p i l e _ c o n t r o l _ b r e a k * ************************************** * * Functional description * Compile a control/page/report break. * **************************************/ for (; control; control = control->brk_next) { report_control_break = control; if (control->brk_field) /* control->brk_field = (qli_syntax*) compile_expression (control->brk_field, request, false); */ { qli_nod* temp = (qli_nod*) control->brk_field;; temp->nod_flags |= NOD_parameter2; temp = compile_expression((qli_nod*) control->brk_field, request, false); if (temp->nod_type == nod_field) temp = temp->nod_arg[e_fld_reference]; control->brk_field = (qli_syntax*) temp; } if (control->brk_line) compile_print_list((qli_nod*) control->brk_line, request, 0); report_control_break = NULL; } } static qli_nod* compile_edit( qli_nod* node, qli_req* request) { /************************************** * * c o m p i l e _ e d i t * ************************************** * * Functional description * Compile the "edit blob" expression. * **************************************/ qli_nod* value; /* Make sure there is a message. If there isn't a message, we can't find the target database. */ if (!request) ERRQ_bugcheck(358); // Msg358 can't find database for blob edit // If there is an input blob, get it now. if (value = node->nod_arg[e_edt_input]) { qli_fld* field = (qli_fld*) value->nod_arg[e_fld_field]; if (value->nod_type != nod_field || field->fld_dtype != dtype_blob) IBERROR(356); // Msg356 EDIT argument must be a blob field node->nod_arg[e_edt_input] = compile_expression(value, request, false); } node->nod_arg[e_edt_dbb] = (qli_nod*) request->req_database; node->nod_desc.dsc_dtype = dtype_blob; node->nod_desc.dsc_length = 8; node->nod_desc.dsc_address = (UCHAR *) & node->nod_arg[e_edt_id1]; return node; } static qli_nod* compile_erase( qli_nod* node, qli_req* org_request) { /************************************** * * c o m p i l e _ e r a s e * ************************************** * * Functional description * Compile an ERASE statement. This is so simple that nothing * needs to be done. * **************************************/ qli_ctx* context = (qli_ctx*) node->nod_arg[e_era_context]; qli_req* request = context->ctx_request; node->nod_arg[e_era_request] = (qli_nod*) request; request->req_database->dbb_flags |= DBB_updates; if (org_request == request && !request->req_continue) return NULL; if (!request->req_continue) request->req_continue = make_message(request); node->nod_arg[e_era_message] = (qli_nod*) make_message(request); return node; } static qli_nod* compile_expression( qli_nod* node, qli_req* request, bool internal_flag) { /************************************** * * c o m p i l e _ e x p r e s s i o n * ************************************** * * Functional description * Compile a value. The value may be used internally as part of * another expression (internal_flag == true) or may be referenced * in the QLI context (internal_flag == false). * **************************************/ qli_nod** ptr; const qli_nod* const* end; qli_nod* value; qli_map* map; qli_fld* field; switch (node->nod_type) { case nod_any: case nod_unique: return compile_any(node, request, internal_flag); case nod_max: case nod_min: case nod_count: case nod_average: case nod_total: case nod_from: node->nod_count = 0; return compile_statistical(node, request, internal_flag); case nod_rpt_max: case nod_rpt_min: case nod_rpt_count: case nod_rpt_average: case nod_rpt_total: if (report_control_break) ALLQ_push((blk*) node, &report_control_break->brk_statisticals); case nod_running_total: case nod_running_count: node->nod_count = 0; if (value = node->nod_arg[e_stt_value]) { value->nod_flags |= NOD_parameter2; node->nod_arg[e_stt_value] = compile_expression(value, request, false); } make_descriptor(node, &node->nod_desc); if (internal_flag) { qli_par* parm = make_parameter(request->req_send, 0); node->nod_export = parm; parm->par_desc = node->nod_desc; parm->par_value = node; } else { CMP_alloc_temp(node); if (request && value && computable(value, request)) node->nod_arg[e_stt_value] = make_reference(value, request->req_receive); } return node; case nod_agg_max: case nod_agg_min: case nod_agg_count: case nod_agg_average: case nod_agg_total: node->nod_count = 0; if (value = node->nod_arg[e_stt_value]) { value->nod_flags |= NOD_parameter2; node->nod_arg[e_stt_value] = compile_expression(value, request, true); } make_descriptor(node, &node->nod_desc); if (!internal_flag && request) return make_reference(node, request->req_receive); return node; case nod_map: map = (qli_map*) node->nod_arg[e_map_map]; map->map_node = value = compile_expression(map->map_node, request, true); make_descriptor(value, &node->nod_desc); if (!internal_flag && request) return make_reference(node, request->req_receive); return node; case nod_function: return compile_function(node, request, internal_flag); case nod_eql: case nod_neq: case nod_gtr: case nod_geq: case nod_leq: case nod_lss: case nod_between: node->nod_flags |= nod_comparison; case nod_list: case nod_matches: case nod_sleuth: case nod_like: case nod_containing: case nod_starts: case nod_missing: case nod_and: case nod_or: case nod_not: for (ptr = node->nod_arg, end = ptr + node->nod_count; ptr < end; ptr++) { (*ptr)->nod_flags |= NOD_parameter2; *ptr = compile_expression(*ptr, request, internal_flag); } if (node->nod_type == nod_and || node->nod_type == nod_or) node->nod_flags |= nod_partial; return node; case nod_null: case nod_add: case nod_subtract: case nod_multiply: case nod_divide: case nod_negate: case nod_concatenate: case nod_substr: case nod_user_name: if (!internal_flag && request && request->req_receive && computable(node, request)) { compile_expression(node, request, true); return make_reference(node, request->req_receive); } for (ptr = node->nod_arg, end = ptr + node->nod_count; ptr < end; ptr++) { (*ptr)->nod_flags |= NOD_parameter2; *ptr = compile_expression(*ptr, request, internal_flag); } make_descriptor(node, &node->nod_desc); if (!internal_flag) { node->nod_flags |= NOD_local; CMP_alloc_temp(node); } return node; case nod_format: value = node->nod_arg[e_fmt_value]; node->nod_arg[e_fmt_value] = compile_expression(value, request, false); node->nod_desc.dsc_length = FMT_expression(node); node->nod_desc.dsc_dtype = dtype_text; CMP_alloc_temp(node); if (!internal_flag) node->nod_flags |= NOD_local; return node; case nod_constant: if (!internal_flag) node->nod_flags |= NOD_local; return node; case nod_edit_blob: compile_edit(node, request); return node; case nod_prompt: node->nod_count = 0; compile_prompt(node); if (internal_flag) { qli_par* parm = make_parameter(request->req_send, node); node->nod_export = parm; parm->par_value = node; if (field = (qli_fld*) node->nod_arg[e_prm_field]) { parm->par_desc.dsc_dtype = field->fld_dtype; parm->par_desc.dsc_length = field->fld_length; parm->par_desc.dsc_scale = field->fld_scale; parm->par_desc.dsc_sub_type = field->fld_sub_type; if (parm->par_desc.dsc_dtype == dtype_text) { parm->par_desc.dsc_dtype = dtype_varying; parm->par_desc.dsc_length += sizeof(SSHORT); } } else parm->par_desc = node->nod_desc; } return node; case nod_field: if (value = node->nod_arg[e_fld_subs]) compile_expression(value, request, true); return compile_field(node, request, internal_flag); case nod_variable: field = (qli_fld*) node->nod_arg[e_fld_field]; node->nod_desc.dsc_address = field->fld_data; make_descriptor(node, &node->nod_desc); if (internal_flag) { qli_par* parm = make_parameter(request->req_send, node); node->nod_export = parm; parm->par_value = node; parm->par_desc = node->nod_desc; } return node; case nod_upcase: case nod_lowcase: value = node->nod_arg[0]; node->nod_arg[0] = compile_field(value, request, internal_flag); return node; default: ERRQ_bugcheck(359); // Msg359 compile_expression: not yet implemented return NULL; } } static qli_nod* compile_field( qli_nod* node, qli_req* request, bool internal_flag) { /************************************** * * c o m p i l e _ f i e l d * ************************************** * * Functional description * Compile a field reference. * **************************************/ // Pick up field characteristics node->nod_count = 0; qli_fld* field = (qli_fld*) node->nod_arg[e_fld_field]; qli_ctx* context = (qli_ctx*) node->nod_arg[e_fld_context]; if ((field->fld_flags & FLD_array) && !node->nod_arg[e_fld_subs]) { node->nod_desc.dsc_dtype = dtype_quad; node->nod_desc.dsc_length = 8; node->nod_desc.dsc_scale = 0; node->nod_desc.dsc_sub_type = 0; } else { node->nod_desc.dsc_dtype = field->fld_dtype; node->nod_desc.dsc_length = field->fld_length; node->nod_desc.dsc_scale = field->fld_scale; node->nod_desc.dsc_sub_type = field->fld_sub_type; } node->nod_flags |= NOD_parameter2; /* If the reference is external, or the value is computable in the current request, there is nothing to do. If the value is not computable, make up a parameter to send the value into the request. */ if (internal_flag) { if (computable(node, request)) return node; qli_par* parm = make_parameter(request->req_send, node); node->nod_export = parm; parm->par_desc = node->nod_desc; parm->par_value = node; qli_msg* message = context->ctx_message; if (!message) message = request->req_receive; node->nod_arg[e_fld_reference] = make_reference(node, message); return node; } qli_msg* message = context->ctx_message; if (!message && request) message = request->req_receive; node->nod_arg[e_fld_reference] = make_reference(node, message); return node; } static qli_nod* compile_for( qli_nod* node, qli_req* old_request, bool internal_flag) { /************************************** * * c o m p i l e _ f o r * ************************************** * * Functional description * Compile a FOR loop. If the loop can be done as part of the parent * request, dandy. * **************************************/ /* Compile rse. This will set up both send and receive message. If the messages aren't needed, we can release them later. */ qli_msg* old_send = NULL; qli_msg* old_receive = NULL; if (old_request) { old_send = old_request->req_send; old_receive = old_request->req_receive; } qli_msg* send; qli_msg* receive; qli_req* request = compile_rse(node->nod_arg[e_for_rse], old_request, false, &send, &receive, 0); if (request) node->nod_arg[e_for_request] = (qli_nod*) request; else request = old_request; /* If nothing is required for sub-statement, and no data is required in either direction, we don't need to execute the statement. */ if (!compile_statement (node->nod_arg[e_for_statement], request, internal_flag) && !receive->msg_parameters) { release_message(receive); receive = NULL; } if (old_request) { old_request->req_send = old_send; old_request->req_receive = old_receive; } if ((send != old_send) && !send->msg_parameters) { release_message(send); send = NULL; if (!receive) return NULL; } if (receive) { qli_par* parameter = make_parameter(receive, 0); node->nod_arg[e_for_eof] = (qli_nod*) parameter; parameter->par_desc.dsc_dtype = dtype_short; parameter->par_desc.dsc_length = sizeof(SSHORT); } node->nod_arg[e_for_send] = (qli_nod*) send; node->nod_arg[e_for_receive] = (qli_nod*) receive; return node; } static qli_nod* compile_function( qli_nod* node, qli_req* old_request, bool internal_flag) { /************************************** * * c o m p i l e _ f u n c t i o n * ************************************** * * Functional description * Compile a database function reference. * **************************************/ qli_msg* old_send = NULL; qli_msg* old_receive = NULL; qli_fun* function = (qli_fun*) node->nod_arg[e_fun_function]; node->nod_count = 0; qli_msg* send = NULL; if (!internal_flag) old_request = NULL; if (old_request) { old_send = old_request->req_send; old_receive = old_request->req_receive; } qli_req* request; qli_msg* receive = 0; if (!old_request || old_request->req_database != function->fun_database) { request = make_request(function->fun_database); node->nod_arg[e_fun_request] = (qli_nod*) request; request->req_send = send = make_message(request); request->req_receive = receive = make_message(request); } else request = old_request; // If there is a value, compile it here qli_par* parameter = 0; if (!internal_flag) { node->nod_import = parameter = make_parameter(request->req_receive, 0); make_descriptor(node, ¶meter->par_desc); } qli_nod* list = node->nod_arg[e_fun_args]; qli_nod** ptr = ptr = list->nod_arg; for (const qli_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++) compile_expression(*ptr, request, true); if (old_request) { old_request->req_send = old_send; old_request->req_receive = old_receive; } if (!internal_flag && request == old_request && computable(node, request)) { make_descriptor(node, &node->nod_desc); return make_reference(node, request->req_receive); } if (send && (send != old_send) && !send->msg_parameters) { release_message(send); send = NULL; } node->nod_arg[e_fun_receive] = (qli_nod*) receive; node->nod_arg[e_fun_send] = (qli_nod*) send; return node; } static qli_nod* compile_if( qli_nod* node, qli_req* request, bool internal_flag) { /************************************** * * c o m p i l e _ i f * ************************************** * * Functional description * Compile an IF statement. Determine whether the * statement is going to be executed in QLI or database * context. Try to make sure that the whole thing is * executed one place or the other. * **************************************/ /* If the statement can't be executed in database context, make sure it gets executed locally */ if (!internal_flag || !computable(node, request)) { internal_flag = false; node->nod_flags |= NOD_local; request = NULL; } qli_nod* sub = node->nod_arg[e_if_boolean]; compile_expression(sub, request, internal_flag); compile_statement(node->nod_arg[e_if_true], request, internal_flag); if (sub = node->nod_arg[e_if_false]) compile_statement(sub, request, internal_flag); if (internal_flag) return NULL; return node; } static qli_nod* compile_list_fields( qli_nod* node, qli_req* request) { /************************************** * * c o m p i l e _ l i s t _ f i e l d s * ************************************** * * Functional description * Compile a print node. * **************************************/ qli_nod* list = node->nod_arg[e_prt_list]; compile_print_list(list, request, 0); node->nod_arg[e_prt_list] = FMT_list(list); node->nod_type = nod_print; return node; } static qli_nod* compile_modify( qli_nod* node, qli_req* org_request, bool internal_flag) { /************************************** * * c o m p i l e _ m o d i f y * ************************************** * * Functional description * Compile a modify statement. * **************************************/ /* If this is a different request from the current one, this will require an optional action and a "continue" mesasge */ qli_nod** ptr = node->nod_arg + e_mod_count; qli_ctx* context = (qli_ctx*) *ptr; qli_req* request = context->ctx_source->ctx_request; node->nod_arg[e_mod_request] = (qli_nod*) request; if ((request != org_request || !internal_flag) && !request->req_continue) request->req_continue = make_message(request); qli_msg* old_send = request->req_send; qli_msg* send = make_message(request); request->req_send = send; for (USHORT i = 0; i < node->nod_count; i++) { context = (qli_ctx*) * ptr++; context->ctx_request = request; context->ctx_context = request->req_context++; context->ctx_message = send; } /* If nothing is required for sub-statement, and no data is required in either direction, we don't need to execute the statement. */ if (internal_flag) internal_flag = computable(node->nod_arg[e_mod_statement], request); if (!compile_statement (node->nod_arg[e_mod_statement], request, internal_flag) && (send != old_send) && !send->msg_parameters) { node->nod_flags |= NOD_remote; release_message(send); send = NULL; } node->nod_arg[e_mod_send] = (qli_nod*) send; request->req_send = old_send; request->req_database->dbb_flags |= DBB_updates; if (internal_flag) return NULL; return node; } static qli_nod* compile_print( qli_nod* node, qli_req* request) { /************************************** * * c o m p i l e _ p r i n t * ************************************** * * Functional description * Compile a print node. * **************************************/ if (!print_header) print_header = (TEXT**) & node->nod_arg[e_prt_header]; compile_print_list(node->nod_arg[e_prt_list], request, &print_items); return node; } static qli_nod* compile_print_list( qli_nod* list, qli_req* request, qli_lls** stack) { /************************************** * * c o m p i l e _ p r i n t _ l i s t * ************************************** * * Functional description * Compile a print node. * **************************************/ qli_nod** ptr = list->nod_arg; for (const qli_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++) { qli_print_item* item = (qli_print_item*) *ptr; if (stack) ALLQ_push((blk*) item, stack); if (item->itm_type == item_value) { qli_nod* value = item->itm_value; value->nod_flags |= NOD_parameter2; item->itm_value = compile_expression(value, request, false); if (item->itm_value->nod_type == nod_field) item->itm_value = item->itm_value->nod_arg[e_fld_reference]; if (!value->nod_desc.dsc_dtype) make_descriptor(value, &value->nod_desc); } } return list; } static qli_nod* compile_prompt( qli_nod* node) { /************************************** * * c o m p i l e _ p r o m p t * ************************************** * * Functional description * Set up a prompt expression for execution. * **************************************/ USHORT prompt_length; // Make up a plausible prompt length qli_fld* field = (qli_fld*) node->nod_arg[e_prm_field]; if (!field) prompt_length = PROMPT_LENGTH; else { switch (field->fld_dtype) { case dtype_text: prompt_length = field->fld_length; break; case dtype_varying: prompt_length = field->fld_length - sizeof(SSHORT); break; case dtype_short: prompt_length = 8; break; case dtype_long: case dtype_real: prompt_length = 15; break; default: prompt_length = 30; break; } } /* Allocate string buffer to hold data, a two byte count, a possible carriage return, and a null */ prompt_length += 2 + sizeof(SSHORT); qli_str* string = (qli_str*) ALLOCDV(type_str, prompt_length); node->nod_arg[e_prm_string] = (qli_nod*) string; node->nod_desc.dsc_dtype = dtype_varying; node->nod_desc.dsc_length = prompt_length; node->nod_desc.dsc_address = (UCHAR *) string->str_data; return node; } static qli_nod* compile_repeat( qli_nod* node, qli_req* request, bool internal_flag) { /************************************** * * c o m p i l e _ r e p e a t * ************************************** * * Functional description * Compile a REPEAT statement. * **************************************/ compile_expression(node->nod_arg[e_rpt_value], request, false); compile_statement(node->nod_arg[e_rpt_statement], 0, internal_flag); return node; } static qli_nod* compile_report( qli_nod* node, qli_req* request) { /************************************** * * c o m p i l e _ r e p o r t * ************************************** * * Functional description * Compile the body of a report specification. * **************************************/ qli_brk* control; qli_rpt* report = (qli_rpt*) node->nod_arg[e_prt_list]; if (control = report->rpt_top_rpt) compile_control_break(control, request); if (control = report->rpt_top_page) compile_control_break(control, request); if (control = report->rpt_top_breaks) compile_control_break(control, request); qli_nod* list = report->rpt_detail_line; if (list) compile_print_list(list, request, 0); if (control = report->rpt_bottom_breaks) { compile_control_break(control, request); report->rpt_bottom_breaks = NULL; while (control) { qli_brk* temp = control; control = control->brk_next; temp->brk_next = report->rpt_bottom_breaks; report->rpt_bottom_breaks = temp; } } if (control = report->rpt_bottom_page) compile_control_break(control, request); if (control = report->rpt_bottom_rpt) compile_control_break(control, request); FMT_report(report); return node; } static qli_req* compile_rse( qli_nod* node, qli_req* old_request, bool internal_flag, qli_msg** send, qli_msg** receive, DBB * database) { /************************************** * * c o m p i l e _ r s e * ************************************** * * Functional description * Compile a record selection expression. If it can't be processed * as part of an existing parent request, make up a new request. If * data must be sent to start the loop, generate a send message. Set * up for a receive message as well. * **************************************/ qli_req* request; DBB local_dbb; qli_req* original_request = old_request; if (!database) { local_dbb = NULL; database = &local_dbb; } // Loop thru relations to make sure only a single database is presented qli_ctx** ctx_ptr = (qli_ctx**) node->nod_arg + e_rse_count; const qli_ctx* const* const ctx_end = ctx_ptr + node->nod_count; for (; ctx_ptr < ctx_end; ctx_ptr++) { qli_ctx* context = *ctx_ptr; if (context->ctx_stream) { if (request = compile_rse(context->ctx_stream, old_request, internal_flag, send, receive, database)) old_request = request; } else { qli_rel* relation = context->ctx_relation; if (!*database) *database = relation->rel_database; else if (*database != relation->rel_database) IBERROR(357); // Msg357 relations from multiple databases in single rse } } if (!old_request || old_request->req_database != *database) request = make_request(*database); else request = old_request; if (send) { if (old_request && request == old_request && !(old_request->req_flags & REQ_rse_compiled)) *send = request->req_send; else request->req_send = *send = make_message(request); request->req_receive = *receive = make_message(request); } compile_context(node, request, internal_flag); // Process various clauses if (node->nod_arg[e_rse_first]) compile_expression(node->nod_arg[e_rse_first], request, true); if (node->nod_arg[e_rse_boolean]) compile_expression(node->nod_arg[e_rse_boolean], request, true); qli_nod** ptr; const qli_nod* const* end; qli_nod* list; if (list = node->nod_arg[e_rse_sort]) for (ptr = list->nod_arg, end = ptr + list->nod_count * 2; ptr < end; ptr += 2) { compile_expression(*ptr, request, true); } if (list = node->nod_arg[e_rse_reduced]) for (ptr = list->nod_arg, end = ptr + list->nod_count * 2; ptr < end; ptr += 2) { compile_expression(*ptr, request, true); } if (list = node->nod_arg[e_rse_group_by]) for (ptr = list->nod_arg, end = ptr + list->nod_count; ptr < end; ptr++) { compile_expression(*ptr, request, true); } if (node->nod_arg[e_rse_having]) compile_expression(node->nod_arg[e_rse_having], request, true); // If we didn't allocate a new request block, say so by returning NULL if (request == original_request) return NULL; request->req_flags |= REQ_rse_compiled; return request; } static qli_nod* compile_statement( qli_nod* node, qli_req* request, bool internal_flag) { /************************************** * * c o m p i l e _ s t a t e m e n t * ************************************** * * Functional description * Compile a statement. Actually, just dispatch, passing along * the parent request. * **************************************/ switch (node->nod_type) { case nod_assign: return compile_assignment(node, request, internal_flag); case nod_erase: return compile_erase(node, request); case nod_commit_retaining: return node; case nod_for: case nod_report_loop: return compile_for(node, request, internal_flag); case nod_list: { qli_nod* result = NULL; qli_nod** ptr = node->nod_arg; for (const qli_nod* const* const end = ptr + node->nod_count; ptr < end; ptr++) { if (compile_statement(*ptr, request, internal_flag)) result = node; } return result; } case nod_modify: return compile_modify(node, request, internal_flag); case nod_output: compile_expression(node->nod_arg[e_out_file], request, false); compile_statement(node->nod_arg[e_out_statement], request, false); return node; case nod_print: return compile_print(node, request); case nod_list_fields: return compile_list_fields(node, request); case nod_repeat: return compile_repeat(node, request, internal_flag); case nod_report: return compile_report(node, request); case nod_store: return compile_store(node, request, internal_flag); case nod_if: return compile_if(node, request, internal_flag); case nod_abort: if (node->nod_count) compile_expression(node->nod_arg[0], 0, false); return node; default: ERRQ_bugcheck(360); // Msg360 not yet implemented (compile_statement) return NULL; } } static qli_nod* compile_statistical( qli_nod* node, qli_req* old_request, bool internal_flag) { /************************************** * * c o m p i l e _ s t a t i s t i c a l * ************************************** * * Functional description * Compile a statistical expression. The expression may or may not * request a separate request. * **************************************/ qli_msg* old_send = NULL; qli_msg* old_receive = NULL; /* If a default value is present, compile it outside the context of the rse. */ qli_nod* value = node->nod_arg[e_stt_default]; if (value) compile_expression(value, old_request, true); /* Compile rse. This will set up both send and receive message. If the messages aren't needed, we can release them later. */ if (!internal_flag) old_request = NULL; if (old_request) { old_send = old_request->req_send; old_receive = old_request->req_receive; } qli_msg* send; qli_msg* receive; qli_req* request = compile_rse(node->nod_arg[e_stt_rse], old_request, internal_flag, &send, &receive, 0); if (request) { node->nod_arg[e_stt_request] = (qli_nod*) request; } else request = old_request; // If there is a value, compile it here if (!internal_flag) { qli_par* parameter = make_parameter(request->req_receive, 0); node->nod_import = parameter; make_descriptor(node, ¶meter->par_desc); } value = node->nod_arg[e_stt_value]; if (value) compile_expression(value, request, true); if (old_request) { old_request->req_send = old_send; old_request->req_receive = old_receive; } if (!internal_flag && request == old_request && computable(node, request)) { make_descriptor(node, &node->nod_desc); return make_reference(node, request->req_receive); } if ((send != old_send) && !send->msg_parameters) { release_message(send); send = NULL; } node->nod_arg[e_stt_receive] = (qli_nod*) receive; node->nod_arg[e_stt_send] = (qli_nod*) send; return node; } static qli_nod* compile_store( qli_nod* node, qli_req* request, bool internal_flag) { /************************************** * * c o m p i l e _ s t o r e * ************************************** * * Functional description * Compile a STORE statement. * **************************************/ // Find or make up request for statement qli_ctx* context = (qli_ctx*) node->nod_arg[e_sto_context]; qli_rel* relation = context->ctx_relation; if (!request || request->req_database != relation->rel_database) { request = make_request(relation->rel_database); node->nod_arg[e_sto_request] = (qli_nod*) request; } request->req_database->dbb_flags |= DBB_updates; context->ctx_request = request; context->ctx_context = request->req_context++; qli_msg* send = make_message(request); context->ctx_message = request->req_send = send; /* If nothing is required for sub-statement, and no data is required in either direction, we don't need to execute the statement. */ if (internal_flag) internal_flag = computable(node->nod_arg[e_sto_statement], request); if (!compile_statement (node->nod_arg[e_sto_statement], request, internal_flag) && !send->msg_parameters) { node->nod_flags |= NOD_remote; release_message(send); return NULL; } node->nod_arg[e_sto_send] = (qli_nod*) send; return node; } static bool computable( qli_nod* node, qli_req* request) { /************************************** * * c o m p u t a b l e * ************************************** * * Functional description * Check to see if a value is computable within the context of a * given request. * **************************************/ qli_nod** ptr; const qli_nod* const* end; qli_nod* sub; qli_ctx* context; qli_map* map; switch (node->nod_type) { case nod_max: case nod_min: case nod_count: case nod_average: case nod_total: case nod_from: if ((sub = node->nod_arg[e_stt_rse]) && !computable(sub, request)) return false; if ((sub = node->nod_arg[e_stt_value]) && !computable(sub, request)) return false; if ((sub = node->nod_arg[e_stt_default]) && !computable(sub, request)) return false; return true; case nod_rse: if (!request) return false; if ((sub = node->nod_arg[e_rse_first]) && !computable(sub, request)) return false; for (ptr = node->nod_arg + e_rse_count, end = ptr + node->nod_count; ptr < end; ptr++) { context = (qli_ctx*) * ptr; if (context->ctx_stream) { if (!computable(context->ctx_stream, request)) return false; } else if (context->ctx_relation->rel_database != request->req_database) { return false; } context->ctx_request = request; } if ((sub = node->nod_arg[e_rse_boolean]) && !computable(sub, request)) return false; return true; case nod_field: if (sub = node->nod_arg[e_fld_subs]) for (ptr = sub->nod_arg, end = ptr + sub->nod_count; ptr < end; ptr++) { if (*ptr && !computable(*ptr, request)) return false; } context = (qli_ctx*) node->nod_arg[e_fld_context]; return (request == context->ctx_request); case nod_map: map = (qli_map*) node->nod_arg[e_map_map]; return computable(map->map_node, request); case nod_print: case nod_report: case nod_abort: case nod_repeat: case nod_commit_retaining: case nod_rpt_average: case nod_rpt_max: case nod_rpt_min: case nod_rpt_total: case nod_rpt_count: case nod_running_total: case nod_running_count: case nod_edit_blob: case nod_prompt: case nod_variable: case nod_format: return false; case nod_null: case nod_constant: case nod_user_name: return true; case nod_for: if ((qli_req*) node->nod_arg[e_for_request] != request) return false; if (!computable(node->nod_arg[e_for_rse], request) || !computable(node->nod_arg[e_for_statement], request)) return false; return true; case nod_store: if ((qli_req*) node->nod_arg[e_sto_request] != request) return false; return computable(node->nod_arg[e_sto_statement], request); case nod_modify: context = (qli_ctx*) node->nod_arg[e_mod_count]; if (context->ctx_source->ctx_request != request) return false; return computable(node->nod_arg[e_mod_statement], request); case nod_erase: context = (qli_ctx*) node->nod_arg[e_era_context]; if (context->ctx_source->ctx_request != request) return false; return true; case nod_unique: case nod_any: if (node->nod_arg[e_any_request] != (qli_nod*) request) return false; return (computable(node->nod_arg[e_any_rse], request)); case nod_agg_max: case nod_agg_min: case nod_agg_count: case nod_agg_average: case nod_agg_total: if (sub = node->nod_arg[e_stt_value]) return (computable(sub, request)); case nod_assign: if (node->nod_arg[e_asn_valid]) { sub = node->nod_arg[e_asn_from]; if (sub->nod_type == nod_prompt) /* Try to do validation in QLI as soon as the user responds to the prompt */ return false; } case nod_list: case nod_if: 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_containing: case nod_starts: case nod_missing: case nod_and: case nod_or: case nod_not: case nod_add: case nod_subtract: case nod_multiply: case nod_divide: case nod_negate: case nod_concatenate: case nod_function: case nod_substr: for (ptr = node->nod_arg, end = ptr + node->nod_count; ptr < end; ptr++) if (*ptr && !computable(*ptr, request)) return false; return true; default: ERRQ_bugcheck(361); // Msg361 computable: not yet implemented return false; } } static void make_descriptor( qli_nod* node, dsc* desc) { /************************************** * * m a k e _ d e s c r i p t o r * ************************************** * * Functional description * Fill out a descriptor based on an expression. * **************************************/ qli_fld* field; qli_par* parameter; qli_map* map; qli_fun* function; USHORT dtype; dsc desc1; desc1.dsc_dtype = 0; desc1.dsc_scale = 0; desc1.dsc_length = 0; desc1.dsc_sub_type = 0; desc1.dsc_address = NULL; desc1.dsc_flags = 0; dsc desc2 = desc1; switch (node->nod_type) { case nod_field: case nod_variable: field = (qli_fld*) node->nod_arg[e_fld_field]; desc->dsc_dtype = field->fld_dtype; desc->dsc_length = field->fld_length; desc->dsc_scale = field->fld_scale; desc->dsc_sub_type = field->fld_sub_type; return; case nod_reference: parameter = node->nod_import; *desc = parameter->par_desc; return; case nod_map: map = (qli_map*) node->nod_arg[e_map_map]; make_descriptor(map->map_node, desc); return; case nod_function: function = (qli_fun*) node->nod_arg[e_fun_function]; *desc = function->fun_return; return; case nod_constant: case nod_prompt: case nod_format: *desc = node->nod_desc; return; case nod_concatenate: make_descriptor(node->nod_arg[0], &desc1); make_descriptor(node->nod_arg[1], &desc2); desc->dsc_scale = 0; desc->dsc_dtype = dtype_varying; desc->dsc_length = sizeof(USHORT) + string_length(&desc1) + string_length(&desc2); if (desc1.dsc_dtype <= dtype_any_text) desc->dsc_sub_type = desc1.dsc_sub_type; else desc->dsc_sub_type = ttype_ascii; return; case nod_add: case nod_subtract: make_descriptor(node->nod_arg[0], &desc1); make_descriptor(node->nod_arg[1], &desc2); if ((desc1.dsc_dtype == dtype_text && desc1.dsc_length >= 9) || (desc2.dsc_dtype == dtype_text && desc2.dsc_length >= 9)) { dtype = dtype_double; } else dtype = MAX(desc1.dsc_dtype, desc2.dsc_dtype); switch (dtype) { case dtype_sql_time: case dtype_sql_date: case dtype_timestamp: node->nod_flags |= nod_date; if (node->nod_type == nod_add) { desc->dsc_dtype = dtype; desc->dsc_length = (dtype == dtype_timestamp) ? 8 : 4; break; } case dtype_varying: case dtype_cstring: case dtype_text: case dtype_double: case dtype_real: case dtype_long: desc->dsc_dtype = dtype_double; desc->dsc_length = sizeof(double); break; default: desc->dsc_dtype = dtype_long; desc->dsc_length = sizeof(SLONG); desc->dsc_scale = MIN(desc1.dsc_scale, desc2.dsc_scale); break; } return; case nod_multiply: make_descriptor(node->nod_arg[0], &desc1); make_descriptor(node->nod_arg[1], &desc2); if ((desc1.dsc_dtype == dtype_text && desc1.dsc_length >= 9) || (desc2.dsc_dtype == dtype_text && desc2.dsc_length >= 9)) { dtype = dtype_double; } else dtype = MAX(desc1.dsc_dtype, desc2.dsc_dtype); switch (dtype) { case dtype_varying: case dtype_cstring: case dtype_text: case dtype_real: case dtype_double: case dtype_long: desc->dsc_dtype = dtype_double; desc->dsc_length = sizeof(double); break; default: desc->dsc_dtype = dtype_long; desc->dsc_length = sizeof(SLONG); desc->dsc_scale = desc1.dsc_scale + desc2.dsc_scale; break; } return; case nod_agg_average: case nod_agg_max: case nod_agg_min: case nod_agg_total: case nod_average: case nod_max: case nod_min: case nod_total: case nod_from: case nod_rpt_average: case nod_rpt_max: case nod_rpt_min: case nod_rpt_total: case nod_running_total: make_descriptor(node->nod_arg[e_stt_value], desc); if (desc->dsc_dtype == dtype_short) { desc->dsc_dtype = dtype_long; desc->dsc_length = sizeof(SLONG); } else if (desc->dsc_dtype == dtype_real) { desc->dsc_dtype = dtype_double; desc->dsc_length = sizeof(double); } return; case nod_null: desc->dsc_dtype = dtype_long; desc->dsc_length = sizeof(SLONG); desc->dsc_missing = DSC_missing; return; case nod_count: case nod_agg_count: case nod_running_count: case nod_rpt_count: desc->dsc_dtype = dtype_long; desc->dsc_length = sizeof(SLONG); return; case nod_divide: desc->dsc_dtype = dtype_double; desc->dsc_length = sizeof(double); return; case nod_negate: make_descriptor(node->nod_arg[0], desc); return; case nod_user_name: desc->dsc_dtype = dtype_varying; desc->dsc_scale = 0; desc->dsc_length = sizeof(USHORT) + 16; return; case nod_substr: default: ERRQ_bugcheck(362); // Msg362 make_descriptor: not yet implemented } } static qli_msg* make_message( qli_req* request) { /************************************** * * m a k e _ m e s s a g e * ************************************** * * Functional description * Allocate a message block for a request. * **************************************/ qli_msg* message = (qli_msg*) ALLOCDV(type_msg, 0); message->msg_request = request; message->msg_next = request->req_messages; request->req_messages = message; message->msg_number = request->req_msg_number++; return message; } static void make_missing_reference( qli_par* parameter) { /************************************** * * m a k e _ m i s s i n g _ r e f e r e n c e * ************************************** * * Functional description * Make up a parameter to pass a missing value. * **************************************/ if (parameter->par_missing) return; qli_par* missing = (qli_par*) ALLOCD(type_par); parameter->par_missing = missing; missing->par_message = parameter->par_message; missing->par_desc.dsc_dtype = dtype_short; missing->par_desc.dsc_length = sizeof(SSHORT); } static qli_par* make_parameter( qli_msg* message, qli_nod* node) { /************************************** * * m a k e _ p a r a m e t e r * ************************************** * * Functional description * Make a parameter block and hang it off a message block. * To make prompts come out in the right order, insert the * new prompt at the end of the prompt list. Sigh. * **************************************/ qli_par** ptr; for (ptr = &message->msg_parameters; *ptr; ptr = &(*ptr)->par_next); qli_par* parm = (qli_par*) ALLOCD(type_par); *ptr = parm; parm->par_message = message; if (node && (node->nod_flags & NOD_parameter2)) make_missing_reference(parm); return parm; } static qli_nod* make_reference( qli_nod* node, qli_msg* message) { /************************************** * * m a k e _ r e f e r e n c e * ************************************** * * Functional description * Make a reference to a value to be computed in the * database context. Since a field can be referenced * several times, construct reference blocks linking * the single field to the single parameter. (I think.) * In any event, if a parameter for a field exists, * use it rather than generating an new one. Make it * parameter2 style if necessary. * **************************************/ qli_par* parm; if (!message) ERRQ_bugcheck(363); // Msg363 missing message // Look for an existing field reference for (parm = message->msg_parameters; parm; parm = parm->par_next) if (CMP_node_match(parm->par_value, node)) break; // Parameter doesn't exist -- make a new one. if (!parm) { parm = make_parameter(message, node); parm->par_value = node; parm->par_desc = node->nod_desc; } qli_nod* reference = (qli_nod*) ALLOCDV(type_nod, 1); reference->nod_type = nod_reference; reference->nod_arg[0] = node; reference->nod_desc = parm->par_desc; reference->nod_import = parm; return reference; } static qli_req* make_request( DBB dbb) { /************************************** * * m a k e _ r e q u e s t * ************************************** * * Functional description * Make a request block for a database. * **************************************/ qli_req* request = (qli_req*) ALLOCD(type_req); request->req_database = dbb; request->req_next = QLI_requests; QLI_requests = request; dbb->dbb_flags |= DBB_active; if (!(dbb->dbb_transaction)) MET_transaction(nod_start_trans, dbb); return request; } static void release_message( qli_msg* message) { /************************************** * * r e l e a s e _ m e s s a g e * ************************************** * * Functional description * A message block is unneeded, so release it. * **************************************/ qli_req* request = message->msg_request; qli_msg** ptr; for (ptr = &request->req_messages; *ptr; ptr = &(*ptr)->msg_next) if (*ptr == message) break; if (!*ptr) ERRQ_bugcheck(364); // Msg 364 lost message *ptr = message->msg_next; ALLQ_release((FRB) message); } static int string_length(const dsc* desc) { /************************************** * * s t r i n g _ l e n g t h * ************************************** * * Functional description * Estimate length of string based on descriptor. * **************************************/ return DSC_string_length(desc); }