//____________________________________________________________ // // PROGRAM: C preprocessor // MODULE: cmp.cpp // DESCRIPTION: Request 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): ______________________________________. // TMN (Mike Nordell) 11.APR.2001 - Reduce compiler warnings // // //____________________________________________________________ // // $Id: cmp.cpp,v 1.32 2004-06-03 07:31:09 robocop Exp $ // #include "firebird.h" #include #include #include "../jrd/ibase.h" #include "../gpre/gpre.h" #include "../jrd/align.h" #include "../gpre/cmd_proto.h" #include "../gpre/cme_proto.h" #include "../gpre/cmp_proto.h" #include "../gpre/gpre_proto.h" #include "../gpre/gpre_meta.h" #include "../gpre/msc_proto.h" #include "../gpre/par_proto.h" static void cmp_any(gpre_req*); static void cmp_assignment(GPRE_NOD, gpre_req*); static void cmp_blob(blb*, bool); static void cmp_blr(gpre_req*); static void cmp_erase(act*, gpre_req*); static void cmp_fetch(act*); static void cmp_field(gpre_req*, const gpre_fld*, const ref*); static void cmp_for(gpre_req*); static void cmp_loop(gpre_req*); static void cmp_modify(act*, gpre_req*); static void cmp_port(gpre_port*, gpre_req*); static void cmp_procedure(gpre_req*); static void cmp_ready(gpre_req*); static void cmp_sdl_fudge(gpre_req*, SLONG); static bool cmp_sdl_loop(gpre_req*, USHORT, const slc*, const ary*); static void cmp_sdl_number(gpre_req*, SLONG); static void cmp_sdl_subscript(gpre_req*, USHORT, const slc*, const ary*); static void cmp_sdl_value(gpre_req*, const gpre_nod*); static void cmp_set_generator(gpre_req*); static void cmp_slice(gpre_req*); static void cmp_store(gpre_req*); static void expand_references(REF); static gpre_port* make_port(gpre_req*, REF); static void make_receive(gpre_port*, gpre_req*); static void make_send(gpre_port*, gpre_req*); static void update_references(REF); static GPRE_NOD lit0, lit1; static gpre_fld* eof_field; static gpre_fld* count_field; static gpre_fld* slack_byte_field; static USHORT next_ident; //#define STUFF(blr) *request->req_blr++ = (UCHAR)(blr) //#define STUFF_WORD(blr) {STUFF (blr); STUFF ((blr) >> 8);} //#define STUFF_LONG(blr) {STUFF (blr); STUFF ((blr) >> 8); STUFF ((blr) >>16); STUFF ((blr) >> 24);} //#define STUFF_INT(blr) STUFF (blr); STUFF ((blr) >> 8); STUFF ((blr) >> 16); STUFF ((blr) >> 24) const int MAX_TPB = 4000; //____________________________________________________________ // // Check to make sure that generated blr string is not about to // over the memory allocated for it. If so, allocate an extra // couple of hundred bytes to be safe. // void CMP_check( gpre_req* request, SSHORT min_reqd) { int length = request->req_blr - request->req_base; if (!(min_reqd) && (length < request->req_length - 100)) return; const int n = ((length + min_reqd + 100) > request->req_length * 2) ? length + min_reqd + 100 : request->req_length * 2; UCHAR* const old = request->req_base; const UCHAR* q = old; UCHAR* p = (UCHAR *) MSC_alloc(n); request->req_base = p; request->req_length = n; request->req_blr = request->req_base + length; do { *p++ = *q++; } while (--length); MSC_free(old); } //____________________________________________________________ // // Compile a single request, but do not generate any text. // Generate port blocks, assign parameter numbers, message // numbers, and internal idents. Compute length of request. // void CMP_compile_request( gpre_req* request) { // if there isn't a request handle specified, make one! if (!request->req_handle && (request->req_type != REQ_procedure)) { request->req_handle = (TEXT *) MSC_alloc(20); sprintf(request->req_handle, gpreGlob.ident_pattern, CMP_next_ident()); } if (!request->req_trans) request->req_trans = gpreGlob.transaction_name; if (!request->req_request_level) request->req_request_level = "0"; request->req_ident = CMP_next_ident(); // If this is an SQL blob cursor, compile the blob and get out fast. if (request->req_flags & (REQ_sql_blob_open | REQ_sql_blob_create)) { for (blb* blob = request->req_blobs; blob; blob = blob->blb_next) cmp_blob(blob, true); return; } // Before we get too far, make sure an eof field has been // constructed. If not, do so now. ref* reference; if (!eof_field) { eof_field = MET_make_field(gpreGlob.utility_name, dtype_short, sizeof(SSHORT), false); count_field = MET_make_field(gpreGlob.count_name, dtype_long, sizeof(SLONG), false); slack_byte_field = MET_make_field(gpreGlob.slack_name, dtype_text, 1, false); reference = (REF) MSC_alloc(REF_LEN); reference->ref_value = "0"; lit0 = MSC_unary(nod_literal, (GPRE_NOD) reference); reference = (REF) MSC_alloc(REF_LEN); reference->ref_value = "1"; lit1 = MSC_unary(nod_literal, (GPRE_NOD) reference); } // Handle different request types differently switch (request->req_type) { case REQ_create_database: case REQ_ddl: CMD_compile_ddl(request); return; case REQ_slice: cmp_slice(request); return; case REQ_ready: cmp_ready(request); return; case REQ_procedure: cmp_procedure(request); return; } // expand any incomplete references or values expand_references(request->req_references); expand_references(request->req_values); // Initialize the blr string request->req_blr = request->req_base = (UCHAR *) MSC_alloc(500); request->req_length = 500; if (request->req_flags & REQ_blr_version4) request->add_byte(blr_version4); else request->add_byte(blr_version5); // If there are values to be transmitted, make a port // to hold them if (request->req_values) request->req_vport = make_port(request, request->req_values); #ifdef SCROLLABLE_CURSORS // If there is an asynchronous message to be sent, make a port for it if (request->req_flags & REQ_sql_cursor && request->req_database->dbb_base_level >= 5) { gpre_fld* direction_field = MET_make_field("direction", dtype_short, sizeof(SSHORT), false); gpre_fld* offset_field = MET_make_field("offset", dtype_long, sizeof(SLONG), false); reference = request->req_avalues; reference->ref_field = direction_field; reference = reference->ref_next; reference->ref_field = offset_field; request->req_aport = make_port(request, request->req_avalues); } #endif // If this is a FOR type request, an eof field reference needs // to be generated. Do it. if (request->req_type == REQ_for || request->req_type == REQ_cursor || request->req_type == REQ_any) { reference = (REF) MSC_alloc(REF_LEN); reference->ref_field = eof_field; reference->ref_next = request->req_references; } else if (request->req_type == REQ_mass_update) { reference = (REF) MSC_alloc(REF_LEN); reference->ref_field = count_field; reference->ref_next = request->req_references; } else reference = request->req_references; // Assume that a general port needs to be constructed. gpre_port* port; if ((request->req_type != REQ_insert) && (request->req_type != REQ_store2) && (request->req_type != REQ_set_generator)) { request->req_primary = port = make_port(request, reference); } // Loop thru actions looking for something interesting to do. Not // all action types are "interesting", so don't worry about missing // ones. upd* update; for (act* action = request->req_actions; action; action = action->act_next) { switch (action->act_type) { case ACT_modify: case ACT_erase: case ACT_update: update = (upd*) action->act_object; expand_references(update->upd_references); update->upd_port = make_port(request, update->upd_references); if (!request->req_sync) request->req_sync = make_port(request, 0); break; case ACT_store: if (request->req_type == REQ_store2) { request->req_primary = make_port(request, action->act_object); update_references(request->req_primary->por_references); } break; case ACT_store2: update = (upd*) action->act_object; expand_references(update->upd_references); update->upd_port = make_port(request, update->upd_references); update_references(update->upd_port->por_references); break; case ACT_select: case ACT_fetch: cmp_fetch(action); break; } } cmp_blr(request); request->add_byte(blr_eoc); // Compute out final blr lengths request->req_length = request->req_blr - request->req_base; request->req_blr = request->req_base; // Finally, assign identifiers to any blobs that may have been referenced for (blb* blob = request->req_blobs; blob; blob = blob->blb_next) cmp_blob(blob, false); } //____________________________________________________________ // // Stuff field datatype info into request. // Text fields are not remapped to process text type. // This is used by the CAST & COLLATE operators to // indicate cast datatypes. // void CMP_external_field( gpre_req* request, const gpre_fld* field) { switch (field->fld_dtype) { case dtype_cstring: request->add_byte(blr_text2); request->add_word(field->fld_ttype); request->add_word(field->fld_length - 1); break; case dtype_text: request->add_byte(blr_text2); request->add_word(field->fld_ttype); request->add_word(field->fld_length); break; case dtype_varying: request->add_byte(blr_varying2); request->add_word(field->fld_ttype); request->add_word(field->fld_length); break; default: cmp_field(request, field, 0); break; } } //____________________________________________________________ // // Initialize (or re-initialize) for request compilation. This is // called at most once per module. // void CMP_init(void) { lit0 = lit1 = NULL; count_field = slack_byte_field = eof_field = NULL; next_ident = 0; } //____________________________________________________________ // // Give out next identifier. // USHORT CMP_next_ident(void) { return next_ident++; } //____________________________________________________________ // // Stuff a symbol. // void CMP_stuff_symbol( gpre_req* request, const gpre_sym* symbol) { request->add_byte(strlen(symbol->sym_string)); for (const TEXT* p = symbol->sym_string; *p; p++) request->add_byte(*p); } //____________________________________________________________ // // Take a transaction block with (potentially) a // lot of relation lock blocks, and generate TPBs // // We'll always generate TPB's, and link them // into the DBB for that database so they get // generated. If there's no lock list, we generate // a simple TPB for every database in the program. // If there is a lock list, we generate a more complex // TPB for each database referenced. // void CMP_t_start( gpre_tra* trans) { char rrl_buffer[MAX_TPB]; char tpb_buffer[MAX_TRA_OPTIONS + 1]; // fill out a standard tpb buffer ahead of time so we know // how large it is char* text = tpb_buffer; *text++ = isc_tpb_version1; *text++ = (trans->tra_flags & TRA_ro) ? isc_tpb_read : isc_tpb_write; if (trans->tra_flags & TRA_con) *text++ = isc_tpb_consistency; else if (trans->tra_flags & TRA_read_committed) *text++ = isc_tpb_read_committed; else *text++ = isc_tpb_concurrency; *text++ = (trans->tra_flags & TRA_nw) ? isc_tpb_nowait : isc_tpb_wait; if (trans->tra_flags & TRA_read_committed) { *text++ = (trans->tra_flags & TRA_rec_version) ? isc_tpb_rec_version : isc_tpb_no_rec_version; } if (trans->tra_flags & TRA_autocommit) *text++ = isc_tpb_autocommit; if (trans->tra_flags & TRA_no_auto_undo) *text++ = isc_tpb_no_auto_undo; *text = 0; const USHORT tpb_len = text - tpb_buffer; for (dbb* database = gpreGlob.isc_databases; database; database = database->dbb_next) { /* * figure out if this is a simple transaction or a reserving * transaction. Allocate a TPB of the right size in either * case. */ tpb* new_tpb = 0; if (trans->tra_flags & TRA_inc) { if (database->dbb_flags & DBB_in_trans) { new_tpb = (tpb*) MSC_alloc(TPB_LEN(tpb_len)); new_tpb->tpb_length = tpb_len; database->dbb_flags &= ~DBB_in_trans; } else continue; } else if (!(trans->tra_flags & TRA_rrl)) { new_tpb = (tpb*) MSC_alloc(TPB_LEN(tpb_len)); new_tpb->tpb_length = tpb_len; } else if (database->dbb_rrls) { char* p = rrl_buffer; for (const rrl* lock_block = database->dbb_rrls; lock_block; lock_block = lock_block->rrl_next) { *p++ = lock_block->rrl_lock_mode; const char* q = lock_block->rrl_relation->rel_symbol->sym_string; *p++ = strlen(q); while (*q) *p++ = *q++; *p++ = lock_block->rrl_lock_level; } *p = 0; const USHORT buff_len = (p - rrl_buffer); new_tpb = (tpb*) MSC_alloc(TPB_LEN(buff_len + tpb_len)); new_tpb->tpb_length = buff_len + tpb_len; database->dbb_rrls = NULL; } else // this database isn't referenced continue; /* link this into the TPB chains (gpre_tra and DBB) */ new_tpb->tpb_database = database; new_tpb->tpb_dbb_next = database->dbb_tpbs; database->dbb_tpbs = new_tpb; new_tpb->tpb_tra_next = trans->tra_tpb; trans->tra_tpb = new_tpb; trans->tra_db_count++; // fill in the standard TPB and concatenate the relation names new_tpb->tpb_ident = CMP_next_ident(); text = reinterpret_cast(new_tpb->tpb_string); for (const char* pb = tpb_buffer; *pb;) *text++ = *pb++; if (trans->tra_flags & TRA_rrl) strcpy(text, rrl_buffer); } } //____________________________________________________________ // // Generate blr tree for free standing ANY expression. // static void cmp_any( gpre_req* request) { // Build the primary send statement gpre_port* port = request->req_primary; make_send(port, request); request->add_byte(blr_if); request->add_byte(blr_any); CME_rse(request->req_rse, request); gpre_nod* value = MSC_unary(nod_value, (GPRE_NOD) port->por_references); // Make a send to signal end of file request->add_byte(blr_assignment); CME_expr(lit1, request); CME_expr(value, request); request->add_byte(blr_assignment); CME_expr(lit0, request); CME_expr(value, request); port = request->req_vport; if (port) { for (ref* reference = port->por_references; reference; reference = reference->ref_next) { ref* source = (ref*) MSC_alloc(REF_LEN); reference->ref_source = source; source->ref_ident = CMP_next_ident(); } } } //____________________________________________________________ // // Compile a build assignment statement. // static void cmp_assignment( GPRE_NOD node, gpre_req* request) { CMP_check(request, 0); request->add_byte(blr_assignment); CME_expr(node->nod_arg[0], request); CME_expr(node->nod_arg[1], request); } //____________________________________________________________ // // Compile a blob parameter block, if required. // static void cmp_blob(blb* blob, bool sql_flag) { blob->blb_ident = CMP_next_ident(); blob->blb_buff_ident = CMP_next_ident(); blob->blb_len_ident = CMP_next_ident(); if (sql_flag) { for (ref* reference = blob->blb_reference; reference; reference = reference->ref_next) { reference->ref_port = make_port(reference->ref_context->ctx_request, reference); } } if (!blob->blb_const_to_type && !blob->blb_type) return; blob->blb_bpb_ident = CMP_next_ident(); UCHAR* p = blob->blb_bpb; *p++ = isc_bpb_version1; if (blob->blb_const_to_type) { *p++ = isc_bpb_target_type; *p++ = 2; *p++ = (UCHAR) blob->blb_const_to_type; *p++ = blob->blb_const_to_type >> 8; } if (blob->blb_const_from_type) { *p++ = isc_bpb_source_type; *p++ = 2; *p++ = (UCHAR) blob->blb_const_from_type; *p++ = blob->blb_const_from_type >> 8; } if (blob->blb_type) { *p++ = isc_bpb_type; *p++ = 1; *p++ = (UCHAR) blob->blb_type; } if (blob->blb_from_charset) { // create bpb instruction for source character set *p++ = isc_bpb_source_interp; *p++ = 2; *p++ = (UCHAR) blob->blb_from_charset; *p++ = blob->blb_from_charset >> 8; } if (blob->blb_to_charset) { // create bpb instruction for target character set *p++ = isc_bpb_target_interp; *p++ = 2; *p++ = (UCHAR) blob->blb_to_charset; *p++ = blob->blb_to_charset >> 8; } blob->blb_bpb_length = p - blob->blb_bpb; } //____________________________________________________________ // // Build a request tree for a request. // static void cmp_blr( gpre_req* request) { request->add_byte(blr_begin); // build message definition for each port gpre_port* port; for (port = request->req_ports; port; port = port->por_next) cmp_port(port, request); // See if there is a receive to be built if ((request->req_type == REQ_store) || (request->req_type == REQ_store2)) port = request->req_primary; else port = request->req_vport; if (port) make_receive(port, request); // Compile up request switch (request->req_type) { case REQ_cursor: case REQ_for: cmp_for(request); break; case REQ_insert: case REQ_store: case REQ_store2: cmp_store(request); break; case REQ_mass_update: cmp_loop(request); break; case REQ_any: cmp_any(request); break; case REQ_set_generator: cmp_set_generator(request); break; default: CPR_error("*** unknown request type node ***"); } request->add_byte(blr_end); } //____________________________________________________________ // // Generate blr for ERASE action. // static void cmp_erase( act* action, gpre_req* request) { upd* update = (upd*) action->act_object; gpre_ctx* source = update->upd_source; make_receive(update->upd_port, request); if (action->act_error) request->add_byte(blr_handler); request->add_byte(blr_erase); request->add_byte(source->ctx_internal); } //____________________________________________________________ // // Go over an SQL fetch list and expand // references to indicator variables. // Not tough. // static void cmp_fetch( act* action) { gpre_nod* list = (GPRE_NOD) action->act_object; if (!list) return; if (list->nod_type != nod_list) CPR_error("invalid fetch action"); gpre_nod** ptr = list->nod_arg; for (const gpre_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++) { ref* var = (REF) *ptr; if (var->ref_null_value && !var->ref_null) if (var->ref_friend) var->ref_null = var->ref_friend->ref_null; } } //____________________________________________________________ // // Stuff field datatype info into request. // static void cmp_field( gpre_req* request, const gpre_fld* field, const ref* reference) { if (reference && reference->ref_value && (reference->ref_flags & REF_array_elem)) { field = field->fld_array; } fb_assert(field != NULL); fb_assert(field->fld_dtype); fb_assert(field->fld_length); switch (field->fld_dtype) { case dtype_cstring: if (!(field->fld_flags & FLD_charset)) { request->add_byte(blr_cstring); request->add_word(field->fld_length); } else { // 3.2j has new, tagged blr instruction for cstring request->add_byte(blr_cstring2); request->add_word(field->fld_ttype); request->add_word(field->fld_length); } break; case dtype_text: if (!(field->fld_flags & FLD_charset)) { request->add_byte(blr_text); request->add_word(field->fld_length); } else { // 3.2j has new, tagged blr instruction for text too request->add_byte(blr_text2); request->add_word(field->fld_ttype); request->add_word(field->fld_length); } break; case dtype_varying: if (!(field->fld_flags & FLD_charset)) { request->add_byte(blr_varying); request->add_word(field->fld_length); } else { // 3.2j has new, tagged blr instruction for varying also request->add_byte(blr_varying2); request->add_word(field->fld_ttype); request->add_word(field->fld_length); } break; case dtype_short: request->add_byte(blr_short); request->add_byte(field->fld_scale); break; case dtype_long: request->add_byte(blr_long); request->add_byte(field->fld_scale); break; case dtype_blob: case dtype_quad: request->add_byte(blr_quad); request->add_byte(field->fld_scale); break; // ** Begin sql date/time/timestamp * case dtype_sql_date: request->add_byte(blr_sql_date); break; case dtype_sql_time: request->add_byte(blr_sql_time); break; case dtype_timestamp: request->add_byte(blr_timestamp); break; // ** Begin sql date/time/timestamp * case dtype_int64: request->add_byte(blr_int64); request->add_byte(field->fld_scale); break; case dtype_real: request->add_byte(blr_float); break; case dtype_double: if (gpreGlob.sw_d_float) request->add_byte(blr_d_float); else request->add_byte(blr_double); break; default: { TEXT s[50]; sprintf(s, "datatype %d not understood", field->fld_dtype); CPR_error(s); } } } //____________________________________________________________ // // Generate blr tree for for statement // static void cmp_for( gpre_req* request) { request->add_byte(blr_begin); if (request->req_type == REQ_cursor && request->req_sync) make_receive(request->req_sync, request); request->add_byte(blr_for); if (request->req_flags & REQ_sql_cursor) { if (!request->req_rse->rse_sort && !request->req_rse->rse_reduced) for (gpre_ctx* context = (gpre_ctx*) request->req_contexts; context; context = context->ctx_next) { if ((context->ctx_scope_level == request->req_scope_level) && context->ctx_procedure) { request->add_byte(blr_stall); break; } } } CME_rse(request->req_rse, request); // Loop thru actions looking for primary port. While we're at it, // count the number of update actions. bool updates = false; for (act* action = request->req_actions; action; action = action->act_next) { switch (action->act_type) { case ACT_modify: case ACT_update: case ACT_erase: updates = true; break; } } if (updates) request->add_byte(blr_begin); // Build the primary send statement gpre_port* port = request->req_primary; make_send(port, request); gpre_nod* field = MSC_node(nod_field, 1); gpre_nod* value = MSC_node(nod_value, 1); request->add_byte(blr_begin); for (ref* reference = port->por_references; reference; reference = reference->ref_next) { CMP_check(request, 0); if (reference->ref_master) continue; request->add_byte(blr_assignment); if (reference->ref_field == eof_field) { request->req_eof = reference; CME_expr(lit1, request); } else if (reference->ref_field == slack_byte_field) CME_expr(lit0, request); else { if (reference->ref_expr) CME_expr(reference->ref_expr, request); else { field->nod_arg[0] = (GPRE_NOD) reference; CME_expr(field, request); } } value->nod_arg[0] = (GPRE_NOD) reference; CME_expr(value, request); } request->add_byte(blr_end); // If there are any actions, handle them here if (updates) { request->add_byte(blr_label); request->add_byte(0); request->add_byte(blr_loop); request->add_byte(blr_select); make_receive(request->req_sync, request); request->add_byte(blr_leave); request->add_byte(0); for (act* action = request->req_actions; action; action = action->act_next) { switch (action->act_type) { case ACT_endmodify: case ACT_update: cmp_modify(action, request); break; case ACT_erase: cmp_erase(action, request); break; } } request->add_byte(blr_end); request->add_byte(blr_end); } // Make a send to signal end of file make_send(port, request); request->add_byte(blr_assignment); CME_expr(lit0, request); value->nod_arg[0] = (GPRE_NOD) request->req_eof; CME_expr(value, request); request->add_byte(blr_end); } //____________________________________________________________ // // Compile a mass looping update statement. // static void cmp_loop( gpre_req* request) { gpre_nod* node = request->req_node; gpre_rse* selection = request->req_rse; gpre_port* primary = request->req_primary; make_send(primary, request); request->add_byte(blr_begin); gpre_nod* counter = MSC_node(nod_value, 1); for (ref* reference = primary->por_references; reference; reference = reference->ref_next) { if (reference->ref_field == count_field) counter->nod_arg[0] = (GPRE_NOD) reference; } request->add_byte(blr_assignment); CME_expr(lit0, request); CME_expr(counter, request); gpre_ctx* for_context = selection->rse_context[0]; gpre_ctx* update_context = request->req_update; request->add_byte(blr_for); CME_rse(selection, request); request->add_byte(blr_begin); switch (node->nod_type) { case nod_modify: { request->add_byte(blr_modify); request->add_byte(for_context->ctx_internal); request->add_byte(update_context->ctx_internal); gpre_nod* list = node->nod_arg[0]; request->add_byte(blr_begin); gpre_nod** ptr = list->nod_arg; for (const gpre_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++) { cmp_assignment(*ptr, request); } request->add_byte(blr_end); break; } case nod_store: { update_context = (gpre_ctx*) node->nod_arg[0]; request->add_byte(blr_store); CME_relation(update_context, request); gpre_nod* list = node->nod_arg[1]; request->add_byte(blr_begin); gpre_nod** ptr = list->nod_arg; for (const gpre_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++) { cmp_assignment(*ptr, request); } request->add_byte(blr_end); break; } case nod_erase: request->add_byte(blr_erase); request->add_byte(for_context->ctx_internal); break; default: fb_assert(false); } request->add_byte(blr_assignment); request->add_byte(blr_add); CME_expr(lit1, request); CME_expr(counter, request); CME_expr(counter, request); request->add_byte(blr_end); request->add_byte(blr_end); } //____________________________________________________________ // // Generate a receive and modify tree for a modify action. // static void cmp_modify( act* action, gpre_req* request) { upd* update = (upd*) action->act_object; gpre_port* port = update->upd_port; make_receive(port, request); if (action->act_error) request->add_byte(blr_handler); request->add_byte(blr_modify); request->add_byte(update->upd_source->ctx_internal); request->add_byte(update->upd_update->ctx_internal); // count the references and build an assignment block gpre_nod* list = update->upd_assignments; request->add_byte(blr_begin); gpre_nod** ptr = list->nod_arg; for (const gpre_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++) { const gpre_nod* field_node = (*ptr)->nod_arg[1]; const ref* reference = (REF) field_node->nod_arg[0]; if ((field_node->nod_type != nod_field) || !reference->ref_master) cmp_assignment(*ptr, request); } request->add_byte(blr_end); } //____________________________________________________________ // // Build a request tree for a request. // static void cmp_port( gpre_port* port, gpre_req* request) { // build message definition for each port request->add_byte(blr_message); request->add_byte(port->por_msg_number); request->add_word(port->por_count); for (ref* reference = port->por_references; reference; reference = reference->ref_next) { CMP_check(request, 0); cmp_field(request, reference->ref_field, reference); } } //____________________________________________________________ // // Compile a EXECUTE PROCEDURE request. // static void cmp_procedure( gpre_req* request) { act* action = request->req_actions; gpre_prc* procedure = (gpre_prc*) action->act_object; // Remember the order of the references. The exec_proc blr verb // requires parameters to be in parameter order which may be changed // when there references are expanded. gpre_lls* outputs = NULL; ref* reference = request->req_references; if (reference) { for (gpre_lls** list = &outputs; reference; reference = reference->ref_next) { MSC_push((GPRE_NOD) reference, list); list = &(*list)->lls_next; } } // Expand any incomplete references or values. expand_references(request->req_values); expand_references(request->req_references); request->req_blr = request->req_base = (UCHAR *) MSC_alloc(500); request->req_length = 500; if (request->req_flags & REQ_blr_version4) request->add_byte(blr_version4); else request->add_byte(blr_version5); request->add_byte(blr_begin); if (request->req_values) { request->req_vport = make_port(request, request->req_values); request->req_values = request->req_vport->por_references; } else request->req_count++; request->req_primary = make_port(request, request->req_references); request->req_references = request->req_primary->por_references; // build message definition for each port for (gpre_port* port = request->req_ports; port; port = port->por_next) cmp_port(port, request); if (request->req_values) { request->add_byte(blr_begin); make_send(request->req_vport, request); } if (gpreGlob.sw_ids) { request->add_byte(blr_exec_pid); request->add_word(procedure->prc_id); } else { request->add_byte(blr_exec_proc); CMP_stuff_symbol(request, procedure->prc_symbol); } if (procedure->prc_in_count) { request->add_word(procedure->prc_in_count); gpre_nod** ptr = request->req_node->nod_arg; for (const gpre_nod* const* const end = ptr + procedure->prc_in_count; ptr < end; ptr++) { CME_expr(*ptr, request); } } else request->add_word(0); if (request->req_references) { gpre_nod* node = MSC_node(nod_value, 1); request->add_word(procedure->prc_out_count); for (; outputs; outputs = outputs->lls_next) { node->nod_arg[0] = outputs->lls_object; CME_expr(node, request); } } else request->add_word(0); if (request->req_values) request->add_byte(blr_end); request->add_byte(blr_end); request->add_byte(blr_eoc); request->req_length = request->req_blr - request->req_base; request->req_blr = request->req_base; } //____________________________________________________________ // // Generate parameter buffer for READY with // buffercount. // static void cmp_ready( gpre_req* request) { dbb* db = request->req_database; act* action = request->req_actions; request->req_blr = request->req_base = MSC_alloc(250); request->req_length = 250; request->req_flags |= REQ_exp_hand; request->add_byte(isc_dpb_version1); if (db->dbb_allocation) { request->add_byte(isc_dpb_allocation); request->add_byte(4); request->add_long(db->dbb_allocation); } if (db->dbb_pagesize) { request->add_byte(isc_dpb_page_size); request->add_byte(4); request->add_long(db->dbb_pagesize); } if (db->dbb_buffercount) { request->add_byte(isc_dpb_num_buffers); request->add_byte(4); request->add_long(db->dbb_buffercount); } if (db->dbb_buffersize) { request->add_byte(isc_dpb_buffer_length); request->add_byte(4); request->add_long(db->dbb_buffersize); } if (db->dbb_users) { request->add_byte(isc_dpb_number_of_users); request->add_byte(4); request->add_long(db->dbb_users); } const TEXT* p; SSHORT l; if (db->dbb_c_user && !db->dbb_r_user) { request->add_byte(isc_dpb_user_name); l = strlen(db->dbb_c_user); request->add_byte(l); p = db->dbb_c_user; while (l--) request->add_byte(*p++); } if (db->dbb_c_password && !db->dbb_r_password) { request->add_byte(isc_dpb_password); l = strlen(db->dbb_c_password); request->add_byte(l); p = db->dbb_c_password; while (l--) request->add_byte(*p++); } if (db->dbb_c_sql_role && !db->dbb_r_sql_role) { request->add_byte(isc_dpb_sql_role_name); l = strlen(db->dbb_c_sql_role); request->add_byte(l); p = db->dbb_c_sql_role; while (l--) request->add_byte(*p++); } if (db->dbb_c_lc_messages && !db->dbb_r_lc_messages) { // Language must be an ASCII string request->add_byte(isc_dpb_lc_messages); l = strlen(db->dbb_c_lc_messages); request->add_byte(l); p = db->dbb_c_lc_messages; while (l--) request->add_byte(*p++); } if (db->dbb_c_lc_ctype && !db->dbb_r_lc_ctype) { // Character Format must be an ASCII string request->add_byte(isc_dpb_lc_ctype); l = strlen(db->dbb_c_lc_ctype); request->add_byte(l); p = db->dbb_c_lc_ctype; while (l--) request->add_byte(*p++); } *request->req_blr = 0; request->req_length = request->req_blr - request->req_base; if (request->req_length == 1) request->req_length = 0; request->req_blr = request->req_base; } //____________________________________________________________ // // Build in a fudge to bias the language specific subscript to the // declared array subscript [i.e. C subscripts are zero based]. // static void cmp_sdl_fudge( gpre_req* request, SLONG lower_bound) { switch (gpreGlob.sw_language) { case lang_c: case lang_cxx: case lang_internal: if (!lower_bound) break; request->add_byte(isc_sdl_add); cmp_sdl_number(request, lower_bound); break; case lang_fortran: if (lower_bound == 1) break; request->add_byte(isc_sdl_add); cmp_sdl_number(request, lower_bound - 1); break; } } //____________________________________________________________ // // Build an SDL loop for GET_SLICE/PUT_SLICE unless the upper and // lower bounds are constant. Return true if a loop has been built, // otherwise false. // static bool cmp_sdl_loop(gpre_req* request, USHORT index, const slc* slice, const ary* array) { const slc::slc_repeat* ranges = slice->slc_rpt + index; if (ranges->slc_upper == ranges->slc_lower) { return false; } const ary::ary_repeat* bounds = array->ary_rpt + index; request->add_byte(isc_sdl_do2); request->add_byte(slice->slc_parameters + index); cmp_sdl_fudge(request, bounds->ary_lower); cmp_sdl_value(request, ranges->slc_lower); cmp_sdl_fudge(request, bounds->ary_lower); cmp_sdl_value(request, ranges->slc_upper); return true; } //____________________________________________________________ // // Write the number in the 'smallest' // form possible to the SDL string. // static void cmp_sdl_number( gpre_req* request, SLONG number) { if ((number > -16) && (number < 15)) { request->add_byte(isc_sdl_tiny_integer); request->add_byte(number); } else if ((number > -32768) && (number < 32767)) { request->add_byte(isc_sdl_short_integer); request->add_word(number); } else { request->add_byte(isc_sdl_long_integer); request->add_long(number); } } //____________________________________________________________ // // Build an SDL loop for GET_SLICE/PUT_SLICE unless the upper and // lower bounds are constant. // static void cmp_sdl_subscript(gpre_req* request, USHORT index, const slc* slice, const ary* array) { const slc::slc_repeat* ranges = slice->slc_rpt + index; if (ranges->slc_upper == ranges->slc_lower) { cmp_sdl_value(request, ranges->slc_upper); return; } request->add_byte(isc_sdl_variable); request->add_byte(slice->slc_parameters + index); } //____________________________________________________________ // // Stuff a slice description language value. // static void cmp_sdl_value( gpre_req* request, const gpre_nod* node) { const ref* reference = (REF) node->nod_arg[0]; switch (node->nod_type) { case nod_literal: cmp_sdl_number(request, atoi(reference->ref_value)); return; case nod_value: request->add_byte(isc_sdl_variable); request->add_byte(reference->ref_id); return; default: CPR_error("cmp_sdl_value: node not understood"); } } //____________________________________________________________ // // generate blr for set generator // static void cmp_set_generator( gpre_req* request) { act* action = request->req_actions; request->add_byte(blr_set_generator); const sgen* setgen = (SGEN) action->act_object; const TEXT* string = setgen->sgen_name; const SLONG value = setgen->sgen_value; const SINT64 int64value = setgen->sgen_int64value; request->add_byte(strlen(string)); while (*string) request->add_byte(*string++); request->add_byte(blr_literal); if (setgen->sgen_dialect == SQL_DIALECT_V5) { request->add_byte(blr_long); request->add_byte(0); request->add_word(value); request->add_word(value >> 16); } else { request->add_byte(blr_int64); request->add_byte(0); request->add_word(int64value); request->add_word(int64value >> 16); request->add_word(int64value >> 32); request->add_word(int64value >> 48); } } //____________________________________________________________ // // static void cmp_slice( gpre_req* request) { slc* slice = request->req_slice; const gpre_fld* field = slice->slc_field; gpre_fld* element = field->fld_array; const ary* array = field->fld_array_info; // Process variable references for (ref* reference = request->req_values; reference; reference = reference->ref_next) { reference->ref_id = slice->slc_parameters++; } request->req_blr = request->req_base = (UCHAR *) MSC_alloc(500); request->req_length = 500; request->add_byte(isc_sdl_version1); request->add_byte(isc_sdl_struct); request->add_byte(1); cmp_field(request, element, 0); request->add_byte(isc_sdl_relation); CMP_stuff_symbol(request, field->fld_relation->rel_symbol); request->add_byte(isc_sdl_field); CMP_stuff_symbol(request, field->fld_symbol); bool loop_flags[16]; { // scope block USHORT n = 0; for (bool* p = loop_flags; n < slice->slc_dimensions; n++, p++) *p = cmp_sdl_loop(request, n, slice, array); } // end scope block request->add_byte(isc_sdl_element); request->add_byte(1); request->add_byte(isc_sdl_scalar); request->add_byte(0); request->add_byte(slice->slc_dimensions); { // scope block USHORT n = 0; for (const bool* p = loop_flags; n < slice->slc_dimensions; n++, p++) { if (!*p) cmp_sdl_fudge(request, array->ary_rpt[n].ary_lower); cmp_sdl_subscript(request, n, slice, array); } } // end scope block request->add_byte(isc_sdl_eoc); request->req_length = request->req_blr - request->req_base; request->req_blr = request->req_base; } //____________________________________________________________ // // Generate blr for a store request. // static void cmp_store( gpre_req* request) { // Make the store statement under the receive if (request->req_type == REQ_store2) request->add_byte(blr_store2); else request->add_byte(blr_store); CME_relation(request->req_contexts, request); // Make an assignment list gpre_nod* list = request->req_node; request->add_byte(blr_begin); gpre_nod** ptr = list->nod_arg; for (const gpre_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++) { cmp_assignment(*ptr, request); } request->add_byte(blr_end); if (request->req_type == REQ_store2) { // whip through actions to find return list act* action; for (action = request->req_actions;; action = action->act_next) { if (action->act_type == ACT_store2) break; } request->add_byte(blr_begin); upd* update = (upd*) action->act_object; gpre_port* port = update->upd_port; make_send(port, request); list = update->upd_assignments; const SSHORT count = list->nod_count; if (count > 1) request->add_byte(blr_begin); gpre_nod** ptr2 = list->nod_arg; for (const gpre_nod* const* const end = ptr2 + count; ptr2 < end; ptr2++) cmp_assignment(*ptr2, request); if (count > 1) request->add_byte(blr_end); request->add_byte(blr_end); } } //____________________________________________________________ // // During the parsing of an SQL statement // we may have run into an indicator variable. // If so, all we've got now is its name, and // we really ought to build a full reference // block for it before we forget. // static void expand_references( REF reference) { for (; reference; reference = reference->ref_next) { if (reference->ref_null_value && !reference->ref_null) { ref* flag = (REF) MSC_alloc(REF_LEN); flag->ref_next = reference->ref_next; reference->ref_next = reference->ref_null = flag; flag->ref_master = reference; flag->ref_value = reference->ref_null_value; flag->ref_field = PAR_null_field(); } } } //____________________________________________________________ // // Make up a port block and process a linked list // of field references. // static gpre_port* make_port( gpre_req* request, REF reference) { gpre_port* port = (gpre_port*) MSC_alloc(POR_LEN); port->por_ident = CMP_next_ident(); port->por_msg_number = request->req_count++; port->por_next = request->req_ports; request->req_ports = port; // Hmmm -- no references. Not going to fly. // Make up a dummy reference. if (!reference) { reference = (REF) MSC_alloc(REF_LEN); reference->ref_field = eof_field; } ref* misc = 0; ref* alignments[3]; alignments[0] = alignments[1] = alignments[2] = NULL; for (ref* temp = reference; temp; temp = reference) { reference = reference->ref_next; gpre_fld* field = temp->ref_field; if (!field) CPR_bugcheck("missing prototype field for value"); if (temp->ref_value && (temp->ref_flags & REF_array_elem)) field = field->fld_array; if ((field->fld_length & 7) == 0) { temp->ref_next = alignments[2]; alignments[2] = temp; } else if ((field->fld_length & 3) == 0) { temp->ref_next = alignments[1]; alignments[1] = temp; } else if ((field->fld_length & 1) == 0) { temp->ref_next = alignments[0]; alignments[0] = temp; } else { temp->ref_next = misc; misc = temp; } } for (int i = 0; i <= 2; i++) while (reference = alignments[i]) { alignments[i] = reference->ref_next; reference->ref_next = misc; misc = reference; } port->por_references = misc; for (reference = misc; reference; reference = reference->ref_next) { gpre_fld* field = reference->ref_field; if (reference->ref_value && (reference->ref_flags & REF_array_elem)) field = field->fld_array; reference->ref_ident = CMP_next_ident(); reference->ref_parameter = port->por_count++; reference->ref_port = port; reference->ref_offset = port->por_length; port->por_length += field->fld_length; } return port; } //____________________________________________________________ // // Make a receive node for a given port. // static void make_receive( gpre_port* port, gpre_req* request) { request->add_byte(blr_receive); request->add_byte(port->por_msg_number); } //____________________________________________________________ // // Make a receive node for a given port. // static void make_send( gpre_port* port, gpre_req* request) { request->add_byte(blr_send); request->add_byte(port->por_msg_number); } //____________________________________________________________ // // where a duplicate reference list is used // to build the port, fix request_references // static void update_references( REF references) { for (ref* re = references; re; re = re->ref_next) { ref* source = re->ref_source; source->ref_port = re->ref_port; source->ref_parameter = re->ref_parameter; source->ref_ident = re->ref_ident; } }