mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 20:03:02 +01:00
Rename REPLACE to UPDATE OR INSERT
This commit is contained in:
parent
c964124a20
commit
5e818bd4a9
@ -1,5 +1,5 @@
|
||||
-----------------
|
||||
REPLACE statement
|
||||
UPDATE OR INSERT statement
|
||||
-----------------
|
||||
|
||||
Function:
|
||||
@ -9,7 +9,7 @@ REPLACE statement
|
||||
Adriano dos Santos Fernandes <adrianosf@uol.com.br>
|
||||
|
||||
Syntax rules:
|
||||
REPLACE INTO <table or view> [(<column_list>)]
|
||||
UPDATE OR INSERT INTO <table or view> [(<column_list>)]
|
||||
VALUES (<value_list>)
|
||||
[MATCHING <column_list>]
|
||||
[RETURNING <column_list> [INTO <variable_list>]]
|
||||
@ -18,9 +18,9 @@ REPLACE statement
|
||||
DSQL, PSQL
|
||||
|
||||
Examples:
|
||||
1. REPLACE INTO T1 (F1, F2) VALUES (:F1, :F2);
|
||||
2. REPLACE INTO EMPLOYEE (ID, NAME) VALUES (:ID, :NAME) RETURNING ID;
|
||||
3. REPLACE INTO T1 (F1, F2) VALUES (:F1, :F2) MATCHING (F1);
|
||||
1. UPDATE OR INSERT INTO T1 (F1, F2) VALUES (:F1, :F2);
|
||||
2. UPDATE OR INSERT INTO EMPLOYEE (ID, NAME) VALUES (:ID, :NAME) RETURNING ID;
|
||||
3. UPDATE OR INSERT INTO T1 (F1, F2) VALUES (:F1, :F2) MATCHING (F1);
|
||||
|
||||
Notes:
|
||||
1. Views are only allowed if it's based on only one table (i.e. no joins or unions).
|
@ -1270,12 +1270,12 @@ C --
|
||||
PARAMETER (GDS__dsql_feature_not_supported_ods = 336003097)
|
||||
INTEGER*4 GDS__primary_key_required
|
||||
PARAMETER (GDS__primary_key_required = 336003098)
|
||||
INTEGER*4 GDS__replace_doesnt_match_pk
|
||||
PARAMETER (GDS__replace_doesnt_match_pk = 336003099)
|
||||
INTEGER*4 GDS__replace_doesnt_match_matching
|
||||
PARAMETER (GDS__replace_doesnt_match_matching = 336003100)
|
||||
INTEGER*4 GDS__replace_with_complex_view
|
||||
PARAMETER (GDS__replace_with_complex_view = 336003101)
|
||||
INTEGER*4 GDS__upd_ins_doesnt_match_pk
|
||||
PARAMETER (GDS__upd_ins_doesnt_match_pk = 336003099)
|
||||
INTEGER*4 GDS__upd_ins_doesnt_match_matching
|
||||
PARAMETER (GDS__upd_ins_doesnt_match_matching = 336003100)
|
||||
INTEGER*4 GDS__upd_ins_with_complex_view
|
||||
PARAMETER (GDS__upd_ins_with_complex_view = 336003101)
|
||||
INTEGER*4 GDS__dsql_incompatible_trigger_type
|
||||
PARAMETER (GDS__dsql_incompatible_trigger_type = 336003102)
|
||||
INTEGER*4 GDS__dsql_db_trigger_type_cant_change
|
||||
|
@ -642,9 +642,9 @@ const
|
||||
gds_dsql_type_not_supp_ext_tab = 336003096;
|
||||
gds_dsql_feature_not_supported_ods = 336003097;
|
||||
gds_primary_key_required = 336003098;
|
||||
gds_replace_doesnt_match_pk = 336003099;
|
||||
gds_replace_doesnt_match_matching = 336003100;
|
||||
gds_replace_with_complex_view = 336003101;
|
||||
gds_upd_ins_doesnt_match_pk = 336003099;
|
||||
gds_upd_ins_doesnt_match_matching = 336003100;
|
||||
gds_upd_ins_with_complex_view = 336003101;
|
||||
gds_dsql_incompatible_trigger_type = 336003102;
|
||||
gds_dsql_db_trigger_type_cant_change = 336003103;
|
||||
gds_dyn_role_does_not_exist = 336068796;
|
||||
|
@ -2834,10 +2834,6 @@ void DSQL_pretty(const dsql_nod* node, int column)
|
||||
verb = "with";
|
||||
break;
|
||||
|
||||
case nod_replace:
|
||||
verb = "replace";
|
||||
break;
|
||||
|
||||
default:
|
||||
sprintf(s, "unknown type %d", node->nod_type);
|
||||
verb = s;
|
||||
|
@ -246,7 +246,6 @@ static const TOK tokens[] =
|
||||
{RECURSIVE, "RECURSIVE", 2, false},
|
||||
{REFERENCES, "REFERENCES", 1, false},
|
||||
{RELEASE, "RELEASE", 2, false},
|
||||
{REPLACE, "REPLACE", 2, false},
|
||||
{REQUESTS, "REQUESTS", 2, true},
|
||||
{RESERVING, "RESERV", 1, false}, // Alias of RESERVING
|
||||
{RESERVING, "RESERVING", 1, false},
|
||||
|
@ -347,7 +347,7 @@ enum nod_t
|
||||
nod_agg_list,
|
||||
nod_src_info,
|
||||
nod_with,
|
||||
nod_replace,
|
||||
nod_update_or_insert,
|
||||
nod_merge,
|
||||
nod_merge_when, // 280
|
||||
nod_merge_update,
|
||||
@ -633,12 +633,12 @@ enum node_args {
|
||||
e_sto_return,
|
||||
e_sto_count,
|
||||
|
||||
e_rep_relation = 0, // nod_replace
|
||||
e_rep_fields,
|
||||
e_rep_values,
|
||||
e_rep_matching,
|
||||
e_rep_return,
|
||||
e_rep_count,
|
||||
e_upi_relation = 0, // nod_update_or_insert
|
||||
e_upi_fields,
|
||||
e_upi_values,
|
||||
e_upi_matching,
|
||||
e_upi_return,
|
||||
e_upi_count,
|
||||
|
||||
e_del_relation = 0, // nod_delete
|
||||
e_del_boolean,
|
||||
|
@ -523,7 +523,6 @@ static LexerState lex;
|
||||
%token PAD
|
||||
%token PRESERVE
|
||||
%token RECURSIVE
|
||||
%token REPLACE
|
||||
%token SENSITIVE
|
||||
%token SPACE
|
||||
%token START
|
||||
@ -578,13 +577,13 @@ statement : alter
|
||||
| exec_procedure
|
||||
| exec_block
|
||||
| recreate
|
||||
| replace
|
||||
| revoke
|
||||
| rollback
|
||||
| savepoint
|
||||
| select
|
||||
| set
|
||||
| update
|
||||
| update_or_insert
|
||||
| KW_DEBUG signed_short_integer
|
||||
{ prepare_console_debug ((IPTR) $2, &yydebug);
|
||||
$$ = make_node (nod_null, (int) 0, NULL); }
|
||||
@ -1626,8 +1625,8 @@ stmt_start_column :
|
||||
simple_proc_statement : assignment
|
||||
| insert
|
||||
| merge
|
||||
| replace
|
||||
| update
|
||||
| update_or_insert
|
||||
| delete
|
||||
| singleton_select
|
||||
| exec_procedure
|
||||
@ -3579,20 +3578,20 @@ update_positioned : UPDATE table_name SET assignments cursor_clause
|
||||
;
|
||||
|
||||
|
||||
/* REPLACE statement */
|
||||
/* UPDATE OR INSERT statement */
|
||||
|
||||
replace
|
||||
: REPLACE INTO simple_table_name ins_column_parens_opt
|
||||
update_or_insert
|
||||
: UPDATE OR INSERT INTO simple_table_name ins_column_parens_opt
|
||||
VALUES '(' value_list ')'
|
||||
replace_matching_opt
|
||||
update_or_insert_matching_opt
|
||||
returning_clause
|
||||
{
|
||||
$$ = make_node (nod_replace, (int) e_rep_count,
|
||||
$3, make_list ($4), make_list ($7), $9, $10);
|
||||
$$ = make_node (nod_update_or_insert, (int) e_upi_count,
|
||||
$5, make_list ($6), make_list ($9), $11, $12);
|
||||
}
|
||||
;
|
||||
|
||||
replace_matching_opt
|
||||
update_or_insert_matching_opt
|
||||
: MATCHING ins_column_parens
|
||||
{ $$ = $2; }
|
||||
|
|
||||
@ -4549,7 +4548,6 @@ non_reserved_word :
|
||||
| MATCHING
|
||||
| PAD
|
||||
| PRESERVE
|
||||
| REPLACE
|
||||
| SPACE
|
||||
| TEMPORARY
|
||||
;
|
||||
|
@ -235,7 +235,6 @@ static dsql_nod* pass1_merge(dsql_req*, dsql_nod*, bool);
|
||||
static dsql_nod* pass1_not(dsql_req*, const dsql_nod*, bool, bool);
|
||||
static void pass1_put_args_on_stack(dsql_req*, dsql_nod*, DsqlNodStack&, bool);
|
||||
static dsql_nod* pass1_relation(dsql_req*, dsql_nod*);
|
||||
static dsql_nod* pass1_replace(dsql_req*, dsql_nod*, bool);
|
||||
static dsql_nod* pass1_returning(dsql_req*, const dsql_nod*, bool);
|
||||
static dsql_nod* pass1_rse(dsql_req*, dsql_nod*, dsql_nod*, dsql_nod*, dsql_nod*, USHORT);
|
||||
static dsql_nod* pass1_rse_impl(dsql_req*, dsql_nod*, dsql_nod*, dsql_nod*, dsql_nod*, USHORT);
|
||||
@ -250,6 +249,7 @@ static dsql_nod* pass1_union(dsql_req*, dsql_nod*, dsql_nod*, dsql_nod*, USHORT)
|
||||
static void pass1_union_auto_cast(dsql_nod*, const dsc&, SSHORT,
|
||||
bool in_select_list = false);
|
||||
static dsql_nod* pass1_update(dsql_req*, dsql_nod*, bool);
|
||||
static dsql_nod* pass1_update_or_insert(dsql_req*, dsql_nod*, bool);
|
||||
static dsql_nod* pass1_variable(dsql_req*, dsql_nod*);
|
||||
static dsql_nod* post_map(dsql_nod*, dsql_ctx*);
|
||||
static dsql_nod* remap_field(dsql_req*, dsql_nod*, dsql_ctx*, USHORT);
|
||||
@ -661,7 +661,7 @@ dsql_nod* PASS1_node(dsql_req* request, dsql_nod* input, bool proc_flag)
|
||||
case nod_delete:
|
||||
case nod_insert:
|
||||
case nod_merge:
|
||||
case nod_replace:
|
||||
case nod_update_or_insert:
|
||||
case nod_order:
|
||||
case nod_select:
|
||||
case nod_with:
|
||||
@ -1840,8 +1840,8 @@ dsql_nod* PASS1_statement(dsql_req* request, dsql_nod* input, bool proc_flag)
|
||||
return input;
|
||||
}
|
||||
|
||||
case nod_replace:
|
||||
node = pass1_savepoint(request, pass1_replace(request, input, proc_flag));
|
||||
case nod_update_or_insert:
|
||||
node = pass1_savepoint(request, pass1_update_or_insert(request, input, proc_flag));
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -6951,249 +6951,6 @@ static dsql_rel* pass1_base_table( dsql_req* request, const dsql_rel* relation,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
||||
pass1_replace
|
||||
|
||||
@brief Process REPLACE statement.
|
||||
|
||||
|
||||
@param request
|
||||
@param input
|
||||
@param proc_flag
|
||||
|
||||
**/
|
||||
static dsql_nod* pass1_replace(dsql_req* request, dsql_nod* input, bool proc_flag)
|
||||
{
|
||||
DEV_BLKCHK(request, dsql_type_req);
|
||||
DEV_BLKCHK(input, dsql_type_nod);
|
||||
|
||||
dsql_str* relation_name =
|
||||
(dsql_str*) input->nod_arg[e_rep_relation]->nod_arg[e_rpn_name];
|
||||
dsql_str* base_name = relation_name;
|
||||
|
||||
dsql_nod* values = input->nod_arg[e_rep_values];
|
||||
|
||||
// build the INSERT node
|
||||
dsql_nod* insert = MAKE_node(nod_insert, e_ins_count);
|
||||
insert->nod_arg[e_ins_relation] = input->nod_arg[e_rep_relation];
|
||||
insert->nod_arg[e_ins_fields] = input->nod_arg[e_rep_fields];
|
||||
insert->nod_arg[e_ins_values] = values;
|
||||
insert->nod_arg[e_ins_return] = input->nod_arg[e_rep_return];
|
||||
insert = pass1_insert(request, insert, proc_flag);
|
||||
|
||||
// PASS1_statement will transform nod_insert to nod_store
|
||||
fb_assert(insert->nod_type == nod_store);
|
||||
|
||||
dsql_ctx* context = (dsql_ctx*) insert->nod_arg[e_sto_relation]->nod_arg[e_rel_context];
|
||||
DEV_BLKCHK(context, dsql_type_ctx);
|
||||
|
||||
dsql_rel* relation = context->ctx_relation;
|
||||
dsql_nod* fields = input->nod_arg[e_rep_fields];
|
||||
|
||||
// if a field list isn't present, build one using the same
|
||||
// rules of INSERT INTO table VALUES ...
|
||||
if (!fields)
|
||||
fields = explode_fields(relation);
|
||||
|
||||
// maintain a pair of view's field name / base field name
|
||||
MetaNamePairMap view_fields;
|
||||
|
||||
if ((relation->rel_flags & REL_view) && !input->nod_arg[e_rep_matching])
|
||||
{
|
||||
dsql_rel* base_rel =
|
||||
METD_get_view_base(request, relation_name->str_data, view_fields);
|
||||
|
||||
// get the base table name if there is only one
|
||||
if (base_rel)
|
||||
base_name = MAKE_cstring(base_rel->rel_name);
|
||||
else
|
||||
ERRD_post(isc_replace_with_complex_view, 0);
|
||||
}
|
||||
|
||||
dsql_nod* matching = input->nod_arg[e_rep_matching];
|
||||
|
||||
if (matching)
|
||||
{
|
||||
request->req_context->push(context);
|
||||
request->req_scope_level++;
|
||||
|
||||
dsql_nod* matching_fields = PASS1_node(request, matching, false);
|
||||
|
||||
request->req_scope_level--;
|
||||
request->req_context->pop();
|
||||
|
||||
field_appears_once(matching_fields, matching, true, "REPLACE");
|
||||
}
|
||||
else
|
||||
{
|
||||
matching = METD_get_primary_key(request, base_name);
|
||||
|
||||
if (!matching)
|
||||
{
|
||||
ERRD_post(isc_primary_key_required,
|
||||
isc_arg_string, base_name->str_data,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
// build a boolean to use in the UPDATE statement
|
||||
dsql_nod* match = NULL;
|
||||
USHORT match_count = 0;
|
||||
|
||||
DsqlNodStack stack;
|
||||
dsql_nod** field_ptr = fields->nod_arg;
|
||||
dsql_nod** value_ptr = values->nod_arg;
|
||||
|
||||
for (const dsql_nod* const* const field_end = field_ptr + fields->nod_count;
|
||||
field_ptr < field_end; field_ptr++, value_ptr++)
|
||||
{
|
||||
DEV_BLKCHK(*field_ptr, dsql_type_nod);
|
||||
DEV_BLKCHK(*value_ptr, dsql_type_nod);
|
||||
|
||||
dsql_nod* temp = MAKE_node(nod_assign, e_asgn_count);
|
||||
temp->nod_arg[e_asgn_value] = *value_ptr;
|
||||
temp->nod_arg[e_asgn_field] = *field_ptr;
|
||||
stack.push(temp);
|
||||
|
||||
temp = *value_ptr;
|
||||
dsql_nod* temp2 = insert->nod_arg[e_sto_statement]->nod_arg[field_ptr - fields->nod_arg]->nod_arg[1];
|
||||
set_parameter_type(request, temp, temp2, false);
|
||||
|
||||
fb_assert((*field_ptr)->nod_type == nod_field_name);
|
||||
|
||||
// When relation is a view and MATCHING was not specified, field_name
|
||||
// stores the base field name that is what we should find in the primary
|
||||
// key of base table.
|
||||
Firebird::MetaName field_name;
|
||||
|
||||
if ((relation->rel_flags & REL_view) && !input->nod_arg[e_rep_matching])
|
||||
{
|
||||
view_fields.get(
|
||||
Firebird::MetaName(((dsql_str*) (*field_ptr)->nod_arg[e_fln_name])->str_data),
|
||||
field_name);
|
||||
}
|
||||
else
|
||||
field_name = ((dsql_str*) (*field_ptr)->nod_arg[e_fln_name])->str_data;
|
||||
|
||||
if (field_name.hasData())
|
||||
{
|
||||
dsql_nod** matching_ptr = matching->nod_arg;
|
||||
|
||||
for (const dsql_nod* const* const matching_end = matching_ptr + matching->nod_count;
|
||||
matching_ptr < matching_end; matching_ptr++)
|
||||
{
|
||||
DEV_BLKCHK(*matching_ptr, dsql_type_nod);
|
||||
fb_assert((*matching_ptr)->nod_type == nod_field_name);
|
||||
|
||||
if (Firebird::MetaName(((dsql_str*)
|
||||
(*matching_ptr)->nod_arg[e_fln_name])->str_data) ==
|
||||
field_name)
|
||||
{
|
||||
++match_count;
|
||||
|
||||
dsql_nod* eql = MAKE_node(nod_eql, 2);
|
||||
eql->nod_arg[0] = *field_ptr;
|
||||
eql->nod_arg[1] = *value_ptr;
|
||||
|
||||
if (match)
|
||||
{
|
||||
// It's a composed MATCHING. Build an AND.
|
||||
dsql_nod* and_node = MAKE_node(nod_and, 2);
|
||||
and_node->nod_arg[0] = match;
|
||||
and_node->nod_arg[1] = eql;
|
||||
match = and_node;
|
||||
}
|
||||
else
|
||||
match = eql;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if implicit or explicit MATCHING is valid
|
||||
if (match_count != matching->nod_count)
|
||||
{
|
||||
if (input->nod_arg[e_rep_matching])
|
||||
ERRD_post(isc_replace_doesnt_match_matching, 0);
|
||||
else
|
||||
{
|
||||
ERRD_post(isc_replace_doesnt_match_pk,
|
||||
isc_arg_string, base_name->str_data,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
// build the UPDATE node
|
||||
dsql_nod* update = MAKE_node(nod_update, e_upd_count);
|
||||
update->nod_arg[e_upd_relation] = input->nod_arg[e_rep_relation];
|
||||
update->nod_arg[e_upd_statement] = MAKE_list(stack);
|
||||
update->nod_arg[e_upd_boolean] = match;
|
||||
|
||||
if (input->nod_arg[e_rep_return])
|
||||
{
|
||||
update->nod_arg[e_upd_rse_flags] = (dsql_nod*) NOD_SELECT_EXPR_SINGLETON;
|
||||
|
||||
dsql_nod* store_ret = insert->nod_arg[e_sto_return];
|
||||
|
||||
// nod_returning was already processed
|
||||
fb_assert(store_ret->nod_type == nod_list);
|
||||
|
||||
// And we create an already processed RETURNING, because
|
||||
// nod_returning creates parameters and they're already
|
||||
// created by the INSERT statement.
|
||||
dsql_nod* update_ret = update->nod_arg[e_upd_return] =
|
||||
MAKE_node(nod_list, store_ret->nod_count);
|
||||
|
||||
dsql_nod** src_ptr = input->nod_arg[e_rep_return]->nod_arg[e_ret_source]->nod_arg;
|
||||
dsql_nod** dst_ptr = store_ret->nod_arg;
|
||||
dsql_nod** ptr = update_ret->nod_arg;
|
||||
|
||||
for (const dsql_nod* const* const end = ptr + update_ret->nod_count;
|
||||
ptr < end; src_ptr++, dst_ptr++, ptr++)
|
||||
{
|
||||
dsql_nod* temp = MAKE_node(nod_assign, e_asgn_count);
|
||||
temp->nod_arg[e_asgn_value] = *src_ptr;
|
||||
temp->nod_arg[e_asgn_field] = (*dst_ptr)->nod_arg[1];
|
||||
*ptr = temp;
|
||||
}
|
||||
}
|
||||
|
||||
update = pass1_update(request, update, proc_flag);
|
||||
|
||||
// PASS1_statement will transform nod_update to nod_modify
|
||||
fb_assert(update->nod_type == nod_modify);
|
||||
|
||||
// test if ROW_COUNT = 0
|
||||
dsql_nod* eql = MAKE_node(nod_eql, 2);
|
||||
eql->nod_arg[0] = MAKE_node(nod_internal_info, e_internal_info_count);
|
||||
eql->nod_arg[0]->nod_arg[e_internal_info] =
|
||||
MAKE_constant((dsql_str*) internal_rows_affected, CONSTANT_SLONG);
|
||||
eql->nod_arg[1] = MAKE_constant((dsql_str*) 0, CONSTANT_SLONG);
|
||||
|
||||
USHORT req_flags = request->req_flags;
|
||||
request->req_flags |= REQ_block; // to compile ROW_COUNT
|
||||
eql = PASS1_node(request, eql, proc_flag);
|
||||
request->req_flags = req_flags;
|
||||
|
||||
// if (ROW_COUNT = 0) then INSERT
|
||||
dsql_nod* if_nod = MAKE_node(nod_if, e_if_count);
|
||||
if_nod->nod_arg[e_if_condition] = eql;
|
||||
if_nod->nod_arg[e_if_true] = insert;
|
||||
|
||||
// build the UPDATE / IF nodes
|
||||
dsql_nod* list = MAKE_node(nod_list, 2);
|
||||
list->nod_arg[0] = update;
|
||||
list->nod_arg[1] = if_nod;
|
||||
|
||||
// if RETURNING is present, req_type is already REQ_EXEC_PROCEDURE
|
||||
if (!input->nod_arg[e_rep_return])
|
||||
request->req_type = REQ_INSERT;
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
||||
pass1_returning
|
||||
@ -8684,6 +8441,249 @@ static dsql_nod* pass1_update( dsql_req* request, dsql_nod* input, bool proc_fla
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
||||
pass1_update_or_insert
|
||||
|
||||
@brief Process UPDATE OR INSERT statement.
|
||||
|
||||
|
||||
@param request
|
||||
@param input
|
||||
@param proc_flag
|
||||
|
||||
**/
|
||||
static dsql_nod* pass1_update_or_insert(dsql_req* request, dsql_nod* input, bool proc_flag)
|
||||
{
|
||||
DEV_BLKCHK(request, dsql_type_req);
|
||||
DEV_BLKCHK(input, dsql_type_nod);
|
||||
|
||||
dsql_str* relation_name =
|
||||
(dsql_str*) input->nod_arg[e_upi_relation]->nod_arg[e_rpn_name];
|
||||
dsql_str* base_name = relation_name;
|
||||
|
||||
dsql_nod* values = input->nod_arg[e_upi_values];
|
||||
|
||||
// build the INSERT node
|
||||
dsql_nod* insert = MAKE_node(nod_insert, e_ins_count);
|
||||
insert->nod_arg[e_ins_relation] = input->nod_arg[e_upi_relation];
|
||||
insert->nod_arg[e_ins_fields] = input->nod_arg[e_upi_fields];
|
||||
insert->nod_arg[e_ins_values] = values;
|
||||
insert->nod_arg[e_ins_return] = input->nod_arg[e_upi_return];
|
||||
insert = pass1_insert(request, insert, proc_flag);
|
||||
|
||||
// PASS1_statement will transform nod_insert to nod_store
|
||||
fb_assert(insert->nod_type == nod_store);
|
||||
|
||||
dsql_ctx* context = (dsql_ctx*) insert->nod_arg[e_sto_relation]->nod_arg[e_rel_context];
|
||||
DEV_BLKCHK(context, dsql_type_ctx);
|
||||
|
||||
dsql_rel* relation = context->ctx_relation;
|
||||
dsql_nod* fields = input->nod_arg[e_upi_fields];
|
||||
|
||||
// if a field list isn't present, build one using the same
|
||||
// rules of INSERT INTO table VALUES ...
|
||||
if (!fields)
|
||||
fields = explode_fields(relation);
|
||||
|
||||
// maintain a pair of view's field name / base field name
|
||||
MetaNamePairMap view_fields;
|
||||
|
||||
if ((relation->rel_flags & REL_view) && !input->nod_arg[e_upi_matching])
|
||||
{
|
||||
dsql_rel* base_rel =
|
||||
METD_get_view_base(request, relation_name->str_data, view_fields);
|
||||
|
||||
// get the base table name if there is only one
|
||||
if (base_rel)
|
||||
base_name = MAKE_cstring(base_rel->rel_name);
|
||||
else
|
||||
ERRD_post(isc_upd_ins_with_complex_view, 0);
|
||||
}
|
||||
|
||||
dsql_nod* matching = input->nod_arg[e_upi_matching];
|
||||
|
||||
if (matching)
|
||||
{
|
||||
request->req_context->push(context);
|
||||
request->req_scope_level++;
|
||||
|
||||
dsql_nod* matching_fields = PASS1_node(request, matching, false);
|
||||
|
||||
request->req_scope_level--;
|
||||
request->req_context->pop();
|
||||
|
||||
field_appears_once(matching_fields, matching, true, "UPDATE OR INSERT");
|
||||
}
|
||||
else
|
||||
{
|
||||
matching = METD_get_primary_key(request, base_name);
|
||||
|
||||
if (!matching)
|
||||
{
|
||||
ERRD_post(isc_primary_key_required,
|
||||
isc_arg_string, base_name->str_data,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
// build a boolean to use in the UPDATE statement
|
||||
dsql_nod* match = NULL;
|
||||
USHORT match_count = 0;
|
||||
|
||||
DsqlNodStack stack;
|
||||
dsql_nod** field_ptr = fields->nod_arg;
|
||||
dsql_nod** value_ptr = values->nod_arg;
|
||||
|
||||
for (const dsql_nod* const* const field_end = field_ptr + fields->nod_count;
|
||||
field_ptr < field_end; field_ptr++, value_ptr++)
|
||||
{
|
||||
DEV_BLKCHK(*field_ptr, dsql_type_nod);
|
||||
DEV_BLKCHK(*value_ptr, dsql_type_nod);
|
||||
|
||||
dsql_nod* temp = MAKE_node(nod_assign, e_asgn_count);
|
||||
temp->nod_arg[e_asgn_value] = *value_ptr;
|
||||
temp->nod_arg[e_asgn_field] = *field_ptr;
|
||||
stack.push(temp);
|
||||
|
||||
temp = *value_ptr;
|
||||
dsql_nod* temp2 = insert->nod_arg[e_sto_statement]->nod_arg[field_ptr - fields->nod_arg]->nod_arg[1];
|
||||
set_parameter_type(request, temp, temp2, false);
|
||||
|
||||
fb_assert((*field_ptr)->nod_type == nod_field_name);
|
||||
|
||||
// When relation is a view and MATCHING was not specified, field_name
|
||||
// stores the base field name that is what we should find in the primary
|
||||
// key of base table.
|
||||
Firebird::MetaName field_name;
|
||||
|
||||
if ((relation->rel_flags & REL_view) && !input->nod_arg[e_upi_matching])
|
||||
{
|
||||
view_fields.get(
|
||||
Firebird::MetaName(((dsql_str*) (*field_ptr)->nod_arg[e_fln_name])->str_data),
|
||||
field_name);
|
||||
}
|
||||
else
|
||||
field_name = ((dsql_str*) (*field_ptr)->nod_arg[e_fln_name])->str_data;
|
||||
|
||||
if (field_name.hasData())
|
||||
{
|
||||
dsql_nod** matching_ptr = matching->nod_arg;
|
||||
|
||||
for (const dsql_nod* const* const matching_end = matching_ptr + matching->nod_count;
|
||||
matching_ptr < matching_end; matching_ptr++)
|
||||
{
|
||||
DEV_BLKCHK(*matching_ptr, dsql_type_nod);
|
||||
fb_assert((*matching_ptr)->nod_type == nod_field_name);
|
||||
|
||||
if (Firebird::MetaName(((dsql_str*)
|
||||
(*matching_ptr)->nod_arg[e_fln_name])->str_data) ==
|
||||
field_name)
|
||||
{
|
||||
++match_count;
|
||||
|
||||
dsql_nod* eql = MAKE_node(nod_eql, 2);
|
||||
eql->nod_arg[0] = *field_ptr;
|
||||
eql->nod_arg[1] = *value_ptr;
|
||||
|
||||
if (match)
|
||||
{
|
||||
// It's a composed MATCHING. Build an AND.
|
||||
dsql_nod* and_node = MAKE_node(nod_and, 2);
|
||||
and_node->nod_arg[0] = match;
|
||||
and_node->nod_arg[1] = eql;
|
||||
match = and_node;
|
||||
}
|
||||
else
|
||||
match = eql;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if implicit or explicit MATCHING is valid
|
||||
if (match_count != matching->nod_count)
|
||||
{
|
||||
if (input->nod_arg[e_upi_matching])
|
||||
ERRD_post(isc_upd_ins_doesnt_match_matching, 0);
|
||||
else
|
||||
{
|
||||
ERRD_post(isc_upd_ins_doesnt_match_pk,
|
||||
isc_arg_string, base_name->str_data,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
// build the UPDATE node
|
||||
dsql_nod* update = MAKE_node(nod_update, e_upd_count);
|
||||
update->nod_arg[e_upd_relation] = input->nod_arg[e_upi_relation];
|
||||
update->nod_arg[e_upd_statement] = MAKE_list(stack);
|
||||
update->nod_arg[e_upd_boolean] = match;
|
||||
|
||||
if (input->nod_arg[e_upi_return])
|
||||
{
|
||||
update->nod_arg[e_upd_rse_flags] = (dsql_nod*) NOD_SELECT_EXPR_SINGLETON;
|
||||
|
||||
dsql_nod* store_ret = insert->nod_arg[e_sto_return];
|
||||
|
||||
// nod_returning was already processed
|
||||
fb_assert(store_ret->nod_type == nod_list);
|
||||
|
||||
// And we create an already processed RETURNING, because
|
||||
// nod_returning creates parameters and they're already
|
||||
// created by the INSERT statement.
|
||||
dsql_nod* update_ret = update->nod_arg[e_upd_return] =
|
||||
MAKE_node(nod_list, store_ret->nod_count);
|
||||
|
||||
dsql_nod** src_ptr = input->nod_arg[e_upi_return]->nod_arg[e_ret_source]->nod_arg;
|
||||
dsql_nod** dst_ptr = store_ret->nod_arg;
|
||||
dsql_nod** ptr = update_ret->nod_arg;
|
||||
|
||||
for (const dsql_nod* const* const end = ptr + update_ret->nod_count;
|
||||
ptr < end; src_ptr++, dst_ptr++, ptr++)
|
||||
{
|
||||
dsql_nod* temp = MAKE_node(nod_assign, e_asgn_count);
|
||||
temp->nod_arg[e_asgn_value] = *src_ptr;
|
||||
temp->nod_arg[e_asgn_field] = (*dst_ptr)->nod_arg[1];
|
||||
*ptr = temp;
|
||||
}
|
||||
}
|
||||
|
||||
update = pass1_update(request, update, proc_flag);
|
||||
|
||||
// PASS1_statement will transform nod_update to nod_modify
|
||||
fb_assert(update->nod_type == nod_modify);
|
||||
|
||||
// test if ROW_COUNT = 0
|
||||
dsql_nod* eql = MAKE_node(nod_eql, 2);
|
||||
eql->nod_arg[0] = MAKE_node(nod_internal_info, e_internal_info_count);
|
||||
eql->nod_arg[0]->nod_arg[e_internal_info] =
|
||||
MAKE_constant((dsql_str*) internal_rows_affected, CONSTANT_SLONG);
|
||||
eql->nod_arg[1] = MAKE_constant((dsql_str*) 0, CONSTANT_SLONG);
|
||||
|
||||
USHORT req_flags = request->req_flags;
|
||||
request->req_flags |= REQ_block; // to compile ROW_COUNT
|
||||
eql = PASS1_node(request, eql, proc_flag);
|
||||
request->req_flags = req_flags;
|
||||
|
||||
// if (ROW_COUNT = 0) then INSERT
|
||||
dsql_nod* if_nod = MAKE_node(nod_if, e_if_count);
|
||||
if_nod->nod_arg[e_if_condition] = eql;
|
||||
if_nod->nod_arg[e_if_true] = insert;
|
||||
|
||||
// build the UPDATE / IF nodes
|
||||
dsql_nod* list = MAKE_node(nod_list, 2);
|
||||
list->nod_arg[0] = update;
|
||||
list->nod_arg[1] = if_nod;
|
||||
|
||||
// if RETURNING is present, req_type is already REQ_EXEC_PROCEDURE
|
||||
if (!input->nod_arg[e_upi_return])
|
||||
request->req_type = REQ_INSERT;
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
resolve_variable_name
|
||||
|
||||
|
@ -631,9 +631,9 @@ static const struct {
|
||||
{"dsql_type_not_supp_ext_tab", 336003096},
|
||||
{"dsql_feature_not_supported_ods", 336003097},
|
||||
{"primary_key_required", 336003098},
|
||||
{"replace_doesnt_match_pk", 336003099},
|
||||
{"replace_doesnt_match_matching", 336003100},
|
||||
{"replace_with_complex_view", 336003101},
|
||||
{"upd_ins_doesnt_match_pk", 336003099},
|
||||
{"upd_ins_doesnt_match_matching", 336003100},
|
||||
{"upd_ins_with_complex_view", 336003101},
|
||||
{"dsql_incompatible_trigger_type", 336003102},
|
||||
{"dsql_db_trigger_type_cant_change", 336003103},
|
||||
{"dyn_role_does_not_exist", 336068796},
|
||||
|
@ -664,9 +664,9 @@ const ISC_LONG isc_dsql_cursor_not_open = 336003095L;
|
||||
const ISC_LONG isc_dsql_type_not_supp_ext_tab = 336003096L;
|
||||
const ISC_LONG isc_dsql_feature_not_supported_ods = 336003097L;
|
||||
const ISC_LONG isc_primary_key_required = 336003098L;
|
||||
const ISC_LONG isc_replace_doesnt_match_pk = 336003099L;
|
||||
const ISC_LONG isc_replace_doesnt_match_matching = 336003100L;
|
||||
const ISC_LONG isc_replace_with_complex_view = 336003101L;
|
||||
const ISC_LONG isc_upd_ins_doesnt_match_pk = 336003099L;
|
||||
const ISC_LONG isc_upd_ins_doesnt_match_matching = 336003100L;
|
||||
const ISC_LONG isc_upd_ins_with_complex_view = 336003101L;
|
||||
const ISC_LONG isc_dsql_incompatible_trigger_type = 336003102L;
|
||||
const ISC_LONG isc_dsql_db_trigger_type_cant_change = 336003103L;
|
||||
const ISC_LONG isc_dyn_role_does_not_exist = 336068796L;
|
||||
@ -1494,9 +1494,9 @@ const ISC_LONG isc_err_max = 805;
|
||||
#define isc_dsql_type_not_supp_ext_tab 336003096L
|
||||
#define isc_dsql_feature_not_supported_ods 336003097L
|
||||
#define isc_primary_key_required 336003098L
|
||||
#define isc_replace_doesnt_match_pk 336003099L
|
||||
#define isc_replace_doesnt_match_matching 336003100L
|
||||
#define isc_replace_with_complex_view 336003101L
|
||||
#define isc_upd_ins_doesnt_match_pk 336003099L
|
||||
#define isc_upd_ins_doesnt_match_matching 336003100L
|
||||
#define isc_upd_ins_with_complex_view 336003101L
|
||||
#define isc_dsql_incompatible_trigger_type 336003102L
|
||||
#define isc_dsql_db_trigger_type_cant_change 336003103L
|
||||
#define isc_dyn_role_does_not_exist 336068796L
|
||||
|
@ -631,9 +631,9 @@ static const struct {
|
||||
{336003096, "Data type %s is not supported for EXTERNAL TABLES. Relation '%s', field '%s'"}, /* 607, dsql_type_not_supp_ext_tab */
|
||||
{336003097, "Feature not supported on ODS version older than %d.%d"}, /* 608, dsql_feature_not_supported_ods */
|
||||
{336003098, "Primary key required on table %s"}, /* 609, primary_key_required */
|
||||
{336003099, "REPLACE field list does not match primary key of table %s"}, /* 610, replace_doesnt_match_pk */
|
||||
{336003100, "REPLACE field list does not match MATCHING clause"}, /* 611, replace_doesnt_match_matching */
|
||||
{336003101, "REPLACE without MATCHING could not be used with views based on more than one table"}, /* 612, replace_with_complex_view */
|
||||
{336003099, "REPLACE field list does not match primary key of table %s"}, /* 610, upd_ins_doesnt_match_pk */
|
||||
{336003100, "REPLACE field list does not match MATCHING clause"}, /* 611, upd_ins_doesnt_match_matching */
|
||||
{336003101, "REPLACE without MATCHING could not be used with views based on more than one table"}, /* 612, upd_ins_with_complex_view */
|
||||
{336003102, "Incompatible trigger type"}, /* 613, dsql_incompatible_trigger_type */
|
||||
{336003103, "Database trigger type can't be changed"}, /* 614, dsql_db_trigger_type_cant_change */
|
||||
{336068796, "SQL role %s does not exist"}, /* 615, dyn_role_does_not_exist */
|
||||
|
@ -630,9 +630,9 @@ static const struct {
|
||||
{336003096, -607}, /* 24 dsql_type_not_supp_ext_tab */
|
||||
{336003097, -804}, /* 25 dsql_feature_not_supported_ods */
|
||||
{336003098, -660}, /* 26 primary_key_required */
|
||||
{336003099, -313}, /* 27 replace_doesnt_match_pk */
|
||||
{336003100, -313}, /* 28 replace_doesnt_match_matching */
|
||||
{336003101, -817}, /* 29 replace_with_complex_view */
|
||||
{336003099, -313}, /* 27 upd_ins_doesnt_match_pk */
|
||||
{336003100, -313}, /* 28 upd_ins_doesnt_match_matching */
|
||||
{336003101, -817}, /* 29 upd_ins_with_complex_view */
|
||||
{336003102, -817}, /* 30 dsql_incompatible_trigger_type */
|
||||
{336003103, -817}, /* 31 dsql_db_trigger_type_cant_change */
|
||||
{336068796, -901}, /* 188 dyn_role_does_not_exist */
|
||||
|
@ -3064,9 +3064,9 @@ INSERT INTO MESSAGES (SYMBOL, ROUTINE, MODULE, TRANS_NOTES, FAC_CODE, NUMBER, FL
|
||||
INSERT INTO MESSAGES (SYMBOL, ROUTINE, MODULE, TRANS_NOTES, FAC_CODE, NUMBER, FLAGS, TEXT, "ACTION", EXPLANATION) VALUES ('dsql_implicit_domain_name', 'define_domain', 'ddl.cpp', NULL, 13, 925, NULL, 'Implicit domain name %s not allowed in user created domain', NULL, NULL);
|
||||
INSERT INTO MESSAGES (SYMBOL, ROUTINE, MODULE, TRANS_NOTES, FAC_CODE, NUMBER, FLAGS, TEXT, "ACTION", EXPLANATION) VALUES ('MSG_COLLATIONS', 'SHOW_metadata', 'show.epp', NULL, 17, 150, NULL, 'Collations:', NULL, NULL);
|
||||
INSERT INTO MESSAGES (SYMBOL, ROUTINE, MODULE, TRANS_NOTES, FAC_CODE, NUMBER, FLAGS, TEXT, "ACTION", EXPLANATION) VALUES ('primary_key_required', 'pass1_replace', 'pass1.cpp', NULL, 7, 26, NULL, 'Primary key required on table %s', NULL, NULL);
|
||||
INSERT INTO MESSAGES (SYMBOL, ROUTINE, MODULE, TRANS_NOTES, FAC_CODE, NUMBER, FLAGS, TEXT, "ACTION", EXPLANATION) VALUES ('replace_doesnt_match_pk', 'pass1_replace', 'pass1.cpp', NULL, 7, 27, NULL, 'REPLACE field list does not match primary key of table %s', NULL, NULL);
|
||||
INSERT INTO MESSAGES (SYMBOL, ROUTINE, MODULE, TRANS_NOTES, FAC_CODE, NUMBER, FLAGS, TEXT, "ACTION", EXPLANATION) VALUES ('replace_doesnt_match_matching', 'pass1_replace', 'pass1.cpp', NULL, 7, 28, NULL, 'REPLACE field list does not match MATCHING clause', NULL, NULL);
|
||||
INSERT INTO MESSAGES (SYMBOL, ROUTINE, MODULE, TRANS_NOTES, FAC_CODE, NUMBER, FLAGS, TEXT, "ACTION", EXPLANATION) VALUES ('replace_with_complex_view', 'pass1_replace', 'pass1.cpp', NULL, 7, 29, NULL, 'REPLACE without MATCHING could not be used with views based on more than one table', NULL, NULL);
|
||||
INSERT INTO MESSAGES (SYMBOL, ROUTINE, MODULE, TRANS_NOTES, FAC_CODE, NUMBER, FLAGS, TEXT, "ACTION", EXPLANATION) VALUES ('upd_ins_doesnt_match_pk', 'pass1_replace', 'pass1.cpp', NULL, 7, 27, NULL, 'REPLACE field list does not match primary key of table %s', NULL, NULL);
|
||||
INSERT INTO MESSAGES (SYMBOL, ROUTINE, MODULE, TRANS_NOTES, FAC_CODE, NUMBER, FLAGS, TEXT, "ACTION", EXPLANATION) VALUES ('upd_ins_doesnt_match_matching', 'pass1_replace', 'pass1.cpp', NULL, 7, 28, NULL, 'REPLACE field list does not match MATCHING clause', NULL, NULL);
|
||||
INSERT INTO MESSAGES (SYMBOL, ROUTINE, MODULE, TRANS_NOTES, FAC_CODE, NUMBER, FLAGS, TEXT, "ACTION", EXPLANATION) VALUES ('upd_ins_with_complex_view', 'pass1_replace', 'pass1.cpp', NULL, 7, 29, NULL, 'REPLACE without MATCHING could not be used with views based on more than one table', NULL, NULL);
|
||||
INSERT INTO MESSAGES (SYMBOL, ROUTINE, MODULE, TRANS_NOTES, FAC_CODE, NUMBER, FLAGS, TEXT, "ACTION", EXPLANATION) VALUES (NULL, 'DYN_define_index', 'dyn_def.epp', NULL, 8, 240, NULL, 'Field %s cannot be used twice in index %s', NULL, NULL);
|
||||
INSERT INTO MESSAGES (SYMBOL, ROUTINE, MODULE, TRANS_NOTES, FAC_CODE, NUMBER, FLAGS, TEXT, "ACTION", EXPLANATION) VALUES ('NO_SECCLASS', 'SHOW_metadata', 'show.epp', NULL, 17, 151, NULL, 'There are no security classes for %s', NULL, NULL);
|
||||
INSERT INTO MESSAGES (SYMBOL, ROUTINE, MODULE, TRANS_NOTES, FAC_CODE, NUMBER, FLAGS, TEXT, "ACTION", EXPLANATION) VALUES ('NO_DB_WIDE_SECCLASS', 'SHOW_metadata', 'show.epp', NULL, 17, 152, NULL, 'There is no database-wide security class', NULL, NULL);
|
||||
|
@ -774,9 +774,9 @@ INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, NUMBER, FAC_CODE,
|
||||
INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, NUMBER, FAC_CODE, VMS_CODE, GDS_SYMBOL, SEVERITY, SEVERITY_TEXT) VALUES (-804, NULL, NULL, 25, 7, NULL, 'dsql_feature_not_supported_ods', NULL, NULL);
|
||||
INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, NUMBER, FAC_CODE, VMS_CODE, GDS_SYMBOL, SEVERITY, SEVERITY_TEXT) VALUES (-637, NULL, NULL, 925, 13, NULL, 'dsql_implicit_domain_name', NULL, NULL);
|
||||
INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, NUMBER, FAC_CODE, VMS_CODE, GDS_SYMBOL, SEVERITY, SEVERITY_TEXT) VALUES (-660, NULL, NULL, 26, 7, NULL, 'primary_key_required', NULL, NULL);
|
||||
INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, NUMBER, FAC_CODE, VMS_CODE, GDS_SYMBOL, SEVERITY, SEVERITY_TEXT) VALUES (-313, NULL, NULL, 27, 7, NULL, 'replace_doesnt_match_pk', NULL, NULL);
|
||||
INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, NUMBER, FAC_CODE, VMS_CODE, GDS_SYMBOL, SEVERITY, SEVERITY_TEXT) VALUES (-313, NULL, NULL, 28, 7, NULL, 'replace_doesnt_match_matching', NULL, NULL);
|
||||
INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, NUMBER, FAC_CODE, VMS_CODE, GDS_SYMBOL, SEVERITY, SEVERITY_TEXT) VALUES (-817, NULL, NULL, 29, 7, NULL, 'replace_with_complex_view', NULL, NULL);
|
||||
INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, NUMBER, FAC_CODE, VMS_CODE, GDS_SYMBOL, SEVERITY, SEVERITY_TEXT) VALUES (-313, NULL, NULL, 27, 7, NULL, 'upd_ins_doesnt_match_pk', NULL, NULL);
|
||||
INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, NUMBER, FAC_CODE, VMS_CODE, GDS_SYMBOL, SEVERITY, SEVERITY_TEXT) VALUES (-313, NULL, NULL, 28, 7, NULL, 'upd_ins_doesnt_match_matching', NULL, NULL);
|
||||
INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, NUMBER, FAC_CODE, VMS_CODE, GDS_SYMBOL, SEVERITY, SEVERITY_TEXT) VALUES (-817, NULL, NULL, 29, 7, NULL, 'upd_ins_with_complex_view', NULL, NULL);
|
||||
INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, NUMBER, FAC_CODE, VMS_CODE, GDS_SYMBOL, SEVERITY, SEVERITY_TEXT) VALUES (-171, NULL, NULL, 553, 0, NULL, 'array_max_dimensions', NULL, NULL);
|
||||
INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, NUMBER, FAC_CODE, VMS_CODE, GDS_SYMBOL, SEVERITY, SEVERITY_TEXT) VALUES (-607, NULL, NULL, 926, 13, NULL, 'dsql_only_can_subscript_array', NULL, NULL);
|
||||
INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, NUMBER, FAC_CODE, VMS_CODE, GDS_SYMBOL, SEVERITY, SEVERITY_TEXT) VALUES (-104, NULL, NULL, 927, 13, NULL, 'dsql_max_sort_items', NULL, NULL);
|
||||
|
Loading…
Reference in New Issue
Block a user