From 237c91d82b99db06e6aa6e28853e9649325df69d Mon Sep 17 00:00:00 2001 From: dimitr Date: Sun, 21 Dec 2014 13:45:26 +0000 Subject: [PATCH] Front-ported CORE-4437 and CORE-4438. --- src/gpre/c_cxx.cpp | 58 ++++- src/gpre/cme.cpp | 6 +- src/gpre/cmp.cpp | 167 +++++++++----- src/gpre/gpre.h | 8 +- src/gpre/hsh.h | 3 +- src/gpre/languages/ada.cpp | 55 ++++- src/gpre/languages/cob.cpp | 39 +++- src/gpre/languages/ftn.cpp | 39 +++- src/gpre/languages/pas.cpp | 67 +++++- src/gpre/languages/rmc.cpp | 37 +++- src/gpre/msc.cpp | 16 ++ src/gpre/msc_proto.h | 1 + src/gpre/par.cpp | 8 +- src/gpre/sqe.cpp | 51 +++-- src/gpre/sqe_proto.h | 3 +- src/gpre/sql.cpp | 440 +++++++++++++++++++++++++++++++++++-- src/gpre/words.h | 1 + 17 files changed, 870 insertions(+), 129 deletions(-) diff --git a/src/gpre/c_cxx.cpp b/src/gpre/c_cxx.cpp index 528fca1742..c516af7e76 100644 --- a/src/gpre/c_cxx.cpp +++ b/src/gpre/c_cxx.cpp @@ -2487,15 +2487,36 @@ static void gen_loop( const act* action, int column) gen_s_start(action, column); const gpre_req* request = action->act_request; const gpre_port* port = request->req_primary; + printa(column, "if (!SQLCODE) "); column += INDENT; begin(column); + gen_receive(action, column, port); + + endp(column); + column -= INDENT; + gen_name(name, port->por_references, true); printa(column, "if (!SQLCODE && !%s)", name); printa(column + INDENT, "SQLCODE = 100;"); - endp(column); - column -= INDENT; + + if (request->req_flags & REQ_sql_returning) + { + printa(column, "if (!SQLCODE && %s)", name); + column += INDENT; + begin(column); + + gpre_nod* var_list = (gpre_nod*) action->act_object; + for (int i = 0; var_list && i < var_list->nod_count; i++) + { + align(column); + asgn_to(action, (ref*) (var_list->nod_arg[i]), column); + } + + endp(column); + column -= INDENT; + } } @@ -3023,12 +3044,43 @@ static void gen_s_start( const act* action, int column) { make_ok_test(action, request, column); column += INDENT; + begin(column); } gen_start(action, port, column, false); + set_sqlcode(action, column); if (action->act_error || (action->act_flags & ACT_sql)) + { + endp(column); column -= INDENT; + } + + if (request->req_type == REQ_insert && (request->req_flags & REQ_sql_returning)) + { + printa(column, "if (!SQLCODE) "); + column += INDENT; + begin(column); + + gen_receive(action, column, request->req_primary); + + endp(column); + column -= INDENT; + + printa(column, "if (!SQLCODE) "); + column += INDENT; + begin(column); + + gpre_nod* var_list = (gpre_nod*) action->act_object; + for (int i = 0; var_list && i < var_list->nod_count; i++) + { + align(column); + asgn_to(action, (ref*) (var_list->nod_arg[i]), column); + } + + endp(column); + column -= INDENT; + } if (action->act_type == ACT_open) { @@ -3037,8 +3089,6 @@ static void gen_s_start( const act* action, int column) endp(column); column -= INDENT; } - - set_sqlcode(action, column); } diff --git a/src/gpre/cme.cpp b/src/gpre/cme.cpp index d3488eb47d..f2d29d4abe 100644 --- a/src/gpre/cme.cpp +++ b/src/gpre/cme.cpp @@ -1469,7 +1469,11 @@ static void cmp_field( const gpre_nod* node, gpre_req* request) if (!field) puts("cmp_field: symbol missing"); - if (field->fld_flags & FLD_dbkey) + if (context->ctx_flags & CTX_null) + { + request->add_byte(blr_null); + } + else if (field->fld_flags & FLD_dbkey) { request->add_byte(blr_dbkey); request->add_byte(context->ctx_internal); diff --git a/src/gpre/cmp.cpp b/src/gpre/cmp.cpp index b448aac237..2cab4027b1 100644 --- a/src/gpre/cmp.cpp +++ b/src/gpre/cmp.cpp @@ -45,6 +45,7 @@ static void cmp_any(gpre_req*); static void cmp_assignment(gpre_nod*, gpre_req*); +static void cmp_assignment_list(gpre_nod*, gpre_req*); static void cmp_blob(blb*, bool); static void cmp_blr(gpre_req*); static void cmp_erase(act*, gpre_req*); @@ -56,6 +57,7 @@ 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_returning(gpre_req*, gpre_nod*); 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); @@ -63,7 +65,7 @@ static void cmp_sdl_subscript(gpre_req*, USHORT, const slc*); 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 cmp_store(gpre_req*, gpre_nod*); static void expand_references(ref*); static gpre_port* make_port(gpre_req*, ref*); static void make_receive(gpre_port*, gpre_req*); @@ -227,8 +229,9 @@ void CMP_compile_request( gpre_req* request) // 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)) + if ((request->req_flags & REQ_sql_returning) || + ((request->req_type != REQ_insert) && (request->req_type != REQ_store2) + && (request->req_type != REQ_set_generator))) { request->req_primary = port = make_port(request, reference); } @@ -266,6 +269,8 @@ void CMP_compile_request( gpre_req* request) break; case ACT_select: case ACT_fetch: + case ACT_loop: + case ACT_insert: cmp_fetch(action); break; } @@ -528,6 +533,26 @@ static void cmp_assignment( gpre_nod* node, gpre_req* request) } +//____________________________________________________________ +// +// Compile a build assignment statement. +// + +static void cmp_assignment_list( gpre_nod* list, gpre_req* request) +{ + 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); +} + + //____________________________________________________________ // // Compile a blob parameter block, if required. @@ -639,7 +664,7 @@ static void cmp_blr( gpre_req* request) case REQ_insert: case REQ_store: case REQ_store2: - cmp_store(request); + cmp_store(request, request->req_node); break; case REQ_mass_update: @@ -967,7 +992,7 @@ static void cmp_for( gpre_req* request) static void cmp_loop( gpre_req* request) { - gpre_nod* node = request->req_node; + gpre_nod* req_node = request->req_node; gpre_rse* selection = request->req_rse; gpre_port* primary = request->req_primary; @@ -991,41 +1016,38 @@ static void cmp_loop( gpre_req* request) CME_rse(selection, request); request->add_byte(blr_begin); + gpre_nod* node = (req_node->nod_type == nod_list) ? req_node->nod_arg[0] : req_node; + switch (node->nod_type) { case nod_modify: { - request->add_byte(blr_modify); + const int blr_op = (request->req_flags & REQ_sql_returning) ? blr_modify2 : blr_modify; + request->add_byte(blr_op); 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); + cmp_assignment_list(node->nod_arg[0], request); + if (request->req_flags & REQ_sql_returning) + cmp_returning(request, node->nod_arg[1]); } 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); - } + cmp_store(request, node); break; case nod_erase: - request->add_byte(blr_erase); - request->add_byte(for_context->ctx_internal); + if (request->req_flags & REQ_sql_returning) + { + request->add_byte(blr_begin); + cmp_returning(request, node->nod_arg[0]); + request->add_byte(blr_erase); + request->add_byte(for_context->ctx_internal); + request->add_byte(blr_end); + } + else + { + request->add_byte(blr_erase); + request->add_byte(for_context->ctx_internal); + } break; default: fb_assert(false); @@ -1038,6 +1060,29 @@ static void cmp_loop( gpre_req* request) CME_expr(counter, request); request->add_byte(blr_end); + + if (req_node->nod_type == nod_list) + { + request->add_byte(blr_if); + request->add_byte(blr_eql); + CME_expr(counter, request); + CME_expr(lit0, request); + + request->add_byte(blr_begin); + + node = req_node->nod_arg[1]; + for_context->ctx_flags |= CTX_null; + cmp_store(request, node); + for_context->ctx_flags &= ~CTX_null; + + request->add_byte(blr_assignment); + CME_expr(lit1, request); + CME_expr(counter, request); + + request->add_byte(blr_end); + request->add_byte(blr_end); + } + request->add_byte(blr_end); } @@ -1306,6 +1351,30 @@ static void cmp_ready( gpre_req* request) } +//____________________________________________________________ +// +// Build assignments for values to be returned. +// + +static void cmp_returning(gpre_req* request, gpre_nod* ret_list) +{ + gpre_nod* value = MSC_node(nod_value, 1); + + request->add_byte(blr_begin); + + for (int i = 0; i < ret_list->nod_count; i++) + { + request->add_byte(blr_assignment); + CME_expr(ret_list->nod_arg[i]->nod_arg[0], request); + ref* reference = (ref*) ret_list->nod_arg[i]->nod_arg[1]; + value->nod_arg[0] = (gpre_nod*) reference->ref_friend; + CME_expr(value, request); + } + + request->add_byte(blr_end); +} + + //____________________________________________________________ // // Build in a fudge to bias the language specific subscript to the @@ -1546,31 +1615,37 @@ static void cmp_slice( gpre_req* request) // Generate blr for a store request. // -static void cmp_store( gpre_req* request) +static void cmp_store( gpre_req* request, gpre_nod* node) { + gpre_ctx* context = (gpre_ctx*) node->nod_arg[0]; + gpre_nod* list = node->nod_arg[1]; + gpre_nod* ret_list = node->nod_arg[2]; + // Make the store statement under the receive - if (request->req_type == REQ_store2) + if (request->req_flags & REQ_sql_returning) + { + if (request->req_type == REQ_insert) + make_send(request->req_primary, request); + + request->add_byte(blr_store2); + } + else if (request->req_type == REQ_store2) request->add_byte(blr_store2); else request->add_byte(blr_store); - CME_relation(request->req_contexts, request); + CME_relation(context, request); // Make an assignment list - gpre_nod* list = request->req_node; - request->add_byte(blr_begin); + cmp_assignment_list(list, request); - gpre_nod** ptr = list->nod_arg; - for (const gpre_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++) + if (request->req_flags & REQ_sql_returning) { - cmp_assignment(*ptr, request); + cmp_returning(request, ret_list); } - - request->add_byte(blr_end); - - if (request->req_type == REQ_store2) + else if (request->req_type == REQ_store2) { // whip through actions to find return list @@ -1584,15 +1659,7 @@ static void cmp_store( gpre_req* request) 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); + cmp_assignment_list(update->upd_assignments, request); request->add_byte(blr_end); } } diff --git a/src/gpre/gpre.h b/src/gpre/gpre.h index 4aba1f2166..1d893f2bcd 100644 --- a/src/gpre/gpre.h +++ b/src/gpre/gpre.h @@ -1242,7 +1242,8 @@ enum req_flags_vals { REQ_sql_blob_open = 8192, // request is SQL open blob cursor REQ_sql_blob_create = 16384, // request is SQL create blob cursor REQ_sql_database_dyn = 32768, // request is to generate DYN to add files o database - REQ_blr_version4 = 65536 // request must generate blr_version4 + REQ_blr_version4 = 65536, // request must generate blr_version4 + REQ_sql_returning = 131072 // RETURNING clause is present }; const size_t REQ_LEN = sizeof(gpre_req); @@ -1261,6 +1262,11 @@ struct gpre_ctx { gpre_prc* ctx_procedure; // procedure for context gpre_nod* ctx_prc_inputs; // procedure input parameters gpre_rse* ctx_stream; // stream for context + USHORT ctx_flags; // misc flags +}; + +enum ctx_flags_vals { + CTX_null = 1 // context evaluates to NULL }; const size_t CTX_LEN = sizeof(gpre_ctx); diff --git a/src/gpre/hsh.h b/src/gpre/hsh.h index 57b66ee6fd..f7facbb5ac 100644 --- a/src/gpre/hsh.h +++ b/src/gpre/hsh.h @@ -328,7 +328,8 @@ {"RESOURCE", KW_RESOURCE}, {"RESTRICT", KW_RESTRICT}, {"RETAIN", KW_RETAIN}, - {"RETURNING_VALUES", KW_RETURNING}, + {"RETURNING", KW_RETURNING}, + {"RETURNING_VALUES", KW_RETURNING_VALUES}, {"RETURNS", KW_RETURNS}, {"REVOKE", KW_REVOKE}, {"RIGHT", KW_RIGHT}, diff --git a/src/gpre/languages/ada.cpp b/src/gpre/languages/ada.cpp index db87eb257e..75516fc623 100644 --- a/src/gpre/languages/ada.cpp +++ b/src/gpre/languages/ada.cpp @@ -2201,17 +2201,36 @@ static void gen_loop( const act* action, int column) gen_s_start(action, column); gpre_req* request = action->act_request; gpre_port* port = request->req_primary; + printa(column, "if SQLCODE = 0 then"); column += INDENT; + gen_receive(action, column, port); + column -= INDENT; + endif(column); + TEXT name[MAX_REF_SIZE]; gen_name(name, port->por_references, true); printa(column, "if (SQLCODE = 0) and (%s = 0) then", name); printa(column + INDENT, "SQLCODE := 100;"); endif(column); - column -= INDENT; - endif(column); + + if (request->req_flags & REQ_sql_returning) + { + printa(column, "if (SQLCODE = 0) and (%s /= 0) then", name); + column += INDENT; + + gpre_nod* var_list = (gpre_nod*) action->act_object; + for (int i = 0; var_list && i < var_list->nod_count; i++) + { + align(column); + asgn_to(action, (ref*) (var_list->nod_arg[i]), column); + } + + column -= INDENT; + endif(column); + } } @@ -2733,13 +2752,37 @@ static void gen_s_start( const act* action, int column) asgn_from(action, port->por_references, column); if (action->act_error || (action->act_flags & ACT_sql)) - { make_ok_test(action, request, column); - gen_start(action, port, column + INDENT); + + gen_start(action, port, column); + set_sqlcode(action); + + if (action->act_error || (action->act_flags & ACT_sql)) endif(column); + + if (request->req_type == REQ_insert && (request->req_flags & REQ_sql_returning)) + { + printa(column, "if (SQLCODE = 0) then"); + column += INDENT; + + gen_receive(action, column, request->req_primary); + + endif(column); + column -= INDENT; + + printa(column, "if (SQLCODE = 0) then"); + column += INDENT; + + gpre_nod* var_list = (gpre_nod*) action->act_object; + for (int i = 0; var_list && i < var_list->nod_count; i++) + { + align(column); + asgn_to(action, (ref*) (var_list->nod_arg[i]), column); + } + + endif(column); + column -= INDENT; } - else - gen_start(action, port, column); if (action->act_type == ACT_open) { diff --git a/src/gpre/languages/cob.cpp b/src/gpre/languages/cob.cpp index 597d670112..8b8afabfa4 100644 --- a/src/gpre/languages/cob.cpp +++ b/src/gpre/languages/cob.cpp @@ -21,7 +21,7 @@ // All Rights Reserved. // Contributor(s): ______________________________________. // Solaris x86 changes - Konstantin Kuznetsov, Neil McCalden -// 8-Mar-2002 FSG (Frank Schlottmann-Gödde) tiny cobol support +// 8-Mar-2002 FSG (Frank Schlottmann-G�dde) tiny cobol support // fixed Bug No. 526204* // // @@ -2713,16 +2713,30 @@ static void gen_loop( const act* action) { gen_s_start(action); const gpre_req* request = action->act_request; - printa(names[COLUMN], false, "IF SQLCODE = 0 THEN"); const gpre_port* port = request->req_primary; + + printa(names[COLUMN], false, "IF SQLCODE = 0 THEN"); gen_receive(action, port); + printa(names[COLUMN], false, "END-IF"); TEXT name[MAX_REF_SIZE]; gen_name(name, port->por_references, true); printa(names[COLUMN], false, "IF SQLCODE = 0 AND %s = 0 THEN ", name); printa(names[COLUMN], false, "MOVE 100 TO SQLCODE"); printa(names[COLUMN], false, "END-IF"); - printa(names[COLUMN], false, "END-IF"); + + if (request->req_flags & REQ_sql_returning) + { + printa(names[COLUMN], false, "IF SQLCODE = 0 AND %s NOT = 0 THEN", name); + + gpre_nod* var_list = (gpre_nod*) action->act_object; + for (int i = 0; var_list && i < var_list->nod_count; i++) + { + asgn_to(action, (ref*) (var_list->nod_arg[i])); + } + + printa(names[COLUMN], false, "END-IF"); + } } @@ -3233,10 +3247,28 @@ static void gen_s_start( const act* action) } gen_start(action, port); + set_sqlcode(action); if (action->act_error || (action->act_flags & ACT_sql)) printa(names[COLUMN], false, "END-IF"); + if (request->req_type == REQ_insert && (request->req_flags & REQ_sql_returning)) + { + printa(names[COLUMN], false, "IF SQLCODE NOT = 0 THEN"); + gen_receive(action, column, request->req_primary); + printa(names[COLUMN], false, "END-IF"); + + printa(names[COLUMN], false, "IF SQLCODE NOT = 0 THEN"); + + gpre_nod* var_list = (gpre_nod*) action->act_object; + for (int i = 0; var_list && i < var_list->nod_count; i++) + { + asgn_to(action, (ref*) (var_list->nod_arg[i])); + } + + printa(names[COLUMN], false, "END-IF"); + } + if (action->act_type == ACT_open) { printa(names[COLUMN], false, "END-IF"); @@ -3244,7 +3276,6 @@ static void gen_s_start( const act* action) if (gpreGlob.sw_auto) printa(names[COLUMN], false, "END-IF"); printa(names[COLUMN], false, "END-IF"); - set_sqlcode(action); } } diff --git a/src/gpre/languages/ftn.cpp b/src/gpre/languages/ftn.cpp index 37d2baeb1f..f0ca7540bd 100644 --- a/src/gpre/languages/ftn.cpp +++ b/src/gpre/languages/ftn.cpp @@ -2497,17 +2497,31 @@ static void gen_get_segment(const act* action) static void gen_loop(const act* action) { - TEXT name[MAX_REF_SIZE]; - gen_s_start(action); const gpre_req* request = action->act_request; const gpre_port* port = request->req_primary; + printa(COLUMN, "IF (SQLCODE .EQ. 0) THEN"); gen_receive(action, port); + printa(COLUMN, "END IF"); + + TEXT name[MAX_REF_SIZE]; gen_name(name, port->por_references, true); printa(COLUMN, "IF (SQLCODE .EQ. 0 .AND. %s .EQ. 0) ", name); printa(CONTINUE, "SQLCODE = 100"); - printa(COLUMN, "END IF"); + + if (request->req_flags & REQ_sql_returning) + { + printa(COLUMN, "IF (SQLCODE .EQ. 0 .AND. %s .NE. 0) THEN", name); + + gpre_nod* var_list = (gpre_nod*) action->act_object; + for (int i = 0; var_list && i < var_list->nod_count; i++) + { + asgn_to(action, (ref*) (var_list->nod_arg[i])); + } + + printa(COLUMN, "END IF"); + } } @@ -3101,16 +3115,33 @@ static void gen_s_start(const act* action) make_ok_test(action, request); gen_start(action, port); + status_and_stop(action); if (action->act_error || (action->act_flags & ACT_sql)) printa(COLUMN, "END IF"); + if (request->req_type == REQ_insert && (request->req_flags & REQ_sql_returning)) + { + printa(COLUMN, "IF (SQLCODE .EQ. 0) THEN"); + gen_receive(action, column, request->req_primary); + printa(COLUMN, "END IF"); + + printa(COLUMN, "IF (SQLCODE .EQ. 0) THEN"); + + gpre_nod* var_list = (gpre_nod*) action->act_object; + for (int i = 0; var_list && i < var_list->nod_count; i++) + { + asgn_to(action, (ref*) (var_list->nod_arg[i])); + } + + printa(COLUMN, "END IF"); + } + if (action->act_type == ACT_open) { printa(COLUMN, "END IF"); printa(COLUMN, "END IF"); printa(COLUMN, "END IF"); - status_and_stop(action); } } diff --git a/src/gpre/languages/pas.cpp b/src/gpre/languages/pas.cpp index 4a434447e2..67260358a2 100644 --- a/src/gpre/languages/pas.cpp +++ b/src/gpre/languages/pas.cpp @@ -2149,22 +2149,40 @@ static void gen_get_segment( const act* action, int column) static void gen_loop( const act* action, int column) { - const gpre_req* request; - gpre_port* port; - TEXT name[MAX_REF_SIZE]; - gen_s_start(action, column); - request = action->act_request; - port = request->req_primary; + const gpre_req* request = action->act_request; + gpre_port* port = request->req_primary; + printa(column, "if SQLCODE = 0 then"); column += INDENT; begin(column); + gen_receive(action, column, port); + + endp(column); + column -= INDENT; + + TEXT name[MAX_REF_SIZE]; gen_name(name, port->por_references, true); printa(column, "if (SQLCODE = 0) and (%s = 0)", name); printa(column + INDENT, "then SQLCODE := 100;"); - endp(column); - column -= INDENT; + + if (request->req_flags & REQ_sql_returning) + { + printa(column, "if (SQLCODE = 0) and (%s <> 0)", name); + column += INDENT; + begin(column); + + gpre_nod* var_list = (gpre_nod*) action->act_object; + for (int i = 0; var_list && i < var_list->nod_count; i++) + { + align(column); + asgn_to(action, (ref*) (var_list->nod_arg[i]), column); + } + + endp(column); + column -= INDENT; + } } @@ -2686,12 +2704,43 @@ static void gen_s_start( const act* action, int column) { make_ok_test(action, request, column); column += INDENT; + begin(column); } gen_start(action, port, column); + set_sqlcode(action, column); if (action->act_error || (action->act_flags & ACT_sql)) + { + endp(column); column -= INDENT; + } + + if (request->req_type == REQ_insert && (request->req_flags & REQ_sql_returning)) + { + printa(column, "if (SQLCODE = 0) "); + column += INDENT; + begin(column); + + gen_receive(action, column, request->req_primary); + + endp(column); + column -= INDENT; + + printa(column, "if (SQLCODE = 0) "); + column += INDENT; + begin(column); + + gpre_nod* var_list = (gpre_nod*) action->act_object; + for (int i = 0; var_list && i < var_list->nod_count; i++) + { + align(column); + asgn_to(action, (ref*) (var_list->nod_arg[i]), column); + } + + endp(column); + column -= INDENT; + } if (action->act_type == ACT_open) { @@ -2702,8 +2751,6 @@ static void gen_s_start( const act* action, int column) ends(column); column -= INDENT; } - - set_sqlcode(action, column); } diff --git a/src/gpre/languages/rmc.cpp b/src/gpre/languages/rmc.cpp index 6c18b33ecf..7c42ceba4f 100644 --- a/src/gpre/languages/rmc.cpp +++ b/src/gpre/languages/rmc.cpp @@ -2816,16 +2816,30 @@ static void gen_loop( const act* action) { gen_s_start(action); const gpre_req* request = action->act_request; - printa(names[COLUMN], false, "IF SQLCODE = 0 THEN"); const gpre_port* port = request->req_primary; + + printa(names[COLUMN], false, "IF SQLCODE = 0 THEN"); gen_receive(action, port); + printa(names[COLUMN], false, "END-IF"); TEXT name[MAX_REF_SIZE]; gen_name(name, port->por_references, true); printa(names[COLUMN], false, "IF SQLCODE = 0 AND %s = 0 THEN ", name); printa(names[COLUMN], false, "MOVE 100 TO SQLCODE"); printa(names[COLUMN], false, "END-IF"); - printa(names[COLUMN], false, "END-IF"); + + if (request->req_flags & REQ_sql_returning) + { + printa(names[COLUMN], false, "IF SQLCODE = 0 AND %s NOT = 0 THEN", name); + + gpre_nod* var_list = (gpre_nod*) action->act_object; + for (int i = 0; var_list && i < var_list->nod_count; i++) + { + asgn_to(action, (ref*) (var_list->nod_arg[i])); + } + + printa(names[COLUMN], false, "END-IF"); + } } @@ -3358,10 +3372,28 @@ static void gen_s_start( const act* action) } gen_start(action, port); + set_sqlcode(action); if (action->act_error || (action->act_flags & ACT_sql)) printa(names[COLUMN], false, "END-IF"); + if (request->req_type == REQ_insert && (request->req_flags & REQ_sql_returning)) + { + printa(names[COLUMN], false, "IF SQLCODE = 0 THEN"); + gen_receive(action, column, request->req_primary); + printa(names[COLUMN], false, "END-IF"); + + printa(names[COLUMN], false, "IF SQLCODE = 0 THEN"); + + gpre_nod* var_list = (gpre_nod*) action->act_object; + for (int i = 0; var_list && i < var_list->nod_count; i++) + { + asgn_to(action, (ref*) (var_list->nod_arg[i])); + } + + printa(names[COLUMN], false, "END-IF"); + } + if (action->act_type == ACT_open) { printa(names[COLUMN], false, "END-IF"); @@ -3369,7 +3401,6 @@ static void gen_s_start( const act* action) if (gpreGlob.sw_auto) printa(names[COLUMN], false, "END-IF"); printa(names[COLUMN], false, "END-IF"); - set_sqlcode(action); } } diff --git a/src/gpre/msc.cpp b/src/gpre/msc.cpp index c6a86a36a2..02e684ae71 100644 --- a/src/gpre/msc.cpp +++ b/src/gpre/msc.cpp @@ -472,6 +472,22 @@ gpre_sym* MSC_symbol(sym_t type, const TEXT* string, USHORT length, gpre_ctx* ob } +//____________________________________________________________ +// +// Make a ternary node. +// + +gpre_nod* MSC_ternary(nod_t type, gpre_nod* arg1, gpre_nod* arg2, gpre_nod* arg3) +{ + gpre_nod* node = MSC_node(type, 3); + node->nod_arg[0] = arg1; + node->nod_arg[1] = arg2; + node->nod_arg[2] = arg3; + + return node; +} + + //____________________________________________________________ // // Make a unary node. diff --git a/src/gpre/msc_proto.h b/src/gpre/msc_proto.h index a38d8ac30c..977be19510 100644 --- a/src/gpre/msc_proto.h +++ b/src/gpre/msc_proto.h @@ -46,6 +46,7 @@ ref* MSC_reference(ref**); gpre_req* MSC_request(req_t); SCHAR* MSC_string(const TEXT*); gpre_sym* MSC_symbol(sym_t, const TEXT*, USHORT, gpre_ctx*); +gpre_nod* MSC_ternary(nod_t, gpre_nod*, gpre_nod*, gpre_nod*); gpre_nod* MSC_unary(nod_t, gpre_nod*); gpre_usn* MSC_username(const SCHAR*, USHORT); diff --git a/src/gpre/par.cpp b/src/gpre/par.cpp index 0b7c638e04..1c498bf82f 100644 --- a/src/gpre/par.cpp +++ b/src/gpre/par.cpp @@ -241,7 +241,7 @@ act* PAR_action(const TEXT* base_dir) return cur_statement = par_ready(); case KW_RELEASE_REQUESTS: return cur_statement = par_release(); - case KW_RETURNING: + case KW_RETURNING_VALUES: return par_returning_values(); case KW_START_STREAM: return cur_statement = par_start_stream(); @@ -1981,7 +1981,8 @@ static act* par_end_store(bool special) } gpre_nod* const assignments = MSC_node(nod_list, (SSHORT) count); - request->req_node = assignments; + request->req_node = + MSC_ternary(nod_store, (gpre_nod*) request->req_contexts, assignments, NULL); count = 0; for (ref* reference = request->req_references; reference; reference = reference->ref_next) @@ -2723,7 +2724,8 @@ static act* par_returning_values() } gpre_nod* assignments = MSC_node(nod_list, (SSHORT) count); - request->req_node = assignments; + request->req_node = + MSC_ternary(nod_store, (gpre_nod*) request->req_contexts, assignments, NULL); count = 0; for (ref* reference = request->req_references; reference; reference = reference->ref_next) diff --git a/src/gpre/sqe.cpp b/src/gpre/sqe.cpp index cdefb834cf..c872660b74 100644 --- a/src/gpre/sqe.cpp +++ b/src/gpre/sqe.cpp @@ -174,7 +174,7 @@ static const ops stat_ops[] = static const nod_t relationals[] = { nod_eq, nod_ne, nod_gt, nod_ge, nod_le, nod_lt, nod_containing, - nod_starting, nod_matches, nod_any, nod_missing, nod_between, nod_like, + nod_starting, nod_matches, nod_any, nod_missing, nod_equiv, nod_between, nod_like, nod_and, nod_or, nod_not, nod_ansi_any, nod_ansi_all, nod_nothing }; @@ -929,11 +929,13 @@ ref* SQE_post_reference(gpre_req* request, gpre_fld* field, gpre_ctx* context, g // otherwise false. // -bool SQE_resolve(gpre_nod* node, gpre_req* request, gpre_rse* selection) +bool SQE_resolve(gpre_nod** node_ptr, gpre_req* request, gpre_rse* selection) { bool result = false; act* slice_action = 0; + gpre_nod* node = *node_ptr; + assert_IS_REQ(request); assert_IS_NOD(node); @@ -968,7 +970,7 @@ bool SQE_resolve(gpre_nod* node, gpre_req* request, gpre_rse* selection) gpre_nod** ptr = node->nod_arg; const gpre_nod* const* const end = ptr + node->nod_count; for (; ptr < end; ptr++) - result |= SQE_resolve(*ptr, request, selection); + result |= SQE_resolve(ptr, request, selection); return result; } @@ -979,7 +981,7 @@ bool SQE_resolve(gpre_nod* node, gpre_req* request, gpre_rse* selection) case nod_agg_count: if (node->nod_arg[0]) { - SQE_resolve(node->nod_arg[0], request, selection); + SQE_resolve(&node->nod_arg[0], request, selection); gpre_nod* node_arg = node->nod_arg[0]; const ref* reference = (ref*) node_arg->nod_arg[0]; if (node_arg->nod_type == nod_field && reference && @@ -996,22 +998,22 @@ bool SQE_resolve(gpre_nod* node, gpre_req* request, gpre_rse* selection) gpre_nod** ptr = node->nod_arg[0]->nod_arg; const gpre_nod* const* const end = ptr + node->nod_arg[0]->nod_count; for (; ptr < end; ptr++) - result |= SQE_resolve(*ptr, request, selection); + result |= SQE_resolve(ptr, request, selection); } return result; case nod_gen_id: - return SQE_resolve(node->nod_arg[0], request, selection); + return SQE_resolve(&node->nod_arg[0], request, selection); // Begin date/time/timestamp support case nod_extract: - result |= SQE_resolve(node->nod_arg[1], request, selection); + result |= SQE_resolve(&node->nod_arg[1], request, selection); return result; // End date/time/timestamp support case nod_coalesce: for (int i = 0; i < node->nod_count; i++) - result |= SQE_resolve(node->nod_arg[0]->nod_arg[i], request, selection); + result |= SQE_resolve(&node->nod_arg[0]->nod_arg[i], request, selection); return result; case nod_deferred: @@ -1073,12 +1075,7 @@ bool SQE_resolve(gpre_nod* node, gpre_req* request, gpre_rse* selection) reference->ref_context = context; reference->ref_slice = (slc*) slice_action; - // do not reinit if this is a nod_deffered type - if (node->nod_type != nod_deferred) - node->nod_count = 0; - - node->nod_type = nod_field; - node->nod_arg[0] = (gpre_nod*) reference; + *node_ptr = MSC_unary(nod_field, (gpre_nod*) reference); return false; } @@ -2230,7 +2227,7 @@ static gpre_nod* par_not( gpre_req* request, USHORT* paren_count) node->nod_arg[0] = (gpre_nod*) selection; if (field) { - SQE_resolve(field, 0, selection); + SQE_resolve(&field, 0, selection); gpre_nod* expr = MSC_unary(nod_missing, field); selection->rse_boolean = merge(negate(expr), selection->rse_boolean); } @@ -2756,11 +2753,21 @@ static gpre_nod* par_relational(gpre_req* request, { if (MSC_match(KW_NOT)) negation = !negation; - if (!MSC_match(KW_NULL)) - CPR_s_error("NULL"); - if (expr1->nod_type == nod_array) - expr1->nod_type = nod_field; - node = MSC_unary(nod_missing, expr1); + if (MSC_match(KW_NULL)) + { + if (expr1->nod_type == nod_array) + expr1->nod_type = nod_field; + node = MSC_unary(nod_missing, expr1); + } + else if (MSC_match(KW_DISTINCT)) + { + if (!MSC_match(KW_FROM)) + CPR_s_error("FROM"); + node = MSC_binary(nod_equiv, expr1, SQE_value(request, false, NULL, NULL)); + pair(node->nod_arg[0], node->nod_arg[1]); + } + else + CPR_s_error("NULL or DISTINCT"); } else { @@ -2823,7 +2830,7 @@ static bool resolve_fields(gpre_nod*& fields, gpre_rse* selection) for (int i = 0; i < count; i++) { - gpre_nod* node = ptr[i]; + gpre_nod*& node = ptr[i]; if (node->nod_type == nod_asterisk) { @@ -2835,7 +2842,7 @@ static bool resolve_fields(gpre_nod*& fields, gpre_rse* selection) } else { - aggregate |= SQE_resolve(node, NULL, selection); + aggregate |= SQE_resolve(&node, NULL, selection); pair(node, 0); } } diff --git a/src/gpre/sqe_proto.h b/src/gpre/sqe_proto.h index 824e10c605..27ad18f8b5 100644 --- a/src/gpre/sqe_proto.h +++ b/src/gpre/sqe_proto.h @@ -33,9 +33,10 @@ gpre_nod* SQE_list(pfn_SQE_list_cb, gpre_req*, bool); ref* SQE_parameter(gpre_req*); void SQE_post_field(gpre_nod*, gpre_fld*); ref* SQE_post_reference(gpre_req*, gpre_fld*, gpre_ctx*, gpre_nod*); -bool SQE_resolve(gpre_nod*, gpre_req*, gpre_rse*); +bool SQE_resolve(gpre_nod**, gpre_req*, gpre_rse*); gpre_rse* SQE_select(gpre_req*, bool); gpre_nod* SQE_value(gpre_req*, bool, USHORT*, bool*); +gpre_nod* SQE_value_or_null(gpre_req*, bool, USHORT*, bool*); gpre_nod* SQE_variable(gpre_req*, bool, USHORT*, bool*); #endif // GPRE_SQE_PROTO_H diff --git a/src/gpre/sql.cpp b/src/gpre/sql.cpp index 792be97bb4..6ba32630a6 100644 --- a/src/gpre/sql.cpp +++ b/src/gpre/sql.cpp @@ -49,6 +49,9 @@ const int DEFAULT_BLOB_SEGMENT_LENGTH = 80; // bytes +const char* const OLD_CONTEXT = "OLD"; +const char* const NEW_CONTEXT = "NEW"; + static act* act_alter(); static act* act_alter_database(); static act* act_alter_domain(); @@ -95,6 +98,7 @@ static act* act_set_statistics(); static act* act_set_transaction(); static act* act_transaction(act_t); static act* act_update(); +static act* act_upsert(); static act* act_whenever(); static bool check_filename(const TEXT *); @@ -127,6 +131,7 @@ static cnstrt* par_table_constraint(gpre_req*); static bool par_transaction_modes(gpre_tra*, bool); static bool par_using(dyn*); static USHORT resolve_dtypes(kwwords_t, bool); +static gpre_nod* return_values(gpre_req*, gpre_nod*, gpre_nod*); static bool tail_database(act_t, gpre_dbb*); static void to_upcase(const TEXT*, TEXT*, int); @@ -2689,9 +2694,31 @@ static act* act_delete() if (where) selection->rse_boolean = SQE_boolean(request, 0); - request->req_node = MSC_node(nod_erase, 0); + gpre_nod* ret_list = NULL; + ref* ref_list = NULL; + + if (MSC_match(KW_RETURNING)) + { + gpre_nod* value_list = SQE_list(SQE_value_or_null, request, false); + + if (!MSC_match(KW_INTO)) + CPR_s_error("INTO"); + + gpre_nod* var_list = SQE_list(SQE_variable, request, false); + + ret_list = return_values(request, value_list, var_list); + + into(request, value_list, var_list); + ref_list = (ref*) var_list; + + request->req_flags |= REQ_sql_returning; + selection->rse_flags |= RSE_singleton; + } + + request->req_node = MSC_unary(nod_erase, ret_list); act* action = MSC_action(request, ACT_loop); + action->act_object = ref_list; action->act_whenever = gen_whenever(); if (hsh_rm) @@ -3450,6 +3477,9 @@ static act* act_insert() EXP_match_paren(); } + gpre_nod* ret_list = NULL; + ref* ref_list = NULL; + gpre_lls* values = NULL; if (MSC_match(KW_VALUES)) { @@ -3458,10 +3488,7 @@ static act* act_insert() EXP_left_paren(0); for (;;) { - if (MSC_match(KW_NULL)) - MSC_push(MSC_node(nod_null, 0), &values); - else - MSC_push(SQE_value(request, false, NULL, NULL), &values); + MSC_push(SQE_value_or_null(request, false, NULL, NULL), &values); count2++; if (!MSC_match(KW_COMMA)) break; @@ -3474,7 +3501,6 @@ static act* act_insert() PAR_error("count of values doesn't match count of columns"); gpre_nod* vlist = MSC_node(nod_list, (SSHORT) count); - request->req_node = vlist; gpre_nod** ptr = &vlist->nod_arg[count]; while (values) @@ -3486,9 +3512,30 @@ static act* act_insert() *--ptr = assignment; } + if (MSC_match(KW_RETURNING)) + { + gpre_nod* value_list = SQE_list(SQE_value_or_null, request, false); + + if (!MSC_match(KW_INTO)) + CPR_s_error("INTO"); + + gpre_nod* var_list = SQE_list(SQE_variable, request, false); + + ret_list = return_values(request, value_list, var_list); + + into(request, value_list, var_list); + ref_list = (ref*) var_list; + + request->req_flags |= REQ_sql_returning; + } + + request->req_node = MSC_ternary(nod_store, (gpre_nod*) context, vlist, ret_list); + if (context->ctx_symbol) HSH_remove(context->ctx_symbol); + act* action = MSC_action(request, ACT_insert); + action->act_object = ref_list; action->act_whenever = gen_whenever(); return action; } @@ -3529,13 +3576,33 @@ static act* act_insert() *--ptr = assignment; } - gpre_nod* store = MSC_binary(nod_store, (gpre_nod*) context, alist); + if (MSC_match(KW_RETURNING)) + { + gpre_nod* value_list = SQE_list(SQE_value_or_null, request, false); + + if (!MSC_match(KW_INTO)) + CPR_s_error("INTO"); + + gpre_nod* var_list = SQE_list(SQE_variable, request, false); + + ret_list = return_values(request, value_list, var_list); + + into(request, value_list, var_list); + ref_list = (ref*) var_list; + + request->req_flags |= REQ_sql_returning; + select->rse_flags |= RSE_singleton; + } + + gpre_nod* store = MSC_ternary(nod_store, (gpre_nod*) context, alist, ret_list); request->req_node = store; EXP_rse_cleanup(select); + if (context->ctx_symbol) HSH_remove(context->ctx_symbol); act* action = MSC_action(request, ACT_loop); + action->act_object = ref_list; action->act_whenever = gen_whenever(); return action; @@ -3932,7 +3999,7 @@ static act* act_procedure() gpre_lls* values = NULL; SSHORT inputs = 0; - if (gpreGlob.token_global.tok_keyword != KW_RETURNING && + if (gpreGlob.token_global.tok_keyword != KW_RETURNING_VALUES && gpreGlob.token_global.tok_keyword != KW_SEMI_COLON) { // parse input references @@ -3960,7 +4027,7 @@ static act* act_procedure() } SSHORT outputs = 0; - if (MSC_match(KW_RETURNING)) + if (MSC_match(KW_RETURNING_VALUES)) { // parse output references @@ -4423,6 +4490,9 @@ static act* act_transaction(act_t type) static act* act_update() { + if (MSC_match(KW_OR) && MSC_match(KW_INSERT)) + return act_upsert(); + const TEXT* transaction = NULL; par_options(&transaction); @@ -4463,10 +4533,7 @@ static act* act_update() set_item->nod_arg[1] = SQE_field(NULL, false); if (!MSC_match(KW_EQUALS)) CPR_s_error("assignment operator"); - if (MSC_match(KW_NULL)) - set_item->nod_arg[0] = MSC_node(nod_null, 0); - else - set_item->nod_arg[0] = SQE_value(request, false, NULL, NULL); + set_item->nod_arg[0] = SQE_value_or_null(request, false, NULL, NULL); MSC_push(set_item, &stack); count++; } while (MSC_match(KW_COMMA)); @@ -4542,7 +4609,7 @@ static act* act_update() for (ptr = set_list->nod_arg; ptr < end_list; ptr++) { gpre_nod* set_item = *ptr; - SQE_resolve(set_item->nod_arg[0], request, 0); + SQE_resolve(&set_item->nod_arg[0], request, 0); pair(set_item->nod_arg[0], set_item->nod_arg[1]); } @@ -4563,7 +4630,7 @@ static act* act_update() for (ptr = set_list->nod_arg; ptr < end_list; ptr++) { gpre_nod* set_item = *ptr; - SQE_resolve(set_item->nod_arg[1], request, 0); + SQE_resolve(&set_item->nod_arg[1], request, 0); ref* field_ref = (ref*) ((set_item->nod_arg[1])->nod_arg[0]); slc* slice = NULL; @@ -4635,7 +4702,7 @@ static act* act_update() for (ptr = set_list->nod_arg; ptr < end_list; ptr++) { gpre_nod* set_item = *ptr; - SQE_resolve(set_item->nod_arg[0], request, select); + SQE_resolve(&set_item->nod_arg[0], request, select); } // Process boolean, if any @@ -4655,7 +4722,7 @@ static act* act_update() for (ptr = set_list->nod_arg; ptr < end_list; ptr++) { gpre_nod* set_item = *ptr; - SQE_resolve(set_item->nod_arg[1], request, 0); + SQE_resolve(&set_item->nod_arg[1], request, 0); ref* field_ref = (ref*) ((set_item->nod_arg[1])->nod_arg[0]); act* slice_action = (act*) field_ref->ref_slice; @@ -4687,11 +4754,44 @@ static act* act_update() pair(set_item->nod_arg[0], set_item->nod_arg[1]); } - gpre_nod* modify = MSC_node(nod_modify, 1); + gpre_nod* ret_list = NULL; + ref* ref_list = NULL; + + if (MSC_match(KW_RETURNING)) + { + gpre_nod* value_list = SQE_list(SQE_value_or_null, NULL, false); + + if (!MSC_match(KW_INTO)) + CPR_s_error("INTO"); + + gpre_nod* var_list = SQE_list(SQE_variable, request, false); + + gpre_sym* old_ctx_sym = MSC_symbol(SYM_context, OLD_CONTEXT, strlen(OLD_CONTEXT), input_context); + HSH_insert(old_ctx_sym); + + gpre_sym* new_ctx_sym = MSC_symbol(SYM_context, NEW_CONTEXT, strlen(NEW_CONTEXT), update_context); + HSH_insert(new_ctx_sym); + + ret_list = return_values(request, value_list, var_list); + + for (int i = 0; i < value_list->nod_count; i++) + SQE_resolve(&value_list->nod_arg[i], request, NULL); + + into(request, value_list, var_list); + ref_list = (ref*) var_list; + + HSH_remove(new_ctx_sym); + HSH_remove(old_ctx_sym); + + request->req_flags |= REQ_sql_returning; + select->rse_flags |= RSE_singleton; + } + + gpre_nod* modify = MSC_binary(nod_modify, set_list, ret_list); request->req_node = modify; - modify->nod_arg[0] = set_list; act* action = MSC_action(request, ACT_loop); + action->act_object = ref_list; action->act_whenever = gen_whenever(); if (alias) @@ -4701,6 +4801,286 @@ static act* act_update() } +//____________________________________________________________ +// +// Process SQL UPDATE OR INSERT statement. +// + +static act* act_upsert(void) +{ + const TEXT* transaction = NULL; + + par_options(&transaction); + + if (!MSC_match(KW_INTO)) + CPR_s_error("INTO"); + + gpre_req* request = MSC_request(REQ_mass_update); + request->req_trans = transaction; + gpre_ctx* context = SQE_context(request); + gpre_rel* relation = context->ctx_relation; + + int count = 0; + gpre_lls* fields = NULL; + + // Pick up a field list + + if (!MSC_match(KW_LEFT_PAREN)) + { + gpre_nod* list = MET_fields(context); + count = list->nod_count; + for (int i = 0; i < count; i++) + MSC_push(list->nod_arg[i], &fields); + } + else + { + do + { + gpre_nod* node = SQE_field(request, false); + + if (node->nod_type == nod_array) + { + node->nod_type = nod_field; + + // Make sure no subscripts are specified + + if (node->nod_arg[1]) { + PAR_error("Partial insert of arrays not permitted"); + } + } + + // Dialect 1 program may not insert new datatypes + if ((SQL_DIALECT_V5 == gpreGlob.sw_sql_dialect) && + (nod_field == node->nod_type)) + { + const USHORT field_dtype = + ((ref*) (node->nod_arg[0]))->ref_field->fld_dtype; + + if ((dtype_sql_date == field_dtype) + || (dtype_sql_time == field_dtype) + || (dtype_int64 == field_dtype)) + { + SQL_dialect1_bad_type(field_dtype); + } + } + + MSC_push(node, &fields); + count++; + } while (MSC_match(KW_COMMA)); + + EXP_match_paren(); + } + + gpre_nod* field_list = MSC_node(nod_list, (SSHORT) count); + gpre_nod** ptr = &field_list->nod_arg[count]; + while (fields) + *--ptr = (gpre_nod*) MSC_pop(&fields); + + // Now pick up a value list + + if (!MSC_match(KW_VALUES)) + CPR_s_error("VALUES"); + + EXP_left_paren(0); + gpre_nod* value_list = SQE_list(SQE_value_or_null, request, false); + EXP_match_paren(); + + if (count != value_list->nod_count) + PAR_error("count of values doesn't match count of columns"); + + // Pick up a matching list + + nod_t cmp_op = nod_equiv; + + gpre_lls* matches = NULL; + if (MSC_match(KW_MATCHES)) + { + EXP_left_paren(0); + for (;;) + { + MSC_push(SQE_field(request, false), &matches); + if (!(MSC_match(KW_COMMA))) + break; + } + EXP_match_paren(); + } + else + { + if (relation->rel_view_rse) + PAR_error("MATCHING clause is required for views"); + + gpre_lls* pk_fields = + MET_get_primary_key(relation->rel_database, relation->rel_symbol->sym_string); + + while (pk_fields) + { + const TEXT* field_name = (TEXT*) MSC_pop(&pk_fields); + gpre_fld* field = MET_field(relation, field_name); + ref* reference = (ref*) MSC_alloc(REF_LEN); + reference->ref_field = field; + reference->ref_context = context; + MSC_push(MSC_unary(nod_field, (gpre_nod*) reference), &matches); + } + + cmp_op = nod_eq; + } + + if (!matches) + PAR_error("Either MATCHING list or primary key is required"); + + // Create selection + + gpre_rse* select = (gpre_rse*) MSC_alloc(RSE_LEN(1)); + request->req_rse = select; + select->rse_count = 1; + select->rse_context[0] = context; + + // Introduce the update context + + gpre_ctx* update_context = MSC_context(request); + update_context->ctx_relation = relation; + request->req_update = update_context; + + // Introduce the insertion context + + gpre_ctx* insert_context = MSC_context(request); + insert_context->ctx_relation = relation; + + // Make assignment lists for both update and insert contexts + + gpre_nod* upd_list = MSC_node(nod_list, (SSHORT) count); + gpre_nod* ins_list = MSC_node(nod_list, (SSHORT) count); + + for (int i = 0; i < count; i++) + { + ref* org_ref = (ref*) field_list->nod_arg[i]->nod_arg[0]; + + ref* new_ref = (ref*) MSC_alloc(REF_LEN); + new_ref->ref_field = org_ref->ref_field; + new_ref->ref_context = update_context; + + gpre_nod* assignment = MSC_node(nod_assignment, 2); + assignment->nod_arg[0] = value_list->nod_arg[i]; + assignment->nod_arg[1] = MSC_unary(nod_field, (gpre_nod*) new_ref); + pair(assignment->nod_arg[0], assignment->nod_arg[1]); + + upd_list->nod_arg[i] = assignment; + + new_ref = (ref*) MSC_alloc(REF_LEN); + new_ref->ref_field = org_ref->ref_field; + new_ref->ref_context = insert_context; + + assignment = MSC_node(nod_assignment, 2); + assignment->nod_arg[0] = value_list->nod_arg[i]; + assignment->nod_arg[1] = MSC_unary(nod_field, (gpre_nod*) new_ref); + pair(assignment->nod_arg[0], assignment->nod_arg[1]); + + ins_list->nod_arg[i] = assignment; + } + + // Create boolean + + gpre_nod* boolean = NULL; + while (matches) + { + gpre_nod* match = MSC_pop(&matches); + ref* match_ref = (ref*) match->nod_arg[0]; + gpre_fld* match_field = match_ref->ref_field; + + for (int i = 0; i < count; i++) + { + gpre_nod* from = value_list->nod_arg[i]; + gpre_nod* to = field_list->nod_arg[i]; + ref* to_ref = (ref*) to->nod_arg[0]; + gpre_fld* to_field = to_ref->ref_field; + + if (!strcmp(match_field->fld_symbol->sym_string, + to_field->fld_symbol->sym_string)) + { + gpre_nod* eql = MSC_binary(cmp_op, match, from); + + if (boolean) + boolean = MSC_binary(nod_and, boolean, eql); + else + boolean = eql; + + break; + } + } + } + + if (!boolean) + PAR_error("Invalid MATCHING list"); + + select->rse_boolean = boolean; + + // Process a returning clause + + ref* ref_list = NULL; + gpre_nod* upd_ret_list = NULL; + gpre_nod* ins_ret_list = NULL; + + if (MSC_match(KW_RETURNING)) + { + gpre_nod* value_list = SQE_list(SQE_value_or_null, NULL, false); + + if (!MSC_match(KW_INTO)) + CPR_s_error("INTO"); + + gpre_nod* var_list = SQE_list(SQE_variable, request, false); + + gpre_sym* old_ctx_sym = MSC_symbol(SYM_context, OLD_CONTEXT, strlen(OLD_CONTEXT), context); + HSH_insert(old_ctx_sym); + + // temporarily hide the insertion context + + fb_assert(request->req_contexts == insert_context); + request->req_contexts = request->req_contexts->ctx_next; + + gpre_sym* new_ctx_sym = MSC_symbol(SYM_context, NEW_CONTEXT, strlen(NEW_CONTEXT), update_context); + HSH_insert(new_ctx_sym); + + upd_ret_list = return_values(request, value_list, var_list); + + // restore the insertion context back + + fb_assert(request->req_contexts == update_context); + request->req_contexts = insert_context; + + new_ctx_sym->sym_object = insert_context; + + ins_ret_list = return_values(request, value_list, var_list); + + for (int i = 0; i < value_list->nod_count; i++) + SQE_resolve(&value_list->nod_arg[i], request, NULL); + + into(request, value_list, var_list); + ref_list = (ref*) var_list; + + HSH_remove(new_ctx_sym); + HSH_remove(old_ctx_sym); + + request->req_flags |= REQ_sql_returning; + select->rse_flags |= RSE_singleton; + } + + // Create the final node + + gpre_nod* compound = MSC_node(nod_list, 2); + compound->nod_arg[0] = MSC_binary(nod_modify, upd_list, upd_ret_list); + compound->nod_arg[1] = MSC_ternary(nod_store, (gpre_nod*) insert_context, ins_list, ins_ret_list); + request->req_node = compound; + + if (context->ctx_symbol) + HSH_remove(context->ctx_symbol); + + act* action = MSC_action(request, ACT_loop); + action->act_object = ref_list; + action->act_whenever = gen_whenever(); + return action; +} + + //____________________________________________________________ // // Handle an SQL whenever statement. This is declaratory, @@ -6122,6 +6502,28 @@ static USHORT resolve_dtypes(kwwords_t typ, bool sql_date) } +//____________________________________________________________ +// +// Generate the assignment list for the RETURNING clause +// + +static gpre_nod* return_values(gpre_req* request, gpre_nod* src_list, gpre_nod* dst_list) +{ + gpre_nod* ret_list = MSC_node(nod_list, src_list->nod_count); + + for (int i = 0; i < src_list->nod_count; i++) + { + gpre_nod* assignment = MSC_node(nod_assignment, 2); + assignment->nod_arg[0] = src_list->nod_arg[i]; + SQE_resolve(&assignment->nod_arg[0], request, NULL); + assignment->nod_arg[1] = dst_list->nod_arg[i]; + ret_list->nod_arg[i] = assignment; + } + + return ret_list; +} + + //____________________________________________________________ // // Parse the tail of a CREATE DATABASE statement. diff --git a/src/gpre/words.h b/src/gpre/words.h index ee2c3d2a14..afd856f54c 100644 --- a/src/gpre/words.h +++ b/src/gpre/words.h @@ -89,6 +89,7 @@ enum kwwords_t { KW_RELEASE, KW_RELEASE_REQUESTS, KW_RETURNING, + KW_RETURNING_VALUES, KW_ROLE, KW_ROLLBACK, KW_R_BRACE,