/* * PROGRAM: Dynamic SQL runtime support * MODULE: gen.cpp * DESCRIPTION: Routines to generate BLR. * * 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): ______________________________________ * 2001.6.21 Claudio Valderrama: BREAK and SUBSTRING. * 2001.07.28 John Bellardo: Added code to generate blr_skip. * 2002.07.30 Arno Brinkman: Added code, procedures to generate COALESCE, CASE * 2002.09.28 Dmitry Yemanov: Reworked internal_info stuff, enhanced * exception handling in SPs/triggers, * implemented ROWS_AFFECTED system variable * 2002.10.21 Nickolay Samofatov: Added support for explicit pessimistic locks * 2002.10.29 Nickolay Samofatov: Added support for savepoints * 2003.10.05 Dmitry Yemanov: Added support for explicit cursors in PSQL * 2004.01.16 Vlad Horsun: Added support for default parameters and * EXECUTE BLOCK statement * Adriano dos Santos Fernandes */ #include "firebird.h" #include #include #include "../dsql/dsql.h" #include "../dsql/node.h" #include "../jrd/ibase.h" #include "../jrd/align.h" #include "../jrd/constants.h" #include "../jrd/intl.h" #include "../jrd/jrd.h" #include "../jrd/val.h" #include "../dsql/ddl_proto.h" #include "../dsql/errd_proto.h" #include "../dsql/gen_proto.h" #include "../dsql/make_proto.h" #include "../dsql/metd_proto.h" #include "../dsql/misc_func.h" #include "../dsql/utld_proto.h" #include "../jrd/thread_proto.h" #include "../jrd/dsc_proto.h" #include "../jrd/why_proto.h" #include "gen/iberror.h" using namespace Jrd; using namespace Dsql; static void gen_aggregate(dsql_req*, const dsql_nod*); static void gen_cast(dsql_req*, const dsql_nod*); static void gen_coalesce(dsql_req*, const dsql_nod*); static void gen_constant(dsql_req*, const dsc*, bool); static void gen_constant(dsql_req*, dsql_nod*, bool); static void gen_descriptor(dsql_req*, const dsc*, bool); static void gen_error_condition(dsql_req*, const dsql_nod*); static void gen_field(dsql_req*, const dsql_ctx*, const dsql_fld*, dsql_nod*); static void gen_for_select(dsql_req*, const dsql_nod*); static void gen_gen_id(dsql_req*, const dsql_nod*); static void gen_join_rse(dsql_req*, const dsql_nod*); static void gen_map(dsql_req*, dsql_map*); static void gen_parameter(dsql_req*, const dsql_par*); static void gen_plan(dsql_req*, const dsql_nod*); static void gen_relation(dsql_req*, dsql_ctx*); static void gen_rse(dsql_req*, const dsql_nod*); static void gen_searched_case(dsql_req*, const dsql_nod*); static void gen_select(dsql_req*, dsql_nod*); static void gen_simple_case(dsql_req*, const dsql_nod*); static void gen_sort(dsql_req*, dsql_nod*); static void gen_statement(dsql_req*, const dsql_nod*); static void gen_sys_function(dsql_req*, const dsql_nod*); static void gen_table_lock(dsql_req*, const dsql_nod*, USHORT); static void gen_udf(dsql_req*, const dsql_nod*); static void gen_union(dsql_req*, const dsql_nod*); static void stuff_context(dsql_req*, const dsql_ctx*); static void stuff_cstring(dsql_req*, const char*); static void stuff_meta_string(dsql_req*, const char*); static void stuff_string(dsql_req*, const char*, int); static void stuff_string(dsql_req* request, const Firebird::MetaName& name); static void stuff_word(dsql_req*, USHORT); // STUFF is defined in dsql.h for use in common with ddl.c // The following are passed as the third argument to gen_constant const bool NEGATE_VALUE = true; const bool USE_VALUE = false; /** GEN_expr @brief Generate blr for an arbitrary expression. @param request @param node **/ void GEN_expr( dsql_req* request, dsql_nod* node) { UCHAR blr_operator; dsql_ctx* context; dsql_map* map; dsql_var* variable; switch (node->nod_type) { case nod_alias: GEN_expr(request, node->nod_arg[e_alias_value]); return; case nod_aggregate: gen_aggregate(request, node); return; case nod_constant: gen_constant(request, node, USE_VALUE); return; case nod_derived_field: GEN_expr(request, node->nod_arg[e_derived_field_value]); return; case nod_extract: stuff(request, blr_extract); stuff(request, node->nod_arg[e_extract_part]->getSlong()); GEN_expr(request, node->nod_arg[e_extract_value]); return; case nod_strlen: stuff(request, blr_strlen); stuff(request, node->nod_arg[e_strlen_type]->getSlong()); GEN_expr(request, node->nod_arg[e_strlen_value]); return; case nod_dbkey: node = node->nod_arg[0]; context = (dsql_ctx*) node->nod_arg[e_rel_context]; stuff(request, blr_dbkey); stuff_context(request, context); return; case nod_rec_version: node = node->nod_arg[0]; context = (dsql_ctx*) node->nod_arg[e_rel_context]; stuff(request, blr_record_version); stuff_context(request, context); return; case nod_dom_value: stuff(request, blr_fid); stuff(request, 0); // Context stuff_word(request, 0); // Field id return; case nod_field: gen_field(request, (dsql_ctx*) node->nod_arg[e_fld_context], (dsql_fld*) node->nod_arg[e_fld_field], node->nod_arg[e_fld_indices]); return; case nod_user_name: stuff(request, blr_user_name); return; case nod_current_time: if (node->nod_arg[0]) { const dsql_nod* const_node = node->nod_arg[0]; fb_assert(const_node->nod_type == nod_constant); const int precision = (int) const_node->getSlong(); stuff(request, blr_current_time2); stuff(request, precision); } else { stuff(request, blr_current_time); } return; case nod_current_timestamp: if (node->nod_arg[0]) { const dsql_nod* const_node = node->nod_arg[0]; fb_assert(const_node->nod_type == nod_constant); const int precision = (int) const_node->getSlong(); stuff(request, blr_current_timestamp2); stuff(request, precision); } else { stuff(request, blr_current_timestamp); } return; case nod_current_date: stuff(request, blr_current_date); return; case nod_current_role: stuff(request, blr_current_role); return; case nod_udf: gen_udf(request, node); return; case nod_sys_function: gen_sys_function(request, node); return; case nod_variable: variable = (dsql_var*) node->nod_arg[e_var_variable]; if (variable->var_type == VAR_input) { stuff(request, blr_parameter2); stuff(request, variable->var_msg_number); stuff_word(request, variable->var_msg_item); stuff_word(request, variable->var_msg_item + 1); } else { stuff(request, blr_variable); stuff_word(request, variable->var_variable_number); } return; case nod_join: gen_join_rse(request, node); return; case nod_map: map = (dsql_map*) node->nod_arg[e_map_map]; context = (dsql_ctx*) node->nod_arg[e_map_context]; stuff(request, blr_fid); stuff_context(request, context); stuff_word(request, map->map_position); return; case nod_parameter: gen_parameter(request, (dsql_par*) node->nod_arg[e_par_parameter]); return; case nod_relation: gen_relation(request, (dsql_ctx*) node->nod_arg[e_rel_context]); return; case nod_rse: gen_rse(request, node); return; case nod_derived_table: gen_rse(request, node->nod_arg[e_derived_table_rse]); return; case nod_exists: stuff(request, blr_any); gen_rse(request, node->nod_arg[0]); return; case nod_singular: stuff(request, blr_unique); gen_rse(request, node->nod_arg[0]); return; case nod_agg_count: if (node->nod_count) blr_operator = (node->nod_flags & NOD_AGG_DISTINCT) ? blr_agg_count_distinct : blr_agg_count2; else blr_operator = blr_agg_count; break; case nod_agg_min: blr_operator = blr_agg_min; break; case nod_agg_max: blr_operator = blr_agg_max; break; case nod_agg_average: blr_operator = (node->nod_flags & NOD_AGG_DISTINCT) ? blr_agg_average_distinct : blr_agg_average; break; case nod_agg_total: blr_operator = (node->nod_flags & NOD_AGG_DISTINCT) ? blr_agg_total_distinct : blr_agg_total; break; case nod_agg_average2: blr_operator = (node->nod_flags & NOD_AGG_DISTINCT) ? blr_agg_average_distinct : blr_agg_average; break; case nod_agg_total2: blr_operator = (node->nod_flags & NOD_AGG_DISTINCT) ? blr_agg_total_distinct : blr_agg_total; break; case nod_agg_list: blr_operator = (node->nod_flags & NOD_AGG_DISTINCT) ? blr_agg_list_distinct : blr_agg_list; break; case nod_and: blr_operator = blr_and; break; case nod_or: blr_operator = blr_or; break; case nod_not: blr_operator = blr_not; break; case nod_eql_all: case nod_eql_any: case nod_eql: blr_operator = blr_eql; break; case nod_equiv: blr_operator = blr_equiv; break; case nod_neq_all: case nod_neq_any: case nod_neq: blr_operator = blr_neq; break; case nod_gtr_all: case nod_gtr_any: case nod_gtr: blr_operator = blr_gtr; break; case nod_leq_all: case nod_leq_any: case nod_leq: blr_operator = blr_leq; break; case nod_geq_all: case nod_geq_any: case nod_geq: blr_operator = blr_geq; break; case nod_lss_all: case nod_lss_any: case nod_lss: blr_operator = blr_lss; break; case nod_between: blr_operator = blr_between; break; case nod_containing: blr_operator = blr_containing; break; case nod_similar: stuff(request, blr_similar); GEN_expr(request, node->nod_arg[e_similar_value]); GEN_expr(request, node->nod_arg[e_similar_pattern]); if (node->nod_arg[e_similar_escape]) { stuff(request, 1); GEN_expr(request, node->nod_arg[e_similar_escape]); } else stuff(request, 0); return; case nod_starting: blr_operator = blr_starting; break; case nod_missing: blr_operator = blr_missing; break; case nod_like: blr_operator = (node->nod_count == 2) ? blr_like : blr_ansi_like; break; case nod_add: blr_operator = blr_add; break; case nod_subtract: blr_operator = blr_subtract; break; case nod_multiply: blr_operator = blr_multiply; break; case nod_negate: { dsql_nod* child = node->nod_arg[0]; if (child->nod_type == nod_constant && DTYPE_IS_NUMERIC(child->nod_desc.dsc_dtype)) { gen_constant(request, child, NEGATE_VALUE); return; } } blr_operator = blr_negate; break; case nod_divide: blr_operator = blr_divide; break; case nod_add2: blr_operator = blr_add; break; case nod_subtract2: blr_operator = blr_subtract; break; case nod_multiply2: blr_operator = blr_multiply; break; case nod_divide2: blr_operator = blr_divide; break; case nod_concatenate: blr_operator = blr_concatenate; break; case nod_null: blr_operator = blr_null; break; case nod_any: blr_operator = blr_any; break; case nod_ansi_any: blr_operator = blr_ansi_any; break; case nod_ansi_all: blr_operator = blr_ansi_all; break; case nod_via: blr_operator = blr_via; break; case nod_internal_info: blr_operator = blr_internal_info; break; case nod_upcase: blr_operator = blr_upcase; break; case nod_lowcase: blr_operator = blr_lowcase; break; case nod_substr: blr_operator = blr_substring; break; case nod_cast: gen_cast(request, node); return; case nod_gen_id: case nod_gen_id2: gen_gen_id(request, node); return; case nod_coalesce: gen_coalesce(request, node); return; case nod_simple_case: gen_simple_case(request, node); return; case nod_searched_case: gen_searched_case(request, node); return; case nod_average: //case nod_count: case nod_from: case nod_max: case nod_min: case nod_total: switch (node->nod_type) { case nod_average: blr_operator = blr_average; break; //case nod_count: // blr_operator = blr_count; // count2 // blr_operator = node->nod_arg[0]->nod_arg[e_rse_items] ? blr_count2 : blr_count; // break; case nod_from: blr_operator = blr_from; break; case nod_max: blr_operator = blr_maximum; break; case nod_min: blr_operator = blr_minimum; break; case nod_total: blr_operator = blr_total; break; default: break; } stuff(request, blr_operator); gen_rse(request, node->nod_arg[0]); if (blr_operator != blr_count) GEN_expr(request, node->nod_arg[0]->nod_arg[e_rse_items]); return; case nod_trim: stuff(request, blr_trim); stuff(request, node->nod_arg[e_trim_specification]->getSlong()); if (node->nod_arg[e_trim_characters]) { stuff(request, blr_trim_characters); GEN_expr(request, node->nod_arg[e_trim_characters]); } else stuff(request, blr_trim_spaces); GEN_expr(request, node->nod_arg[e_trim_value]); return; default: ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 901, isc_arg_gds, isc_dsql_internal_err, isc_arg_gds, isc_expression_eval_err, // expression evaluation not supported 0); } stuff(request, blr_operator); dsql_nod* const* ptr = node->nod_arg; for (const dsql_nod* const* const end = ptr + node->nod_count; ptr < end; ptr++) { GEN_expr(request, *ptr); } /* Check whether the node we just processed is for a dialect 3 operation which gives a different result than the corresponding operation in dialect 1. If it is, and if the client dialect is 2, issue a warning about the difference. */ if (node->nod_type == nod_add2 || node->nod_type == nod_subtract2 || node->nod_type == nod_multiply2 || node->nod_type == nod_divide2 || node->nod_type == nod_agg_total2 || node->nod_type == nod_agg_average2) { dsc desc; const char* s = 0; char message_buf[8]; MAKE_desc(request, &desc, node, NULL); if ((node->nod_flags & NOD_COMP_DIALECT) && (request->req_client_dialect == SQL_DIALECT_V6_TRANSITION)) { switch (node->nod_type) { case nod_add2: s = "add"; break; case nod_subtract2: s = "subtract"; break; case nod_multiply2: s = "multiply"; break; case nod_divide2: s = "divide"; break; case nod_agg_total2: s = "sum"; break; case nod_agg_average2: s = "avg"; break; default: sprintf(message_buf, "blr %d", (int) blr_operator); s = message_buf; } ERRD_post_warning(isc_dsql_dialect_warning_expr, isc_arg_string, s, isc_arg_end); } } } /** GEN_port @brief Generate a port from a message. Feel free to rearrange the order of parameters. @param request @param message **/ void GEN_port(dsql_req* request, dsql_msg* message) { thread_db* tdbb = JRD_get_thread_data(); Attachment* att = tdbb->getAttachment(); // if (request->req_blr_string) { stuff(request, blr_message); stuff(request, message->msg_number); stuff_word(request, message->msg_parameter); // } dsql_par* parameter; ULONG offset = 0; USHORT number = 0; for (parameter = message->msg_parameters; parameter; parameter = parameter->par_next) { parameter->par_parameter = number++; const USHORT fromCharSet = parameter->par_desc.getCharSet(); const USHORT toCharSet = (fromCharSet == CS_NONE || fromCharSet == CS_BINARY) ? fromCharSet : att->att_charset; if (parameter->par_desc.dsc_dtype <= dtype_any_text && att->att_charset != CS_NONE && att->att_charset != CS_BINARY) { USHORT adjust = 0; if (parameter->par_desc.dsc_dtype == dtype_varying) adjust = sizeof(USHORT); else if (parameter->par_desc.dsc_dtype == dtype_cstring) adjust = 1; parameter->par_desc.dsc_length -= adjust; USHORT fromCharSetBPC = METD_get_charset_bpc(request, fromCharSet); USHORT toCharSetBPC = METD_get_charset_bpc(request, toCharSet); INTL_ASSIGN_TTYPE(¶meter->par_desc, INTL_CS_COLL_TO_TTYPE(toCharSet, (fromCharSet == toCharSet ? INTL_GET_COLLATE(¶meter->par_desc) : 0))); parameter->par_desc.dsc_length = UTLD_char_length_to_byte_length(parameter->par_desc.dsc_length / fromCharSetBPC, toCharSetBPC); parameter->par_desc.dsc_length += adjust; } else if (ENCODE_ODS(request->req_dbb->dbb_ods_version, request->req_dbb->dbb_minor_version) >= ODS_11_1 && parameter->par_desc.dsc_dtype == dtype_blob && parameter->par_desc.dsc_sub_type == isc_blob_text && att->att_charset != CS_NONE && att->att_charset != CS_BINARY) { if (fromCharSet != toCharSet) parameter->par_desc.setTextType(toCharSet); } /* For older clients - generate an error should they try and access data types which did not exist in the older dialect */ if (request->req_client_dialect <= SQL_DIALECT_V5) switch (parameter->par_desc.dsc_dtype) { /* In V6.0 - older clients, which we distinguish by their use of SQL DIALECT 0 or 1, are forbidden from selecting values of new datatypes */ case dtype_sql_date: case dtype_sql_time: case dtype_int64: ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 804, isc_arg_gds, isc_dsql_datatype_err, isc_arg_gds, isc_sql_dialect_datatype_unsupport, isc_arg_number, (SLONG) request->req_client_dialect, isc_arg_string, DSC_dtype_tostring(parameter->par_desc.dsc_dtype), 0); break; default: // No special action for other data types break; } const USHORT align = type_alignments[parameter->par_desc.dsc_dtype]; if (align) offset = FB_ALIGN(offset, align); parameter->par_desc.dsc_address = (UCHAR*)(IPTR) offset; offset += parameter->par_desc.dsc_length; // if (request->req_blr_string) gen_descriptor(request, ¶meter->par_desc, false); } if (offset > MAX_FORMAT_SIZE) { ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) -204, isc_arg_gds, isc_imp_exc, isc_arg_gds, isc_blktoobig, 0); } message->msg_length = (USHORT) offset; // Allocate buffer for message const ULONG new_len = message->msg_length + DOUBLE_ALIGN - 1; dsql_str* buffer = FB_NEW_RPT(*tdbb->getDefaultPool(), new_len) dsql_str; message->msg_buffer = (UCHAR *) FB_ALIGN((U_IPTR) buffer->str_data, DOUBLE_ALIGN); // Relocate parameter descriptors to point direction into message buffer for (parameter = message->msg_parameters; parameter; parameter = parameter->par_next) { parameter->par_desc.dsc_address = message->msg_buffer + (IPTR)parameter->par_desc.dsc_address; } } /** GEN_request @brief Generate complete blr for a request. @param request @param node **/ void GEN_request( dsql_req* request, dsql_nod* node) { if (request->req_type == REQ_CREATE_DB || request->req_type == REQ_DDL) { DDL_generate(request, node); return; } if (request->req_flags & REQ_blr_version4) stuff(request, blr_version4); else stuff(request, blr_version5); if (request->req_type == REQ_SAVEPOINT) { // Do not generate BEGIN..END block around savepoint statement // to avoid breaking of savepoint logic request->req_send = NULL; request->req_receive = NULL; GEN_statement(request, node); } else { stuff(request, blr_begin); switch (request->req_type) { case REQ_SELECT: case REQ_SELECT_UPD: case REQ_EMBED_SELECT: gen_select(request, node); break; case REQ_EXEC_BLOCK: case REQ_SELECT_BLOCK: GEN_statement(request, node); break; default: { dsql_msg* message = request->req_send; if (!message->msg_parameter) request->req_send = NULL; else { GEN_port(request, message); stuff(request, blr_receive); stuff(request, message->msg_number); } message = request->req_receive; if (!message->msg_parameter) request->req_receive = NULL; else GEN_port(request, message); GEN_statement(request, node); } } stuff(request, blr_end); } stuff(request, blr_eoc); } /** GEN_start_transaction @brief Generate tpb for set transaction. Use blr string of request. If a value is not specified, default is not STUFF'ed, let the engine handle it. Do not allow an option to be specified more than once. @param request @param tran_node **/ void GEN_start_transaction( dsql_req* request, const dsql_nod* tran_node) { SSHORT count = tran_node->nod_count; if (!count) return; const dsql_nod* node = tran_node->nod_arg[0]; if (!node) return; // Find out isolation level - if specified. This is required for // specifying the correct lock level in reserving clause. USHORT lock_level = isc_tpb_shared; if (count = node->nod_count) { while (count--) { const dsql_nod* ptr = node->nod_arg[count]; if ((!ptr) || (ptr->nod_type != nod_isolation)) continue; lock_level = (ptr->nod_flags & NOD_CONSISTENCY) ? isc_tpb_protected : isc_tpb_shared; } } bool sw_access = false, sw_wait = false, sw_isolation = false, sw_reserve = false, sw_lock_timeout = false; int misc_flags = 0; // Stuff some version info. if (count = node->nod_count) stuff(request, isc_tpb_version1); while (count--) { const dsql_nod* ptr = node->nod_arg[count]; if (!ptr) continue; switch (ptr->nod_type) { case nod_access: if (sw_access) ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104, isc_arg_gds, isc_dsql_dup_option, 0); sw_access = true; if (ptr->nod_flags & NOD_READ_ONLY) stuff(request, isc_tpb_read); else stuff(request, isc_tpb_write); break; case nod_wait: if (sw_wait) ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104, isc_arg_gds, isc_dsql_dup_option, 0); sw_wait = true; if (ptr->nod_flags & NOD_NO_WAIT) stuff(request, isc_tpb_nowait); else stuff(request, isc_tpb_wait); break; case nod_isolation: if (sw_isolation) ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104, isc_arg_gds, isc_dsql_dup_option, 0); sw_isolation = true; if (ptr->nod_flags & NOD_CONCURRENCY) stuff(request, isc_tpb_concurrency); else if (ptr->nod_flags & NOD_CONSISTENCY) stuff(request, isc_tpb_consistency); else { stuff(request, isc_tpb_read_committed); if ((ptr->nod_count) && (ptr->nod_arg[0]) && (ptr->nod_arg[0]->nod_type == nod_version)) { if (ptr->nod_arg[0]->nod_flags & NOD_VERSION) stuff(request, isc_tpb_rec_version); else stuff(request, isc_tpb_no_rec_version); } else stuff(request, isc_tpb_no_rec_version); } break; case nod_reserve: { if (sw_reserve) ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104, isc_arg_gds, isc_dsql_dup_option, 0); sw_reserve = true; const dsql_nod* reserve = ptr->nod_arg[0]; if (reserve) { const dsql_nod* const* temp = reserve->nod_arg; for (const dsql_nod* const* end = temp + reserve->nod_count; temp < end; temp++) { gen_table_lock(request, *temp, lock_level); } } break; } case nod_tra_misc: if (misc_flags & ptr->nod_flags) ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104, isc_arg_gds, isc_dsql_dup_option, 0); misc_flags |= ptr->nod_flags; if (ptr->nod_flags & NOD_NO_AUTO_UNDO) stuff(request, isc_tpb_no_auto_undo); else if (ptr->nod_flags & NOD_IGNORE_LIMBO) stuff(request, isc_tpb_ignore_limbo); else if (ptr->nod_flags & NOD_RESTART_REQUESTS) stuff(request, isc_tpb_restart_requests); break; case nod_lock_timeout: if (sw_lock_timeout) ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104, isc_arg_gds, isc_dsql_dup_option, 0); sw_lock_timeout = true; if (ptr->nod_count == 1 && ptr->nod_arg[0]->nod_type == nod_constant) { const int lck_timeout = (int) ptr->nod_arg[0]->getSlong(); stuff(request, isc_tpb_lock_timeout); stuff(request, 2); stuff_word(request, lck_timeout); } break; default: ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104, isc_arg_gds, isc_dsql_tran_err, 0); } } } /** GEN_statement @brief Generate blr for an arbitrary expression. @param request @param node **/ void GEN_statement( dsql_req* request, dsql_nod* node) { dsql_nod* temp; dsql_nod** ptr; const dsql_nod* const* end; dsql_str* string; ULONG id_length; switch (node->nod_type) { case nod_assign: stuff(request, blr_assignment); GEN_expr(request, node->nod_arg[0]); GEN_expr(request, node->nod_arg[1]); return; case nod_block: stuff(request, blr_block); GEN_statement(request, node->nod_arg[e_blk_action]); if (node->nod_count > 1) { temp = node->nod_arg[e_blk_errs]; for (ptr = temp->nod_arg, end = ptr + temp->nod_count; ptr < end; ptr++) { GEN_statement(request, *ptr); } } stuff(request, blr_end); return; case nod_exec_block: DDL_gen_block(request, node); return; case nod_auto_trans: stuff(request, blr_auto_trans); stuff(request, 0); // to extend syntax in the future GEN_statement(request, node->nod_arg[e_auto_trans_action]); return; case nod_for_select: gen_for_select(request, node); return; case nod_set_generator: case nod_set_generator2: stuff(request, blr_set_generator); string = (dsql_str*) node->nod_arg[e_gen_id_name]; stuff_cstring(request, string->str_data); GEN_expr(request, node->nod_arg[e_gen_id_value]); return; case nod_if: stuff(request, blr_if); GEN_expr(request, node->nod_arg[e_if_condition]); GEN_statement(request, node->nod_arg[e_if_true]); if (node->nod_arg[e_if_false]) GEN_statement(request, node->nod_arg[e_if_false]); else stuff(request, blr_end); return; case nod_list: stuff(request, blr_begin); for (ptr = node->nod_arg, end = ptr + node->nod_count; ptr < end; ptr++) { GEN_statement(request, *ptr); } stuff(request, blr_end); return; case nod_erase: case nod_erase_current: case nod_modify: case nod_modify_current: case nod_store: case nod_exec_procedure: gen_statement(request, node); return; case nod_on_error: stuff(request, blr_error_handler); temp = node->nod_arg[e_err_errs]; stuff_word(request, temp->nod_count); for (ptr = temp->nod_arg, end = ptr + temp->nod_count; ptr < end; ptr++) { gen_error_condition(request, *ptr); } GEN_statement(request, node->nod_arg[e_err_action]); return; case nod_post: if ( (temp = node->nod_arg[e_pst_argument]) ) { stuff(request, blr_post_arg); GEN_expr(request, node->nod_arg[e_pst_event]); GEN_expr(request, temp); } else { stuff(request, blr_post); GEN_expr(request, node->nod_arg[e_pst_event]); } return; case nod_exec_sql: stuff(request, blr_exec_sql); GEN_expr(request, node->nod_arg[e_exec_sql_stmnt]); return; case nod_exec_into: if (node->nod_arg[e_exec_into_block]) { stuff(request, blr_label); stuff(request, (int) (IPTR) node->nod_arg[e_exec_into_label]->nod_arg[e_label_number]); } stuff(request, blr_exec_into); temp = node->nod_arg[e_exec_into_list]; stuff_word(request, temp->nod_count); GEN_expr(request, node->nod_arg[e_exec_into_stmnt]); if (node->nod_arg[e_exec_into_block]) { stuff(request, 0); // Non-singleton GEN_statement(request, node->nod_arg[e_exec_into_block]); } else stuff(request, 1); // Singleton for (ptr = temp->nod_arg, end = ptr + temp->nod_count; ptr < end; ptr++) { GEN_expr(request, *ptr); } return; case nod_exec_stmt: { // scope const bool old_syntax = (node->nod_arg[e_exec_stmt_inputs] == NULL) && (node->nod_arg[e_exec_stmt_data_src]->nod_type == nod_null) && (node->nod_arg[e_exec_stmt_user]->nod_type == nod_null) && (node->nod_arg[e_exec_stmt_pwd]->nod_type == nod_null) && (node->nod_arg[e_exec_stmt_tran]->nod_flags == NOD_TRAN_COMMON); const bool old_exec_into = old_syntax && node->nod_arg[e_exec_stmt_outputs]; if (node->nod_arg[e_exec_stmt_proc_block]) { stuff(request, blr_label); stuff(request, (int)(IPTR) node->nod_arg[e_exec_stmt_label]->nod_arg[e_label_number]); } if (old_syntax) { if (old_exec_into) { stuff(request, blr_exec_into); } else { stuff(request, blr_exec_sql); } } else { stuff(request, blr_exec_stmt); } // counts of input and output parameters if (!old_syntax) { temp = node->nod_arg[e_exec_stmt_inputs]; stuff_word(request, temp ? temp->nod_count : 0); } if (!old_syntax || old_exec_into) { temp = node->nod_arg[e_exec_stmt_outputs]; stuff_word(request, temp ? temp->nod_count : 0); } // query expression GEN_expr(request, node->nod_arg[e_exec_stmt_sql]); if (!old_syntax) { // external data source, user and password GEN_expr(request, node->nod_arg[e_exec_stmt_data_src]); GEN_expr(request, node->nod_arg[e_exec_stmt_user]); GEN_expr(request, node->nod_arg[e_exec_stmt_pwd]); // statement's transaction behavior stuff(request, (UCHAR) node->nod_arg[e_exec_stmt_tran]->nod_flags); stuff(request, 0); // transaction parameters equal to current transaction } // singleton flag and proc block body if (node->nod_arg[e_exec_stmt_proc_block]) { stuff(request, 0); // non-singleton GEN_statement(request, node->nod_arg[e_exec_stmt_proc_block]); } else if (!old_syntax || old_exec_into) { stuff(request, 1); // singleton } // inputs temp = node->nod_arg[e_exec_stmt_inputs]; if (temp) { ptr = temp->nod_arg; const bool haveNames = ((*ptr)->nod_arg[e_named_param_name] != 0); if (haveNames) stuff(request, 1); else stuff(request, 0); for (end = ptr + temp->nod_count; ptr < end; ptr++) { if (haveNames) { dsql_str* name = (dsql_str*) (*ptr)->nod_arg[e_named_param_name]; stuff_cstring(request, name->str_data); } GEN_expr(request, (*ptr)->nod_arg[e_named_param_expr]); } } // outputs temp = node->nod_arg[e_exec_stmt_outputs]; if (temp) { for (ptr = temp->nod_arg, end = ptr + temp->nod_count; ptr < end; ptr++) { GEN_expr(request, *ptr); } } } // scope of case nod_exec_stmt return; case nod_return: if ( (temp = node->nod_arg[e_rtn_procedure]) ) { if (temp->nod_type == nod_exec_block) GEN_return(request, temp->nod_arg[e_exe_blk_outputs], false); else GEN_return(request, temp->nod_arg[e_prc_outputs], false); } return; case nod_exit: stuff(request, blr_leave); stuff(request, 0); return; case nod_breakleave: stuff(request, blr_leave); stuff(request, (int) (IPTR) node->nod_arg[e_breakleave_label]->nod_arg[e_label_number]); return; case nod_abort: stuff(request, blr_leave); stuff(request, (int) (IPTR) node->nod_arg[e_abrt_number]); return; case nod_start_savepoint: stuff(request, blr_start_savepoint); return; case nod_end_savepoint: stuff(request, blr_end_savepoint); return; case nod_user_savepoint: stuff(request, blr_user_savepoint); stuff(request, blr_savepoint_set); stuff_cstring(request, ((dsql_str*)node->nod_arg[e_sav_name])->str_data); return; case nod_release_savepoint: stuff(request, blr_user_savepoint); if (node->nod_arg[1]) { stuff(request, blr_savepoint_release_single); } else { stuff(request, blr_savepoint_release); } stuff_cstring(request, ((dsql_str*)node->nod_arg[e_sav_name])->str_data); return; case nod_undo_savepoint: stuff(request, blr_user_savepoint); stuff(request, blr_savepoint_undo); stuff_cstring(request, ((dsql_str*)node->nod_arg[e_sav_name])->str_data); return; case nod_exception_stmt: stuff(request, blr_abort); string = (dsql_str*) node->nod_arg[e_xcps_name]; temp = node->nod_arg[e_xcps_msg]; /* if exception name is undefined, it means we have re-initiate semantics here, so blr_raise verb should be generated */ if (!string) { stuff(request, blr_raise); return; } /* if exception value is defined, it means we have user-defined exception message here, so blr_exception_msg verb should be generated */ if (temp) { stuff(request, blr_exception_msg); } /* otherwise go usual way, i.e. generate blr_exception */ else { stuff(request, blr_exception); } if (!(string->str_flags & STR_delimited_id)) { id_length = string->str_length; for (TEXT* p = reinterpret_cast(string->str_data); *p; id_length--) { *p = UPPER(*p); *p++; } } stuff_cstring(request, string->str_data); /* if exception value is defined, generate appropriate BLR verbs */ if (temp) { GEN_expr(request, temp); } return; case nod_while: stuff(request, blr_label); stuff(request, (int) (IPTR) node->nod_arg[e_while_label]->nod_arg[e_label_number]); stuff(request, blr_loop); stuff(request, blr_begin); stuff(request, blr_if); GEN_expr(request, node->nod_arg[e_while_cond]); GEN_statement(request, node->nod_arg[e_while_action]); stuff(request, blr_leave); stuff(request, (int) (IPTR) node->nod_arg[e_while_label]->nod_arg[e_label_number]); stuff(request, blr_end); return; case nod_sqlcode: case nod_gdscode: stuff(request, blr_abort); gen_error_condition(request, node); return; case nod_cursor: stuff(request, blr_dcl_cursor); stuff_word(request, (int) (IPTR) node->nod_arg[e_cur_number]); GEN_expr(request, node->nod_arg[e_cur_rse]); temp = node->nod_arg[e_cur_rse]->nod_arg[e_rse_items]; stuff_word(request, temp->nod_count); ptr = temp->nod_arg; end = ptr + temp->nod_count; while (ptr < end) { GEN_expr(request, *ptr++); } return; case nod_cursor_open: case nod_cursor_close: case nod_cursor_fetch: { // op-code stuff(request, blr_cursor_stmt); if (node->nod_type == nod_cursor_open) stuff(request, blr_cursor_open); else if (node->nod_type == nod_cursor_close) stuff(request, blr_cursor_close); else stuff(request, blr_cursor_fetch); // cursor reference dsql_nod* cursor = node->nod_arg[e_cur_stmt_id]; stuff_word(request, (int) (IPTR) cursor->nod_arg[e_cur_number]); // preliminary navigation dsql_nod* seek = node->nod_arg[e_cur_stmt_seek]; if (seek) { stuff(request, blr_seek); GEN_expr(request, seek->nod_arg[0]); GEN_expr(request, seek->nod_arg[1]); } // assignment dsql_nod* list_into = node->nod_arg[e_cur_stmt_into]; if (list_into) { dsql_nod* list = cursor->nod_arg[e_cur_rse]->nod_arg[e_rse_items]; if (list->nod_count != list_into->nod_count) ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 313, isc_arg_gds, isc_dsql_count_mismatch, 0); stuff(request, blr_begin); ptr = list->nod_arg; end = ptr + list->nod_count; dsql_nod** ptr_to = list_into->nod_arg; while (ptr < end) { stuff(request, blr_assignment); GEN_expr(request, *ptr++); GEN_expr(request, *ptr_to++); } stuff(request, blr_end); } } return; case nod_src_info: request->put_debug_src_info(node->nod_line, node->nod_column); GEN_statement(request, node->nod_arg[e_src_info_stmt]); return; default: ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 901, isc_arg_gds, isc_dsql_internal_err, isc_arg_gds, isc_node_err, // gen.c: node not supported 0); } } /** gen_aggregate @brief Generate blr for a relation reference. @param @param **/ static void gen_aggregate( dsql_req* request, const dsql_nod* node) { const dsql_ctx* context = (dsql_ctx*) node->nod_arg[e_agg_context]; stuff(request, blr_aggregate); stuff_context(request, context); gen_rse(request, node->nod_arg[e_agg_rse]); // Handle GROUP BY clause stuff(request, blr_group_by); dsql_nod* list = node->nod_arg[e_agg_group]; if (list != NULL) { stuff(request, list->nod_count); dsql_nod** ptr = list->nod_arg; for (const dsql_nod* const* end = ptr + list->nod_count; ptr < end; ptr++) { GEN_expr(request, *ptr); } } else stuff(request, 0); // Generate value map gen_map(request, context->ctx_map); } /** gen_cast @brief Generate BLR for a data-type cast operation @param request @param node **/ static void gen_cast( dsql_req* request, const dsql_nod* node) { stuff(request, blr_cast); const dsql_fld* field = (dsql_fld*) node->nod_arg[e_cast_target]; DDL_put_field_dtype(request, field, true); GEN_expr(request, node->nod_arg[e_cast_source]); } /** gen_coalesce @brief Generate BLR for coalesce function Generate the blr values, begin with a cast and then : blr_value_if blr_missing blr for expression 1 blr_value_if blr_missing blr for expression n-1 expression n blr for expression n-1 @param request @param node **/ static void gen_coalesce( dsql_req* request, const dsql_nod* node) { // blr_value_if is used for building the coalesce function dsql_nod* list = node->nod_arg[0]; stuff(request, blr_cast); gen_descriptor(request, &node->nod_desc, true); dsql_nod* const* ptr = list->nod_arg; for (const dsql_nod* const* const end = ptr + (list->nod_count - 1); ptr < end; ptr++) { // IF (expression IS NULL) THEN stuff(request, blr_value_if); stuff(request, blr_missing); GEN_expr(request, *ptr); } // Return values list = node->nod_arg[1]; const dsql_nod* const* const begin = list->nod_arg; ptr = list->nod_arg + list->nod_count; // if all expressions are NULL return NULL for (ptr--; ptr >= begin; ptr--) { GEN_expr(request, *ptr); } } /** gen_constant @brief Generate BLR for a constant. @param request @param desc @param negate_value **/ static void gen_constant( dsql_req* request, const dsc* desc, bool negate_value) { SLONG value; SINT64 i64value; DSC tmp_desc; stuff(request, blr_literal); USHORT l = 0; //= desc->dsc_length; const UCHAR* p = desc->dsc_address; switch (desc->dsc_dtype) { case dtype_short: gen_descriptor(request, desc, true); value = *(SSHORT *) p; if (negate_value) value = -value; stuff_word(request, value); break; case dtype_long: gen_descriptor(request, desc, true); value = *(SLONG *) p; if (negate_value) value = -value; //printf("gen.cpp = %p %d\n", *((void**)p), value); stuff_word(request, value); stuff_word(request, value >> 16); break; case dtype_sql_time: case dtype_sql_date: gen_descriptor(request, desc, true); value = *(SLONG *) p; stuff_word(request, value); stuff_word(request, value >> 16); break; case dtype_double: /* this is used for approximate/large numeric literal which is transmitted to the engine as a string. */ gen_descriptor(request, desc, true); // Length of string literal, cast because it could be > 127 bytes. l = (USHORT) (UCHAR) desc->dsc_scale; if (negate_value) { stuff_word(request, l + 1); stuff(request, '-'); } else { stuff_word(request, l); } if (l) do { stuff(request, *p++); } while (--l); break; case dtype_int64: i64value = *(SINT64 *) p; if (negate_value) i64value = -i64value; else if (i64value == MIN_SINT64) { /* UH OH! * yylex correctly recognized the digits as the most-negative * possible INT64 value, but unfortunately, there was no * preceding '-' (a fact which the lexer could not know). * The value is too big for a positive INT64 value, and it * didn't contain an exponent so it's not a valid DOUBLE * PRECISION literal either, so we have to bounce it. */ ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 104, isc_arg_gds, isc_arith_except, isc_arg_gds, isc_numeric_out_of_range, 0); } /* We and the lexer both agree that this is an SINT64 constant, * and if the value needed to be negated, it already has been. * If the value will fit into a 32-bit signed integer, generate * it that way, else as an INT64. */ if ((i64value >= (SINT64) MIN_SLONG) && (i64value <= (SINT64) MAX_SLONG)) { stuff(request, blr_long); stuff(request, desc->dsc_scale); stuff_word(request, i64value); stuff_word(request, i64value >> 16); break; } else { stuff(request, blr_int64); stuff(request, desc->dsc_scale); stuff_word(request, i64value); stuff_word(request, i64value >> 16); stuff_word(request, i64value >> 32); stuff_word(request, i64value >> 48); } break; case dtype_quad: case dtype_blob: case dtype_array: case dtype_timestamp: gen_descriptor(request, desc, true); value = *(SLONG *) p; stuff_word(request, value); stuff_word(request, value >> 16); value = *(SLONG *) (p + 4); stuff_word(request, value); stuff_word(request, value >> 16); break; case dtype_text: { USHORT length = desc->dsc_length; gen_descriptor(request, desc, true); if (length) do { stuff(request, *p++); } while (--length); break; } default: // gen_constant: datatype not understood ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 103, isc_arg_gds, isc_dsql_constant_err, 0); } } /** gen_constant @brief Generate BLR for a constant. @param request @param node @param negate_value **/ static void gen_constant( dsql_req* request, dsql_nod* node, bool negate_value) { if (node->nod_desc.dsc_dtype == dtype_text) node->nod_desc.dsc_length = ((dsql_str*) node->nod_arg[0])->str_length; gen_constant(request, &node->nod_desc, negate_value); } /** gen_descriptor @brief Generate a blr descriptor from an internal descriptor. @param request @param desc @param texttype **/ static void gen_descriptor( dsql_req* request, const dsc* desc, bool texttype) { switch (desc->dsc_dtype) { case dtype_text: if (texttype || desc->dsc_ttype() == ttype_binary || desc->dsc_ttype() == ttype_none) { stuff(request, blr_text2); stuff_word(request, desc->dsc_ttype()); } else { stuff(request, blr_text2); // automatic transliteration stuff_word(request, ttype_dynamic); } stuff_word(request, desc->dsc_length); break; case dtype_varying: if (texttype || desc->dsc_ttype() == ttype_binary || desc->dsc_ttype() == ttype_none) { stuff(request, blr_varying2); stuff_word(request, desc->dsc_ttype()); } else { stuff(request, blr_varying2); // automatic transliteration stuff_word(request, ttype_dynamic); } stuff_word(request, desc->dsc_length - sizeof(USHORT)); break; case dtype_short: stuff(request, blr_short); stuff(request, desc->dsc_scale); break; case dtype_long: stuff(request, blr_long); stuff(request, desc->dsc_scale); break; case dtype_quad: stuff(request, blr_quad); stuff(request, desc->dsc_scale); break; case dtype_int64: stuff(request, blr_int64); stuff(request, desc->dsc_scale); break; case dtype_real: stuff(request, blr_float); break; case dtype_double: stuff(request, blr_double); break; case dtype_sql_date: stuff(request, blr_sql_date); break; case dtype_sql_time: stuff(request, blr_sql_time); break; case dtype_timestamp: stuff(request, blr_timestamp); break; case dtype_array: stuff(request, blr_quad); stuff(request, 0); break; case dtype_blob: stuff(request, blr_blob2); stuff_word(request, desc->dsc_sub_type); stuff_word(request, desc->getTextType()); break; default: // don't understand dtype ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 804, isc_arg_gds, isc_dsql_datatype_err, 0); } } /** gen_error_condition @brief Generate blr for an error condtion @param request @param node **/ static void gen_error_condition( dsql_req* request, const dsql_nod* node) { const dsql_str* string; switch (node->nod_type) { case nod_sqlcode: stuff(request, blr_sql_code); stuff_word(request, (USHORT)(IPTR) node->nod_arg[0]); return; case nod_gdscode: stuff(request, blr_gds_code); string = (dsql_str*) node->nod_arg[0]; stuff_cstring(request, string->str_data); return; case nod_exception: stuff(request, blr_exception); string = (dsql_str*) node->nod_arg[0]; stuff_cstring(request, string->str_data); return; case nod_default: stuff(request, blr_default_code); return; default: fb_assert(false); return; } } /** gen_field @brief Generate blr for a field - field id's are preferred but not for trigger or view blr. @param request @param context @param field @param indices **/ static void gen_field( dsql_req* request, const dsql_ctx* context, const dsql_fld* field, dsql_nod* indices) { /* For older clients - generate an error should they try and * access data types which did not exist in the older dialect */ if (request->req_client_dialect <= SQL_DIALECT_V5) { switch (field->fld_dtype) { case dtype_sql_date: case dtype_sql_time: case dtype_int64: ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 804, isc_arg_gds, isc_dsql_datatype_err, isc_arg_gds, isc_sql_dialect_datatype_unsupport, isc_arg_number, (SLONG) request->req_client_dialect, isc_arg_string, DSC_dtype_tostring(static_cast < UCHAR > (field->fld_dtype)), 0); break; default: // No special action for other data types break; } } if (indices) stuff(request, blr_index); if (DDL_ids(request)) { stuff(request, blr_fid); stuff_context(request, context); stuff_word(request, field->fld_id); } else { stuff(request, blr_field); stuff_context(request, context); stuff_string(request, field->fld_name); } if (indices) { stuff(request, indices->nod_count); dsql_nod** ptr = indices->nod_arg; for (const dsql_nod* const* end = ptr + indices->nod_count; ptr < end; ptr++) { GEN_expr(request, *ptr); } } } /** gen_for_select @brief Generate BLR for a SELECT statement. @param request @param for_select **/ static void gen_for_select( dsql_req* request, const dsql_nod* for_select) { dsql_nod* rse = for_select->nod_arg[e_flp_select]; // CVC: Only put a label if this is not singular; otherwise, // what loop is the user trying to abandon? if (for_select->nod_arg[e_flp_action]) { stuff(request, blr_label); stuff(request, (int) (IPTR) for_select->nod_arg[e_flp_label]->nod_arg[e_label_number]); } // Generate FOR loop stuff(request, blr_for); if (!for_select->nod_arg[e_flp_action]) { stuff(request, blr_singular); } gen_rse(request, rse); stuff(request, blr_begin); // Build body of FOR loop // Handle write locks dsql_nod* streams = rse->nod_arg[e_rse_streams]; dsql_ctx* context = NULL; if (!rse->nod_arg[e_rse_reduced] && streams->nod_count == 1) { dsql_nod* item = streams->nod_arg[0]; if (item && (item->nod_type == nod_relation)) context = (dsql_ctx*) item->nod_arg[e_rel_context]; } dsql_nod* list = rse->nod_arg[e_rse_items]; dsql_nod* list_to = for_select->nod_arg[e_flp_into]; if (list_to) { if (list->nod_count != list_to->nod_count) ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) - 313, isc_arg_gds, isc_dsql_count_mismatch, 0); dsql_nod** ptr = list->nod_arg; dsql_nod** ptr_to = list_to->nod_arg; for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++, ptr_to++) { stuff(request, blr_assignment); GEN_expr(request, *ptr); GEN_expr(request, *ptr_to); } } if (for_select->nod_arg[e_flp_action]) GEN_statement(request, for_select->nod_arg[e_flp_action]); stuff(request, blr_end); } /** gen_gen_id @brief Generate BLR for gen_id @param request @param node **/ static void gen_gen_id( dsql_req* request, const dsql_nod* node) { stuff(request, blr_gen_id); const dsql_str* string = (dsql_str*) node->nod_arg[e_gen_id_name]; stuff_cstring(request, string->str_data); GEN_expr(request, node->nod_arg[e_gen_id_value]); } /** gen_join_rse @brief Generate a record selection expression with an explicit join type. @param request @param rse **/ static void gen_join_rse( dsql_req* request, const dsql_nod* rse) { stuff(request, blr_rs_stream); stuff(request, 2); GEN_expr(request, rse->nod_arg[e_join_left_rel]); GEN_expr(request, rse->nod_arg[e_join_rght_rel]); const dsql_nod* node = rse->nod_arg[e_join_type]; if (node->nod_type != nod_join_inner) { stuff(request, blr_join_type); if (node->nod_type == nod_join_left) stuff(request, blr_left); else if (node->nod_type == nod_join_right) stuff(request, blr_right); else stuff(request, blr_full); } if (rse->nod_arg[e_join_boolean]) { stuff(request, blr_boolean); GEN_expr(request, rse->nod_arg[e_join_boolean]); } stuff(request, blr_end); } /** gen_map @brief Generate a value map for a record selection expression. @param request @param map **/ static void gen_map( dsql_req* request, dsql_map* map) { USHORT count = 0; dsql_map* temp; for (temp = map; temp; temp = temp->map_next) temp->map_position = count++; stuff(request, blr_map); stuff_word(request, count); for (temp = map; temp; temp = temp->map_next) { stuff_word(request, temp->map_position); GEN_expr(request, temp->map_node); } } /** gen_parameter @brief Generate a parameter reference. @param request @param parameter **/ static void gen_parameter( dsql_req* request, const dsql_par* parameter) { const dsql_msg* message = parameter->par_message; const dsql_par* null = parameter->par_null; if (null != NULL) { stuff(request, blr_parameter2); stuff(request, message->msg_number); stuff_word(request, parameter->par_parameter); stuff_word(request, null->par_parameter); return; } stuff(request, blr_parameter); stuff(request, message->msg_number); stuff_word(request, parameter->par_parameter); } /** gen_plan @brief Generate blr for an access plan expression. @param request @param plan_expression **/ static void gen_plan( dsql_req* request, const dsql_nod* plan_expression) { // stuff the join type const dsql_nod* list = plan_expression->nod_arg[1]; if (list->nod_count > 1) { if (plan_expression->nod_arg[0]) stuff(request, blr_merge); else stuff(request, blr_join); stuff(request, list->nod_count); } // stuff one or more plan items const dsql_nod* const* ptr = list->nod_arg; for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++) { const dsql_nod* node = *ptr; if (node->nod_type == nod_plan_expr) { gen_plan(request, node); continue; } // if we're here, it must be a nod_plan_item stuff(request, blr_retrieve); /* stuff the relation--the relation id itself is redundant except when there is a need to differentiate the base tables of views */ const dsql_nod* arg = node->nod_arg[0]; gen_relation(request, (dsql_ctx*) arg->nod_arg[e_rel_context]); // now stuff the access method for this stream const dsql_str* index_string; arg = node->nod_arg[1]; switch (arg->nod_type) { case nod_natural: stuff(request, blr_sequential); break; case nod_index_order: stuff(request, blr_navigational); index_string = (dsql_str*) arg->nod_arg[0]; stuff_cstring(request, index_string->str_data); if (!arg->nod_arg[1]) break; // dimitr: FALL INTO, if the plan item is ORDER ... INDEX (...) case nod_index: { stuff(request, blr_indices); arg = (arg->nod_type == nod_index) ? arg->nod_arg[0] : arg->nod_arg[1]; stuff(request, arg->nod_count); const dsql_nod* const* ptr2 = arg->nod_arg; for (const dsql_nod* const* const end2 = ptr2 + arg->nod_count; ptr2 < end2; ptr2++) { index_string = (dsql_str*) * ptr2; stuff_cstring(request, index_string->str_data); } break; } default: fb_assert(false); break; } } } /** gen_relation @brief Generate blr for a relation reference. @param request @param context **/ static void gen_relation( dsql_req* request, dsql_ctx* context) { const dsql_rel* relation = context->ctx_relation; const dsql_prc* procedure = context->ctx_procedure; // if this is a trigger or procedure, don't want relation id used if (relation) { if (DDL_ids(request)) { if (context->ctx_alias) stuff(request, blr_rid2); else stuff(request, blr_rid); stuff_word(request, relation->rel_id); } else { if (context->ctx_alias) stuff(request, blr_relation2); else stuff(request, blr_relation); stuff_meta_string(request, relation->rel_name.c_str()); } if (context->ctx_alias) stuff_meta_string(request, context->ctx_alias); stuff_context(request, context); } else if (procedure) { if (DDL_ids(request)) { stuff(request, blr_pid); stuff_word(request, procedure->prc_id); } else { stuff(request, blr_procedure); stuff_meta_string(request, procedure->prc_name.c_str()); } stuff_context(request, context); dsql_nod* inputs = context->ctx_proc_inputs; if (inputs) { stuff_word(request, inputs->nod_count); dsql_nod* const* ptr = inputs->nod_arg; for (const dsql_nod* const* const end = ptr + inputs->nod_count; ptr < end; ptr++) { GEN_expr(request, *ptr); } } else stuff_word(request, 0); } } /** gen_return @brief Generate blr for a procedure return. @param request @param procedure @param eos_flag **/ void GEN_return( dsql_req* request, const dsql_nod* parameters, bool eos_flag) { if (!eos_flag) stuff(request, blr_begin); stuff(request, blr_send); stuff(request, 1); stuff(request, blr_begin); USHORT outputs = 0; if (parameters) { const dsql_nod* const* ptr = parameters->nod_arg; for (const dsql_nod* const* const end = ptr + parameters->nod_count; ptr < end; ptr++) { outputs++; const dsql_nod* parameter = *ptr; const dsql_var* variable = (dsql_var*) parameter->nod_arg[e_var_variable]; stuff(request, blr_assignment); stuff(request, blr_variable); stuff_word(request, variable->var_variable_number); stuff(request, blr_parameter2); stuff(request, variable->var_msg_number); stuff_word(request, variable->var_msg_item); stuff_word(request, variable->var_msg_item + 1); } } stuff(request, blr_assignment); stuff(request, blr_literal); stuff(request, blr_short); stuff(request, 0); if (eos_flag) stuff_word(request, 0); else stuff_word(request, 1); stuff(request, blr_parameter); stuff(request, 1); stuff_word(request, 2 * outputs); stuff(request, blr_end); if (!eos_flag) { stuff(request, blr_stall); stuff(request, blr_end); } } /** gen_rse @brief Generate a record selection expression. @param request @param rse **/ static void gen_rse( dsql_req* request, const dsql_nod* rse) { if (rse->nod_flags & NOD_SELECT_EXPR_SINGLETON) { stuff(request, blr_singular); } stuff(request, blr_rse); dsql_nod* list = rse->nod_arg[e_rse_streams]; // Handle source streams if (list->nod_type == nod_union) { stuff(request, 1); gen_union(request, rse); } else if (list->nod_type == nod_list) { stuff(request, list->nod_count); dsql_nod* const* ptr = list->nod_arg; for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++) { dsql_nod* node = *ptr; if (node->nod_type == nod_relation || node->nod_type == nod_aggregate || node->nod_type == nod_join) { GEN_expr(request, node); } else if (node->nod_type == nod_derived_table) { GEN_expr(request, node->nod_arg[e_derived_table_rse]); } } } else { stuff(request, 1); GEN_expr(request, list); } if (rse->nod_arg[e_rse_lock]) stuff(request, blr_writelock); dsql_nod* node; if ((node = rse->nod_arg[e_rse_first]) != NULL) { stuff(request, blr_first); GEN_expr(request, node); } if ((node = rse->nod_arg[e_rse_skip]) != NULL) { stuff(request, blr_skip); GEN_expr (request, node); } if ((node = rse->nod_arg[e_rse_boolean]) != NULL) { stuff(request, blr_boolean); GEN_expr(request, node); } if ((list = rse->nod_arg[e_rse_sort]) != NULL) gen_sort(request, list); if ((list = rse->nod_arg[e_rse_reduced]) != NULL) { stuff(request, blr_project); stuff(request, list->nod_count); dsql_nod** ptr = list->nod_arg; for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++) { GEN_expr(request, *ptr); } } // if the user specified an access plan to use, add it here if ((node = rse->nod_arg[e_rse_plan]) != NULL) { stuff(request, blr_plan); gen_plan(request, node); } #ifdef SCROLLABLE_CURSORS /* generate a statement to be executed if the user scrolls in a direction other than forward; a message is sent outside the normal send/receive protocol to specify the direction and offset to scroll; note that we do this only on a SELECT type statement and only when talking to a 4.1 engine or greater */ if (request->req_type == REQ_SELECT && request->req_dbb->dbb_base_level >= 5) { stuff(request, blr_receive); stuff(request, request->req_async->msg_number); stuff(request, blr_seek); const dsql_par* parameter = request->req_async->msg_parameters; gen_parameter(request, parameter->par_next); gen_parameter(request, parameter); } #endif stuff(request, blr_end); } /** gen_searched_case @brief Generate BLR for CASE function (searched) @param request @param node **/ static void gen_searched_case( dsql_req* request, const dsql_nod* node) { // blr_value_if is used for building the case expression stuff(request, blr_cast); gen_descriptor(request, &node->nod_desc, true); const SSHORT count = node->nod_arg[e_searched_case_search_conditions]->nod_count; dsql_nod* boolean_list = node->nod_arg[e_searched_case_search_conditions]; dsql_nod* results_list = node->nod_arg[e_searched_case_results]; dsql_nod* const* bptr = boolean_list->nod_arg; dsql_nod* const* rptr = results_list->nod_arg; for (const dsql_nod* const* const end = bptr + count; bptr < end; bptr++, rptr++) { stuff(request, blr_value_if); GEN_expr(request, *bptr); GEN_expr(request, *rptr); } // else_result GEN_expr(request, node->nod_arg[e_searched_case_results]->nod_arg[count]); } /** gen_select @brief Generate BLR for a SELECT statement. @param request @param rse **/ static void gen_select( dsql_req* request, dsql_nod* rse) { const dsql_rel* relation; dsql_ctx* context; fb_assert(rse->nod_type == nod_rse); // Set up parameter for things in the select list const dsql_nod* list = rse->nod_arg[e_rse_items]; dsql_nod* const* ptr = list->nod_arg; for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++) { dsql_par* parameter = MAKE_parameter(request->req_receive, true, true, 0, *ptr); parameter->par_node = *ptr; MAKE_desc(request, ¶meter->par_desc, *ptr, NULL); } // Set up parameter to handle EOF dsql_par* parameter_eof = MAKE_parameter(request->req_receive, false, false, 0, NULL); request->req_eof = parameter_eof; parameter_eof->par_desc.dsc_dtype = dtype_short; parameter_eof->par_desc.dsc_scale = 0; parameter_eof->par_desc.dsc_length = sizeof(SSHORT); // Save DBKEYs for possible update later list = rse->nod_arg[e_rse_streams]; if (!rse->nod_arg[e_rse_reduced]) { dsql_nod* const* ptr2 = list->nod_arg; for (const dsql_nod* const* const end2 = ptr2 + list->nod_count; ptr2 < end2; ptr2++) { dsql_nod* item = *ptr2; if (item && item->nod_type == nod_relation) { context = (dsql_ctx*) item->nod_arg[e_rel_context]; if (relation = context->ctx_relation) { // Set up dbkey dsql_par* parameter = MAKE_parameter(request->req_receive, false, false, 0, NULL); parameter->par_dbkey_ctx = context; parameter->par_desc.dsc_dtype = dtype_text; parameter->par_desc.dsc_ttype() = ttype_binary; parameter->par_desc.dsc_length = relation->rel_dbkey_length; // Set up record version - for post v33 databases parameter = MAKE_parameter(request->req_receive, false, false, 0, NULL); parameter->par_rec_version_ctx = context; parameter->par_desc.dsc_dtype = dtype_text; parameter->par_desc.dsc_ttype() = ttype_binary; parameter->par_desc.dsc_length = relation->rel_dbkey_length / 2; } } } } #ifdef SCROLLABLE_CURSORS /* define the parameters for the scrolling message--offset and direction, in that order to make it easier to generate the request */ if (request->req_type == REQ_SELECT && request->req_dbb->dbb_base_level >= 5) { dsql_par* parameter = MAKE_parameter(request->req_async, false, false, 0, NULL); parameter->par_desc.dsc_dtype = dtype_short; parameter->par_desc.dsc_length = sizeof(USHORT); parameter->par_desc.dsc_scale = 0; parameter->par_desc.dsc_flags = 0; parameter->par_desc.dsc_sub_type = 0; parameter = MAKE_parameter(request->req_async, false, false, 0, NULL); parameter->par_desc.dsc_dtype = dtype_long; parameter->par_desc.dsc_length = sizeof(ULONG); parameter->par_desc.dsc_scale = 0; parameter->par_desc.dsc_flags = 0; parameter->par_desc.dsc_sub_type = 0; } #endif // Generate definitions for the messages GEN_port(request, request->req_receive); dsql_msg* message = request->req_send; if (message->msg_parameter) GEN_port(request, message); else request->req_send = NULL; #ifdef SCROLLABLE_CURSORS if (request->req_type == REQ_SELECT && request->req_dbb->dbb_base_level >= 5) GEN_port(request, request->req_async); #endif // If there is a send message, build a RECEIVE if ((message = request->req_send) != NULL) { stuff(request, blr_receive); stuff(request, message->msg_number); } // Generate FOR loop message = request->req_receive; stuff(request, blr_for); stuff(request, blr_stall); gen_rse(request, rse); stuff(request, blr_send); stuff(request, message->msg_number); stuff(request, blr_begin); // Build body of FOR loop SSHORT constant; dsc constant_desc; constant_desc.dsc_dtype = dtype_short; constant_desc.dsc_scale = 0; constant_desc.dsc_sub_type = 0; constant_desc.dsc_flags = 0; constant_desc.dsc_length = sizeof(SSHORT); constant_desc.dsc_address = (UCHAR*) & constant; // Add invalid usage here stuff(request, blr_assignment); constant = 1; gen_constant(request, &constant_desc, USE_VALUE); gen_parameter(request, request->req_eof); for (dsql_par* parameter = message->msg_parameters; parameter; parameter = parameter->par_next) { if (parameter->par_node) { stuff(request, blr_assignment); GEN_expr(request, parameter->par_node); gen_parameter(request, parameter); } if (context = parameter->par_dbkey_ctx) { stuff(request, blr_assignment); stuff(request, blr_dbkey); stuff_context(request, context); gen_parameter(request, parameter); } if (context = parameter->par_rec_version_ctx) { stuff(request, blr_assignment); stuff(request, blr_record_version); stuff_context(request, context); gen_parameter(request, parameter); } } stuff(request, blr_end); stuff(request, blr_send); stuff(request, message->msg_number); stuff(request, blr_assignment); constant = 0; gen_constant(request, &constant_desc, USE_VALUE); gen_parameter(request, request->req_eof); } /** gen_simple_case @brief Generate BLR for CASE function (simple) @param request @param node **/ static void gen_simple_case( dsql_req* request, const dsql_nod* node) { // blr_value_if is used for building the case expression stuff(request, blr_cast); gen_descriptor(request, &node->nod_desc, true); SSHORT count = node->nod_arg[e_simple_case_when_operands]->nod_count; dsql_nod* when_list = node->nod_arg[e_simple_case_when_operands]; dsql_nod* results_list = node->nod_arg[e_simple_case_results]; dsql_nod* const* wptr = when_list->nod_arg; dsql_nod* const* rptr = results_list->nod_arg; for (const dsql_nod* const* const end = wptr + count; wptr < end; wptr++, rptr++) { stuff(request, blr_value_if); stuff(request, blr_eql); GEN_expr(request, node->nod_arg[e_simple_case_case_operand]); GEN_expr(request, *wptr); GEN_expr(request, *rptr); } // else_result GEN_expr(request, node->nod_arg[e_simple_case_results]->nod_arg[count]); } /** gen_sort @brief Generate a sort clause. @param request @param list **/ static void gen_sort( dsql_req* request, dsql_nod* list) { stuff(request, blr_sort); stuff(request, list->nod_count); dsql_nod* const* ptr = list->nod_arg; for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++) { dsql_nod* nulls_placement = (*ptr)->nod_arg[e_order_nulls]; if (nulls_placement) { switch (nulls_placement->getSlong()) { case NOD_NULLS_FIRST: stuff(request, blr_nullsfirst); break; case NOD_NULLS_LAST: stuff(request, blr_nullslast); break; } } if ((*ptr)->nod_arg[e_order_flag]) stuff(request, blr_descending); else stuff(request, blr_ascending); GEN_expr(request, (*ptr)->nod_arg[e_order_field]); } } /** gen_statement @brief Generate BLR for DML statements. @param request @param node **/ static void gen_statement(dsql_req* request, const dsql_nod* node) { dsql_nod* rse = NULL; const dsql_msg* message = NULL; bool send_before_for = !(request->req_flags & REQ_dsql_upd_or_ins); switch (node->nod_type) { case nod_store: rse = node->nod_arg[e_sto_rse]; break; case nod_modify: rse = node->nod_arg[e_mod_rse]; break; case nod_erase: rse = node->nod_arg[e_era_rse]; break; default: send_before_for = false; break; } if (request->req_type == REQ_EXEC_PROCEDURE && send_before_for) { if ((message = request->req_receive)) { stuff(request, blr_send); stuff(request, message->msg_number); } } if (rse) { stuff(request, blr_for); GEN_expr(request, rse); } if (request->req_type == REQ_EXEC_PROCEDURE) { if ((message = request->req_receive)) { stuff(request, blr_begin); if (!send_before_for) { stuff(request, blr_send); stuff(request, message->msg_number); } } } dsql_nod* temp; dsql_ctx* context; dsql_str* name; switch (node->nod_type) { case nod_store: stuff(request, node->nod_arg[e_sto_return] ? blr_store2 : blr_store); GEN_expr(request, node->nod_arg[e_sto_relation]); GEN_statement(request, node->nod_arg[e_sto_statement]); if (node->nod_arg[e_sto_return]) { GEN_statement(request, node->nod_arg[e_sto_return]); } break; case nod_modify: stuff(request, node->nod_arg[e_mod_return] ? blr_modify2 : blr_modify); temp = node->nod_arg[e_mod_source]; context = (dsql_ctx*) temp->nod_arg[e_rel_context]; stuff_context(request, context); temp = node->nod_arg[e_mod_update]; context = (dsql_ctx*) temp->nod_arg[e_rel_context]; stuff_context(request, context); GEN_statement(request, node->nod_arg[e_mod_statement]); if (node->nod_arg[e_mod_return]) { GEN_statement(request, node->nod_arg[e_mod_return]); } break; case nod_modify_current: stuff(request, node->nod_arg[e_mdc_return] ? blr_modify2 : blr_modify); context = (dsql_ctx*) node->nod_arg[e_mdc_context]; stuff_context(request, context); temp = node->nod_arg[e_mdc_update]; context = (dsql_ctx*) temp->nod_arg[e_rel_context]; stuff_context(request, context); GEN_statement(request, node->nod_arg[e_mdc_statement]); if (node->nod_arg[e_mdc_return]) { GEN_statement(request, node->nod_arg[e_mdc_return]); } break; case nod_erase: temp = node->nod_arg[e_era_relation]; context = (dsql_ctx*) temp->nod_arg[e_rel_context]; if (node->nod_arg[e_era_return]) { stuff(request, blr_begin); GEN_statement(request, node->nod_arg[e_era_return]); stuff(request, blr_erase); stuff_context(request, context); stuff(request, blr_end); } else { stuff(request, blr_erase); stuff_context(request, context); } break; case nod_erase_current: context = (dsql_ctx*) node->nod_arg[e_erc_context]; if (node->nod_arg[e_erc_return]) { stuff(request, blr_begin); GEN_statement(request, node->nod_arg[e_erc_return]); stuff(request, blr_erase); stuff_context(request, context); stuff(request, blr_end); } else { stuff(request, blr_erase); stuff_context(request, context); } break; case nod_exec_procedure: stuff(request, blr_exec_proc); name = (dsql_str*) node->nod_arg[e_exe_procedure]; stuff_meta_string(request, name->str_data); // Input parameters if ( (temp = node->nod_arg[e_exe_inputs]) ) { stuff_word(request, temp->nod_count); dsql_nod** ptr = temp->nod_arg; const dsql_nod* const* end = ptr + temp->nod_count; while (ptr < end) { GEN_expr(request, *ptr++); } } else { stuff_word(request, 0); } // Output parameters if ( ( temp = node->nod_arg[e_exe_outputs]) ) { stuff_word(request, temp->nod_count); dsql_nod** ptr = temp->nod_arg; const dsql_nod* const* end = ptr + temp->nod_count; while (ptr < end) { GEN_expr(request, *ptr++); } } else { stuff_word(request, 0); } break; default: fb_assert(false); } if (message) { stuff(request, blr_end); } } /** gen_sys_function @brief Generate a system defined function. @param request @param node **/ static void gen_sys_function(dsql_req* request, const dsql_nod* node) { stuff(request, blr_sys_function); stuff_cstring(request, ((dsql_str*) node->nod_arg[e_sysfunc_name])->str_data); const dsql_nod* list; if ((node->nod_count == e_sysfunc_args + 1) && (list = node->nod_arg[e_sysfunc_args])) { stuff(request, list->nod_count); dsql_nod* const* ptr = list->nod_arg; for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++) { GEN_expr(request, *ptr); } } else stuff(request, 0); } /** gen_table_lock @brief Generate tpb for table lock. If lock level is specified, it overrrides the transaction lock level. @param request @param tbl_lock @param lock_level **/ static void gen_table_lock( dsql_req* request, const dsql_nod* tbl_lock, USHORT lock_level) { if ((!tbl_lock) || (tbl_lock->nod_type != nod_table_lock)) return; const dsql_nod* tbl_names = tbl_lock->nod_arg[e_lock_tables]; SSHORT flags = 0; if (tbl_lock->nod_arg[e_lock_mode]) flags = tbl_lock->nod_arg[e_lock_mode]->nod_flags; if (flags & NOD_PROTECTED) lock_level = isc_tpb_protected; else if (flags & NOD_SHARED) lock_level = isc_tpb_shared; const USHORT lock_mode = (flags & NOD_WRITE) ? isc_tpb_lock_write : isc_tpb_lock_read; const dsql_nod* const* ptr = tbl_names->nod_arg; for (const dsql_nod* const* const end = ptr + tbl_names->nod_count; ptr < end; ptr++) { if ((*ptr)->nod_type != nod_relation_name) continue; stuff(request, lock_mode); // stuff table name const dsql_str* temp = (dsql_str*) ((*ptr)->nod_arg[e_rln_name]); stuff_cstring(request, reinterpret_cast(temp->str_data)); stuff(request, lock_level); } } /** gen_udf @brief Generate a user defined function. @param request @param node **/ static void gen_udf( dsql_req* request, const dsql_nod* node) { const dsql_udf* userFunc = (dsql_udf*) node->nod_arg[0]; stuff(request, blr_function); stuff_string(request, userFunc->udf_name); const dsql_nod* list; if ((node->nod_count == 2) && (list = node->nod_arg[1])) { stuff(request, list->nod_count); dsql_nod* const* ptr = list->nod_arg; for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++) { GEN_expr(request, *ptr); } } else stuff(request, 0); } /** gen_union @brief Generate a union of substreams. @param request @param union_node **/ static void gen_union( dsql_req* request, const dsql_nod* union_node) { if (union_node->nod_arg[0]->nod_flags & NOD_UNION_RECURSIVE) { stuff(request, blr_recurse); } else { stuff(request, blr_union); } // Obtain the context for UNION from the first dsql_map* node dsql_nod* items = union_node->nod_arg[e_rse_items]; dsql_nod* map_item = items->nod_arg[0]; // AB: First item could be a virtual field generated by derived table. if (map_item->nod_type == nod_derived_field) { map_item = map_item->nod_arg[e_alias_value]; } dsql_ctx* union_context = (dsql_ctx*) map_item->nod_arg[e_map_context]; stuff_context(request, union_context); // secondary context number must be present once in generated blr union_context->ctx_flags &= ~CTX_recursive; dsql_nod* streams = union_node->nod_arg[e_rse_streams]; stuff(request, streams->nod_count); // number of substreams dsql_nod** ptr = streams->nod_arg; for (const dsql_nod* const* const end = ptr + streams->nod_count; ptr < end; ptr++) { dsql_nod* sub_rse = *ptr; gen_rse(request, sub_rse); items = sub_rse->nod_arg[e_rse_items]; stuff(request, blr_map); stuff_word(request, items->nod_count); USHORT count = 0; dsql_nod** iptr = items->nod_arg; for (const dsql_nod* const* const iend = iptr + items->nod_count; iptr < iend; iptr++) { stuff_word(request, count); GEN_expr(request, *iptr); count++; } } } /** stuff_context @brief Write a context number into the BLR buffer. Check for possible overflow. @param request @param context **/ static void stuff_context(dsql_req* request, const dsql_ctx* context) { if (context->ctx_context > MAX_UCHAR) { ERRD_post(isc_too_many_contexts, 0); } stuff(request, context->ctx_context); if (context->ctx_flags & CTX_recursive) { if (context->ctx_recursive > MAX_UCHAR) { ERRD_post(isc_too_many_contexts, 0); } stuff(request, context->ctx_recursive); } } /** stuff_cstring @brief Write out a string with one byte of length. @param request @param string **/ static void stuff_cstring(dsql_req* request, const char* string) { stuff_string(request, string, strlen(string)); } /** stuff_meta_string @brief Write out a string in metadata charset with one byte of length. @param request @param string **/ static void stuff_meta_string(dsql_req* request, const char* string) { request->append_meta_string(string); } /** stuff_string @brief Write out a string with one byte of length. @param request @param string **/ static void stuff_string(dsql_req* request, const char* string, int len) { fb_assert(len >= 0 && len <= 255); stuff(request, len); while (len--) stuff(request, *string++); } static void stuff_string(dsql_req* request, const Firebird::MetaName& name) { stuff_string(request, name.c_str(), name.length()); } /** stuff_word @brief Cram a word into the blr buffer. If the buffer is getting ready to overflow, expand it. @param request @param word **/ static void stuff_word( dsql_req* request, USHORT word) { stuff(request, word); stuff(request, word >> 8); }