2
* PROGRAM: JRD Access Method
4
* DESCRIPTION: Statement execution
6
* The contents of this file are subject to the Interbase Public
7
* License Version 1.0 (the "License"); you may not use this file
8
* except in compliance with the License. You may obtain a copy
9
* of the License at http://www.Inprise.com/IPL.html
11
* Software distributed under the License is distributed on an
12
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
13
* or implied. See the License for the specific language governing
14
* rights and limitations under the License.
16
* The Original Code was created by Inprise Corporation
17
* and its predecessors. Portions created by Inprise Corporation are
18
* Copyright (C) Inprise Corporation.
20
* All Rights Reserved.
21
* Contributor(s): ______________________________________.
23
* 2001.6.21 Claudio Valderrama: Allow inserting strings into blob fields.
24
* 2001.6.28 Claudio Valderrama: Move code to cleanup_rpb() as directed
25
* by Ann Harrison and cleanup of new record in store() routine.
26
* 2001.10.11 Claudio Valderrama: Fix SF Bug #436462: From now, we only
27
* count real store, modify and delete operations either in an external
28
* file or in a table. Counting on a view caused up to three operations
29
* being reported instead of one.
30
* 2001.12.03 Claudio Valderrama: new visit to the same issue: views need
31
* to count virtual operations, not real I/O on the underlying tables.
32
* 2002.09.28 Dmitry Yemanov: Reworked internal_info stuff, enhanced
33
* exception handling in SPs/triggers,
34
* implemented ROWS_AFFECTED system variable
36
* 2002.10.21 Nickolay Samofatov: Added support for explicit pessimistic locks
37
* 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "MPEXL" port
38
* 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "DecOSF" port
39
* 2002.10.29 Nickolay Samofatov: Added support for savepoints
40
* 2002.10.30 Sean Leyne - Removed support for obsolete "PC_PLATFORM" define
41
* 2003.10.05 Dmitry Yemanov: Added support for explicit cursors in PSQL
42
* Adriano dos Santos Fernandes
47
#if TIME_WITH_SYS_TIME
48
# include <sys/time.h>
52
# include <sys/time.h>
58
#include "../jrd/common.h"
59
#include "../jrd/ibsetjmp.h"
61
#include "../jrd/jrd.h"
62
#include "../jrd/req.h"
63
#include "../jrd/val.h"
64
#include "../jrd/exe.h"
65
#include "../jrd/tra.h"
66
#include "gen/iberror.h"
67
#include "../jrd/ods.h"
68
#include "../jrd/btr.h"
69
#include "../jrd/rse.h"
70
#include "../jrd/lck.h"
71
#include "../jrd/intl.h"
72
#include "../jrd/sbm.h"
73
#include "../jrd/blb.h"
74
#include "../jrd/blr.h"
75
#include "../jrd/blb_proto.h"
76
#include "../jrd/btr_proto.h"
77
#include "../jrd/cmp_proto.h"
78
#include "../jrd/dfw_proto.h"
79
#include "../jrd/dpm_proto.h"
80
#include "../jrd/err_proto.h"
81
#include "../jrd/evl_proto.h"
82
#include "../jrd/exe_proto.h"
83
#include "../jrd/ext_proto.h"
84
#include "../jrd/gds_proto.h"
85
#include "../jrd/idx_proto.h"
86
#include "../jrd/intl_proto.h"
87
#include "../jrd/jrd_proto.h"
89
#include "../jrd/lck_proto.h"
90
#include "../jrd/met_proto.h"
91
#include "../jrd/mov_proto.h"
92
#include "../jrd/opt_proto.h"
93
#include "../jrd/par_proto.h"
94
#include "../jrd/rlck_proto.h"
96
#include "../jrd/rse_proto.h"
97
#include "../jrd/thd.h"
98
#include "../jrd/tra_proto.h"
99
#include "../jrd/vio_proto.h"
100
#include "../jrd/isc_s_proto.h"
102
#include "../jrd/execute_statement.h"
103
#include "../dsql/dsql_proto.h"
104
#include "../jrd/rpb_chain.h"
105
#include "../jrd/VirtualTable.h"
110
// AffectedRows class implementation
112
AffectedRows::AffectedRows()
117
void AffectedRows::clear()
120
fetchedRows = modifiedRows = 0;
123
void AffectedRows::bumpFetched()
128
void AffectedRows::bumpModified(bool increment)
138
bool AffectedRows::isReadOnly() const
143
bool AffectedRows::hasCursor() const
145
return (fetchedRows > 0);
148
int AffectedRows::getCount() const
150
return writeFlag ? modifiedRows : fetchedRows;
153
// StatusXcp class implementation
155
StatusXcp::StatusXcp()
160
void StatusXcp::clear()
162
status[0] = isc_arg_gds;
163
status[1] = FB_SUCCESS;
164
status[2] = isc_arg_end;
167
void StatusXcp::init(const ISC_STATUS* vector)
169
memcpy(status, vector, sizeof(ISC_STATUS_ARRAY));
172
void StatusXcp::copyTo(ISC_STATUS* vector) const
174
memcpy(vector, status, sizeof(ISC_STATUS_ARRAY));
177
bool StatusXcp::success() const
179
return (status[1] == FB_SUCCESS);
182
SLONG StatusXcp::as_gdscode() const
187
SLONG StatusXcp::as_sqlcode() const
189
return gds__sqlcode(status);
192
static void cleanup_rpb(thread_db*, record_param*);
193
static jrd_nod* erase(thread_db*, jrd_nod*, SSHORT);
194
static void execute_looper(thread_db*, jrd_req*, jrd_tra*, enum jrd_req::req_s);
195
static void exec_sql(thread_db*, jrd_req*, DSC *);
196
static void execute_procedure(thread_db*, jrd_nod*);
197
static jrd_req* execute_triggers(thread_db*, trig_vec**, Record*, Record*,
198
enum jrd_req::req_ta);
199
static jrd_nod* looper(thread_db*, jrd_req*, jrd_nod*);
200
static jrd_nod* modify(thread_db*, jrd_nod*, SSHORT);
201
static jrd_nod* receive_msg(thread_db*, jrd_nod*);
202
static void release_blobs(thread_db*, jrd_req*);
203
static void release_proc_save_points(jrd_req*);
204
#ifdef SCROLLABLE_CURSORS
205
static jrd_nod* seek_rse(thread_db*, jrd_req*, jrd_nod*);
206
static void seek_rsb(thread_db*, jrd_req*, RecordSource*, USHORT, SLONG);
208
static jrd_nod* selct(thread_db*, jrd_nod*);
209
static jrd_nod* send_msg(thread_db*, jrd_nod*);
210
static void set_error(thread_db*, const xcp_repeat*, jrd_nod*);
211
static jrd_nod* stall(thread_db*, jrd_nod*);
212
static jrd_nod* store(thread_db*, jrd_nod*, SSHORT);
213
static bool test_and_fixup_error(thread_db*, const PsqlException*, jrd_req*);
214
static void trigger_failure(thread_db*, jrd_req*);
215
static void validate(thread_db*, jrd_nod*);
216
inline void verb_cleanup(thread_db*, jrd_tra*);
217
inline void PreModifyEraseTriggers(thread_db*, trig_vec**, SSHORT, record_param*,
218
Record*, jrd_req::req_ta);
219
static void stuff_stack_trace(const jrd_req*);
222
/* macro definitions */
224
#if (defined SUPERSERVER) && (defined WIN_NT || defined SOLARIS_MT)
225
const int MAX_CLONES = 750;
226
#elif defined (HP10) && defined (SUPERSERVER)
227
const int MAX_CLONES = 110;
229
const int MAX_CLONES = 1000;
232
const int ALL_TRIGS = 0;
233
const int PRE_TRIG = 1;
234
const int POST_TRIG = 2;
236
const size_t MAX_STACK_TRACE = 2048;
238
/* this constant defines how many records are locked
239
before we check whether record locking has been
240
turned off for a given relation; if we set the
241
constant to a low number, we will do too much
242
locking in the case where record locking is always
243
turned on; too high and we will do too much record
244
locking in the case where someone is only occasionally
247
const int RECORD_LOCK_CHECK_INTERVAL = 10;
250
void EXE_assignment(thread_db* tdbb, jrd_nod* node)
252
/**************************************
254
* E X E _ a s s i g n m e n t
256
**************************************
258
* Functional description
259
* Perform an assignment
261
**************************************/
262
DEV_BLKCHK(node, type_nod);
265
jrd_req* request = tdbb->getRequest();
266
BLKCHK(node, type_nod);
268
// Get descriptors of src field/parameter/variable, etc.
269
request->req_flags &= ~req_null;
270
dsc* from_desc = EVL_expr(tdbb, node->nod_arg[e_asgn_from]);
272
EXE_assignment(tdbb, node->nod_arg[e_asgn_to], from_desc, (request->req_flags & req_null),
273
node->nod_arg[e_asgn_missing], node->nod_arg[e_asgn_missing2]);
275
request->req_operation = jrd_req::req_return;
279
void EXE_assignment(thread_db* tdbb, jrd_nod* to, dsc* from_desc, bool from_null,
280
jrd_nod* missing_node, jrd_nod* missing2_node)
282
/**************************************
284
* E X E _ a s s i g n m e n t
286
**************************************
288
* Functional description
289
* Perform an assignment
291
**************************************/
292
DEV_BLKCHK(node, type_nod);
295
jrd_req* request = tdbb->getRequest();
297
// Get descriptors of receiving and sending fields/parameters, variables, etc.
301
missing = EVL_expr(tdbb, missing_node);
304
// Get descriptor of target field/parameter/variable, etc.
305
DSC* to_desc = EVL_assign_to(tdbb, to);
307
request->req_flags &= ~req_null;
309
// NS: If we are assigning to NULL, we finished.
310
// This functionality is currently used to allow calling UDF routines
311
// without assigning resulting value anywhere.
315
SSHORT null = from_null ? -1 : 0;
317
if (!null && missing && MOV_compare(missing, from_desc) == 0) {
321
USHORT* impure_flags = NULL;
323
switch (to->nod_type)
326
if (to->nod_arg[e_var_info])
329
Item(nod_variable, (IPTR) to->nod_arg[e_var_id]),
330
reinterpret_cast<const ItemInfo*>(to->nod_arg[e_var_info]),
331
from_desc, null == -1);
333
impure_flags = &((impure_value*) ((SCHAR *) request +
334
to->nod_arg[e_var_variable]->nod_impure))->vlu_flags;
338
if (to->nod_arg[e_arg_info])
341
Item(nod_argument, (IPTR) to->nod_arg[e_arg_message]->nod_arg[e_msg_number],
342
(IPTR) to->nod_arg[e_arg_number]),
343
reinterpret_cast<const ItemInfo*>(to->nod_arg[e_arg_info]),
344
from_desc, null == -1);
346
impure_flags = (USHORT*) ((UCHAR *) request +
347
(IPTR) to->nod_arg[e_arg_message]->nod_arg[e_msg_impure_flags] +
348
(sizeof(USHORT) * (IPTR) to->nod_arg[e_arg_number]));
352
if (impure_flags != NULL)
353
*impure_flags |= VLU_checked;
355
// If the value is non-missing, move/convert it. Otherwise fill the
356
// field with appropriate nulls.
361
// if necessary and appropriate, use the indicator variable
363
if (to->nod_type == nod_argument && to->nod_arg[e_arg_indicator])
365
dsc* indicator = EVL_assign_to(tdbb, to->nod_arg[e_arg_indicator]);
366
temp.dsc_dtype = dtype_short;
367
temp.dsc_length = sizeof(SSHORT);
369
temp.dsc_sub_type = 0;
373
if ((from_desc->dsc_dtype <= dtype_varying) &&
374
(to_desc->dsc_dtype <= dtype_varying) &&
375
(TEXT_LEN(from_desc) > TEXT_LEN(to_desc)))
377
len = TEXT_LEN(from_desc);
383
temp.dsc_address = (UCHAR *) &len;
384
MOV_move(tdbb, &temp, indicator);
388
temp.dsc_length = TEXT_LEN(to_desc);
389
if (temp.dsc_dtype == dtype_cstring) {
390
temp.dsc_length += 1;
392
else if (temp.dsc_dtype == dtype_varying) {
393
temp.dsc_length += 2;
399
// Validate range for datetime values
401
if (DTYPE_IS_DATE(from_desc->dsc_dtype)) {
402
Firebird::TimeStamp ts(true);
403
switch (from_desc->dsc_dtype) {
405
ts.value().timestamp_date =
406
*(GDS_DATE*) from_desc->dsc_address;
409
ts.value().timestamp_time =
410
*(GDS_TIME*) from_desc->dsc_address;
412
case dtype_timestamp:
413
ts.value() = *(GDS_TIMESTAMP*) from_desc->dsc_address;
419
if (!ts.isRangeValid()) {
420
ERR_post(isc_date_range_exceeded, 0);
424
if (DTYPE_IS_BLOB_OR_QUAD(from_desc->dsc_dtype) ||
425
DTYPE_IS_BLOB_OR_QUAD(to_desc->dsc_dtype))
427
// ASF: Don't let MOV_move call BLB_move because MOV
428
// will not pass the destination field to BLB_move.
429
BLB_move(tdbb, from_desc, to_desc, to);
431
else if (!DSC_EQUIV(from_desc, to_desc, false))
432
MOV_move(tdbb, from_desc, to_desc);
433
else if (from_desc->dsc_dtype == dtype_short)
435
*((SSHORT *) to_desc->dsc_address) =
436
*((SSHORT *) from_desc->dsc_address);
438
else if (from_desc->dsc_dtype == dtype_long)
440
*((SLONG *) to_desc->dsc_address) =
441
*((SLONG *) from_desc->dsc_address);
443
else if (from_desc->dsc_dtype == dtype_int64)
445
*((SINT64 *) to_desc->dsc_address) =
446
*((SINT64 *) from_desc->dsc_address);
448
else if (((U_IPTR) from_desc->dsc_address & (ALIGNMENT - 1)) ||
449
((U_IPTR) to_desc->dsc_address & (ALIGNMENT - 1)))
451
MOVE_FAST(from_desc->dsc_address, to_desc->dsc_address,
452
from_desc->dsc_length);
456
MOVE_FASTER(from_desc->dsc_address, to_desc->dsc_address,
457
from_desc->dsc_length);
459
to_desc->dsc_flags &= ~DSC_null;
461
else if (missing2_node && (missing = EVL_expr(tdbb, missing2_node)))
463
MOV_move(tdbb, missing, to_desc);
464
to_desc->dsc_flags |= DSC_null;
468
USHORT l = to_desc->dsc_length;
469
UCHAR* p = to_desc->dsc_address;
470
switch (to_desc->dsc_dtype) {
472
// YYY - not necessarily the right thing to do
473
// for text formats that don't have trailing spaces
475
const CHARSET_ID chid = DSC_GET_CHARSET(to_desc);
477
CVC: I don't know if we have to check for dynamic-127 charset here.
478
If that is needed, the line above should be replaced by the commented code here.
479
CHARSET_ID chid = INTL_TTYPE(to_desc);
480
if (chid == ttype_dynamic)
481
chid = INTL_charset(tdbb, chid);
483
const char pad = chid == ttype_binary ? '\0' : ' ';
500
to_desc->dsc_flags |= DSC_null;
503
// Handle the null flag as appropriate for fields and message arguments.
505
if (to->nod_type == nod_field)
507
const SSHORT id = (USHORT)(IPTR) to->nod_arg[e_fld_id];
509
request->req_rpb[(int) (IPTR) to->nod_arg[e_fld_stream]].rpb_record;
511
SET_NULL(record, id);
514
CLEAR_NULL(record, id);
517
else if (to->nod_type == nod_argument && to->nod_arg[e_arg_flag])
519
to_desc = EVL_assign_to(tdbb, to->nod_arg[e_arg_flag]);
521
// If the null flag is a string with an effective length of one,
522
// then -1 will not fit. Therefore, store 1 instead.
525
to_desc->dsc_dtype <= dtype_varying &&
526
to_desc->dsc_length <=
527
((to_desc->dsc_dtype == dtype_text) ? 1 :
528
((to_desc->dsc_dtype == dtype_cstring) ? 2 :
534
temp.dsc_dtype = dtype_short;
535
temp.dsc_length = sizeof(SSHORT);
537
temp.dsc_sub_type = 0;
538
temp.dsc_address = (UCHAR *) & null;
539
MOV_move(tdbb, &temp, to_desc);
540
if (null && to->nod_arg[e_arg_indicator]) {
541
to_desc = EVL_assign_to(tdbb, to->nod_arg[e_arg_indicator]);
542
MOV_move(tdbb, &temp, to_desc);
548
void EXE_execute_db_triggers(thread_db* tdbb,
549
jrd_tra* transaction,
550
enum jrd_req::req_ta trigger_action)
552
/**************************************
554
* E X E _ e x e c u t e _ d b _ t r i g g e r s
556
**************************************
558
* Functional description
559
* Execute database triggers
561
**************************************/
562
// do nothing if user doesn't want database triggers
563
if (tdbb->getAttachment()->att_flags & ATT_no_db_triggers)
568
switch (trigger_action)
570
case jrd_req::req_trigger_connect:
571
type = DB_TRIGGER_CONNECT;
574
case jrd_req::req_trigger_disconnect:
575
type = DB_TRIGGER_DISCONNECT;
578
case jrd_req::req_trigger_trans_start:
579
type = DB_TRIGGER_TRANS_START;
582
case jrd_req::req_trigger_trans_commit:
583
type = DB_TRIGGER_TRANS_COMMIT;
586
case jrd_req::req_trigger_trans_rollback:
587
type = DB_TRIGGER_TRANS_ROLLBACK;
595
jrd_req* trigger = NULL;
597
if (tdbb->getDatabase()->dbb_triggers[type])
599
jrd_tra* old_transaction = tdbb->getTransaction();
600
tdbb->setTransaction(transaction);
604
trigger = execute_triggers(tdbb, &tdbb->getDatabase()->dbb_triggers[type],
605
NULL, NULL, trigger_action);
606
tdbb->setTransaction(old_transaction);
610
tdbb->setTransaction(old_transaction);
616
trigger_failure(tdbb, trigger);
620
jrd_req* EXE_find_request(thread_db* tdbb, jrd_req* request, bool validate)
622
/**************************************
624
* E X E _ f i n d _ r e q u e s t
626
**************************************
628
* Functional description
629
* Find an inactive incarnation of a trigger request. If necessary,
632
**************************************/
633
DEV_BLKCHK(request, type_req);
636
Database* dbb = tdbb->getDatabase();
638
/* I found a core file from my test runs that came from a NULL request -
639
* but have no idea what test was running. Let's bugcheck so we can
643
BUGCHECK /* REQUEST */ (167); /* msg 167 invalid SEND request */
645
dbb->dbb_mutexes[DBB_MUTX_clone].enter();
646
jrd_req* clone = NULL;
648
if (!(request->req_flags & req_in_use))
651
if (request->req_attachment == tdbb->getAttachment())
654
/* Request exists and is in use. Search clones for one in use by
655
this attachment. If not found, return first inactive request. */
657
vec<jrd_req*>* vector = request->req_sub_requests;
658
const USHORT clones = (vector) ? (vector->count() - 1) : 0;
661
for (n = 1; n <= clones; n++) {
662
jrd_req* next = CMP_clone_request(tdbb, request, n, validate);
663
if (next->req_attachment == tdbb->getAttachment()) {
664
if (!(next->req_flags & req_in_use)) {
671
else if (!(next->req_flags & req_in_use) && !clone)
675
if (count > MAX_CLONES) {
676
dbb->dbb_mutexes[DBB_MUTX_clone].leave();
677
ERR_post(isc_req_max_clones_exceeded, 0);
680
clone = CMP_clone_request(tdbb, request, n, validate);
682
clone->req_attachment = tdbb->getAttachment();
683
clone->req_stats.reset();
684
clone->req_flags |= req_in_use;
685
dbb->dbb_mutexes[DBB_MUTX_clone].leave();
690
void EXE_receive(thread_db* tdbb,
697
/**************************************
699
* E X E _ r e c e i v e
701
**************************************
703
* Functional description
704
* Move a message from JRD to the host program. This corresponds to
705
* a JRD BLR/jrd_nod* send.
707
**************************************/
710
DEV_BLKCHK(request, type_req);
712
if (--tdbb->tdbb_quantum < 0)
713
JRD_reschedule(tdbb, 0, true);
715
jrd_tra* transaction = request->req_transaction;
717
if (!(request->req_flags & req_active)) {
718
ERR_post(isc_req_sync, 0);
721
if (request->req_flags & req_proc_fetch)
723
/* request->req_proc_sav_point stores all the request savepoints.
724
When going to continue execution put request save point list
725
into transaction->tra_save_point so that it is used in looper.
726
When we come back to EXE_receive() restore
727
transaction->tra_save_point and merge all work done under
728
stored procedure savepoints into the current transaction
729
savepoint, which is the savepoint for fetch. */
731
Savepoint* const save_sav_point = transaction->tra_save_point;
732
transaction->tra_save_point = request->req_proc_sav_point;
733
request->req_proc_sav_point = save_sav_point;
735
if (!transaction->tra_save_point) {
736
VIO_start_save_point(tdbb, transaction);
743
if (request->req_message->nod_type == nod_stall
744
#ifdef SCROLLABLE_CURSORS
745
|| request->req_flags & req_fetch_required
748
execute_looper(tdbb, request, transaction, jrd_req::req_sync);
750
if (!(request->req_flags & req_active) ||
751
request->req_operation != jrd_req::req_send)
753
ERR_post(isc_req_sync, 0);
756
const jrd_nod* message = request->req_message;
757
const Format* format = (Format*) message->nod_arg[e_msg_format];
759
if (msg != (USHORT)(IPTR) message->nod_arg[e_msg_number])
760
ERR_post(isc_req_sync, 0);
762
if (length != format->fmt_length) {
763
ERR_post(isc_port_len,
764
isc_arg_number, (SLONG) length,
765
isc_arg_number, (SLONG) format->fmt_length, 0);
768
if ((U_IPTR) buffer & (ALIGNMENT - 1))
769
MOVE_FAST((SCHAR *) request + message->nod_impure, buffer, length);
771
MOVE_FASTER((SCHAR *) request + message->nod_impure, buffer, length);
773
// ASF: temporary blobs returned to the client should not be released
774
// with the request, but in the transaction end.
777
for (int i = 0; i < format->fmt_count; ++i)
779
const DSC* desc = &format->fmt_desc[i];
783
const bid* id = (bid*)
784
((UCHAR*)request + message->nod_impure + (ULONG)(IPTR)desc->dsc_address);
786
if (transaction->tra_blobs.locate(id->bid_temp_id()))
788
BlobIndex* current = &transaction->tra_blobs.current();
790
if (current->bli_request &&
791
current->bli_request->req_blobs.locate(id->bid_temp_id()))
793
current->bli_request->req_blobs.fastRemove();
794
current->bli_request = NULL;
801
execute_looper(tdbb, request, transaction, jrd_req::req_proceed);
804
catch (const Firebird::Exception&)
806
if (request->req_flags & req_proc_fetch)
808
Savepoint* const save_sav_point = transaction->tra_save_point;
809
transaction->tra_save_point = request->req_proc_sav_point;
810
request->req_proc_sav_point = save_sav_point;
811
release_proc_save_points(request);
816
if (request->req_flags & req_proc_fetch) {
817
Savepoint* const save_sav_point = transaction->tra_save_point;
818
transaction->tra_save_point = request->req_proc_sav_point;
819
request->req_proc_sav_point = save_sav_point;
820
VIO_merge_proc_sav_points(tdbb, transaction,
821
&request->req_proc_sav_point);
827
#ifdef SCROLLABLE_CURSORS
828
void EXE_seek(thread_db* tdbb, jrd_req* request, USHORT direction, ULONG offset)
830
/**************************************
834
**************************************
836
* Functional description
837
* Seek a given request in a particular direction
838
* for offset records.
840
**************************************/
842
DEV_BLKCHK(request, type_req);
844
/* loop through all RSEs in the request,
845
and describe the rsb tree for that rsb;
846
go backwards because items were popped
847
off the stack backwards */
849
/* find the top-level rsb in the request and seek it */
851
for (SLONG i = request->req_fors.getCount() - 1; i >= 0; i--) {
852
RecordSource* rsb = request->req_fors[i];
854
seek_rsb(tdbb, request, rsb, direction, offset);
862
void EXE_send(thread_db* tdbb,
868
/**************************************
872
**************************************
874
* Functional description
875
* Send a message from the host program to the engine.
876
* This corresponds to a blr_receive or blr_select statement.
878
**************************************/
880
DEV_BLKCHK(request, type_req);
882
if (--tdbb->tdbb_quantum < 0)
883
JRD_reschedule(tdbb, 0, true);
885
if (!(request->req_flags & req_active))
886
ERR_post(isc_req_sync, 0);
890
#ifdef SCROLLABLE_CURSORS
891
/* look for an asynchronous send message--if such
892
a message was defined, we allow the user to send
893
us a message at any time during request execution */
894
jrd_nod* save_next = NULL, save_message = NULL;
896
if ((message = request->req_async_message) &&
897
(node = message->nod_arg[e_send_message]) &&
898
(msg == (USHORT)(ULONG) node->nod_arg[e_msg_number]))
900
/* save the current state of the request so we can go
901
back to what was interrupted */
903
const USHORT save_operation = request->req_operation;
904
save_message = request->req_message;
905
save_next = request->req_next;
907
request->req_operation = req_receive;
908
request->req_message = node;
909
request->req_next = message->nod_arg[e_send_statement];
911
/* indicate that we are processing an asynchronous message */
913
request->req_flags |= req_async_processing;
917
if (request->req_operation != jrd_req::req_receive)
918
ERR_post(isc_req_sync, 0);
919
node = request->req_message;
920
#ifdef SCROLLABLE_CURSORS
924
jrd_tra* transaction = request->req_transaction;
926
if (node->nod_type == nod_message)
928
else if (node->nod_type == nod_select) {
929
jrd_nod** ptr = node->nod_arg;
930
for (const jrd_nod* const* const end = ptr + node->nod_count; ptr < end;
933
message = (*ptr)->nod_arg[e_send_message];
934
if ((USHORT)(IPTR) message->nod_arg[e_msg_number] == msg) {
935
request->req_next = *ptr;
941
BUGCHECK(167); /* msg 167 invalid SEND request */
943
const Format* format = (Format*) message->nod_arg[e_msg_format];
945
if (msg != (USHORT)(IPTR) message->nod_arg[e_msg_number])
946
ERR_post(isc_req_sync, 0);
948
if (length != format->fmt_length) {
949
ERR_post(isc_port_len,
950
isc_arg_number, (SLONG) length,
951
isc_arg_number, (SLONG) format->fmt_length, 0);
954
if ((U_IPTR) buffer & (ALIGNMENT - 1))
955
MOVE_FAST(buffer, (SCHAR *) request + message->nod_impure, length);
957
MOVE_FASTER(buffer, (SCHAR *) request + message->nod_impure, length);
959
for (USHORT i = 0; i < format->fmt_count; ++i)
961
const DSC* desc = &format->fmt_desc[i];
963
// ASF: I'll not test for dtype_cstring because usage is only internal
964
if (desc->dsc_dtype == dtype_text || desc->dsc_dtype == dtype_varying)
966
const UCHAR* p = (UCHAR*)request + message->nod_impure + (ULONG)(IPTR)desc->dsc_address;
969
switch (desc->dsc_dtype)
972
len = desc->dsc_length;
976
len = reinterpret_cast<const vary*>(p)->vary_length;
981
CharSet* charSet = INTL_charset_lookup(tdbb, DSC_GET_CHARSET(desc));
983
if (!charSet->wellFormed(len, p))
984
ERR_post(isc_malformed_string, 0);
986
else if (desc->isBlob())
988
if (desc->getCharSet() != CS_NONE && desc->getCharSet() != CS_BINARY)
990
const Jrd::bid* bid = (Jrd::bid*) ((UCHAR*)request +
991
message->nod_impure + (ULONG)(IPTR)desc->dsc_address);
995
AutoBlb blob(tdbb, BLB_open(tdbb, tdbb->getTransaction(), bid));
996
BLB_check_well_formed(tdbb, desc, blob.getBlb());
1002
execute_looper(tdbb, request, transaction, jrd_req::req_proceed);
1004
#ifdef SCROLLABLE_CURSORS
1006
/* if the message was sent asynchronously, restore all the
1007
previous values so that whatever we were trying to do when
1008
the message came in is what we do next */
1010
request->req_operation = save_operation;
1011
request->req_message = save_message;
1012
request->req_next = save_next;
1018
void EXE_start(thread_db* tdbb, jrd_req* request, jrd_tra* transaction)
1020
/**************************************
1024
**************************************
1026
* Functional description
1027
* Start an execution running.
1029
**************************************/
1031
Database* dbb = tdbb->getDatabase();
1033
BLKCHK(request, type_req);
1034
BLKCHK(transaction, type_tra);
1036
if (request->req_flags & req_active)
1037
ERR_post(isc_req_sync, isc_arg_gds, isc_reqinuse, 0);
1039
if (transaction->tra_flags & TRA_prepared)
1040
ERR_post(isc_req_no_trans, 0);
1042
/* Post resources to transaction block. In particular, the interest locks
1043
on relations/indices are copied to the transaction, which is very
1044
important for (short-lived) dynamically compiled requests. This will
1045
provide transaction stability by preventing a relation from being
1046
dropped after it has been referenced from an active transaction. */
1048
TRA_post_resources(tdbb, transaction, request->req_resources);
1050
Lock* lock = transaction->tra_cancel_lock;
1051
if (lock && lock->lck_logical == LCK_none)
1052
LCK_lock_non_blocking(tdbb, lock, LCK_SR, LCK_WAIT);
1054
TRA_attach_request(transaction, request);
1055
request->req_flags &= REQ_FLAGS_INIT_MASK;
1056
request->req_flags |= req_active;
1057
request->req_flags &= ~req_reserved;
1058
request->req_operation = jrd_req::req_evaluate;
1060
/* set up to count records affected by request */
1062
request->req_flags |= req_count_records;
1063
request->req_records_selected = 0;
1064
request->req_records_updated = 0;
1065
request->req_records_inserted = 0;
1066
request->req_records_deleted = 0;
1068
request->req_records_affected.clear();
1070
/* CVC: set up to count virtual operations on SQL views. */
1072
request->req_view_flags = 0;
1073
request->req_top_view_store = NULL;
1074
request->req_top_view_modify = NULL;
1075
request->req_top_view_erase = NULL;
1077
// Store request start time for timestamp work
1078
request->req_timestamp.validate();
1080
// Set all invariants to not computed.
1081
jrd_nod **ptr, **end;
1082
for (ptr = request->req_invariants.begin(),
1083
end = request->req_invariants.end(); ptr < end;
1086
impure_value* impure = (impure_value*) ((SCHAR *) request + (*ptr)->nod_impure);
1087
impure->vlu_flags = 0;
1090
tdbb->bumpStats(RuntimeStatistics::STMT_EXECUTES);
1092
// Start a save point if not in middle of one
1093
if (transaction && (transaction != dbb->dbb_sys_trans)) {
1094
VIO_start_save_point(tdbb, transaction);
1097
request->req_src_line = 0;
1098
request->req_src_column = 0;
1101
START_CHECK_FOR_EXCEPTIONS(NULL);
1104
// 1. Try to fix the problem with MSVC C++ runtime library, making
1105
// even C++ exceptions that are implemented in terms of Win32 SEH
1106
// getting catched by the SEH handler below.
1107
// 2. Check if it really is correct that only Win32 catches CPU
1108
// exceptions (such as SEH) here. Shouldn't any platform capable
1109
// of handling signals use this stuff?
1110
// (see jrd/ibsetjmp.h for implementation of these macros)
1112
looper(tdbb, request, request->req_top_node);
1115
END_CHECK_FOR_EXCEPTIONS(NULL);
1118
// If any requested modify/delete/insert ops have completed, forget them
1121
(transaction != dbb->dbb_sys_trans) &&
1122
transaction->tra_save_point &&
1123
!(transaction->tra_save_point->sav_flags & SAV_user) &&
1124
!transaction->tra_save_point->sav_verb_count)
1126
// Forget about any undo for this verb
1128
VIO_verb_cleanup(tdbb, transaction);
1133
void EXE_unwind(thread_db* tdbb, jrd_req* request)
1135
/**************************************
1137
* E X E _ u n w i n d
1139
**************************************
1141
* Functional description
1142
* Unwind a request, maybe active, maybe not. This is particularly
1143
* simple since nothing really needs to be done.
1145
**************************************/
1146
DEV_BLKCHK(request, type_req);
1150
if (request->req_flags & req_active)
1152
if (request->req_fors.getCount() || request->req_exec_sta.getCount())
1154
Jrd::ContextPoolHolder context(tdbb, request->req_pool);
1155
jrd_req* old_request = tdbb->getRequest();
1156
jrd_tra* old_transaction = tdbb->getTransaction();
1158
tdbb->setRequest(request);
1159
tdbb->setTransaction(request->req_transaction);
1161
RecordSource** ptr = request->req_fors.begin();
1162
for (const RecordSource* const* const end =
1163
request->req_fors.end(); ptr < end; ptr++)
1166
RSE_close(tdbb, *ptr);
1169
for (size_t i = 0; i < request->req_exec_sta.getCount(); ++i)
1171
jrd_nod* node = request->req_exec_sta[i];
1172
ExecuteStatement* impure =
1173
(ExecuteStatement*) ((char*) request + node->nod_impure);
1174
impure->Close(tdbb);
1177
catch (const Firebird::Exception&)
1179
tdbb->setRequest(old_request);
1180
tdbb->setTransaction(old_transaction);
1184
tdbb->setRequest(old_request);
1185
tdbb->setTransaction(old_transaction);
1187
release_blobs(tdbb, request);
1190
if (request->req_proc_sav_point && (request->req_flags & req_proc_fetch))
1191
release_proc_save_points(request);
1193
TRA_detach_request(request);
1195
request->req_flags &= ~(req_active | req_proc_fetch | req_reserved);
1196
request->req_flags |= req_abort | req_stall;
1197
request->req_timestamp.invalidate();
1201
/* CVC: Moved to its own routine, originally in store(). */
1202
static void cleanup_rpb(thread_db* tdbb, record_param* rpb)
1204
/**************************************
1206
* c l e a n u p _ r p b
1208
**************************************
1210
* Functional description
1211
* Perform cleaning of rpb, zeroing unassigned fields and
1212
* the impure tail of varying fields that we don't want to carry
1213
* when the RLE algorithm is applied.
1215
**************************************/
1216
Record* record = rpb->rpb_record;
1217
const Format* format = record->rec_format;
1219
SET_TDBB(tdbb); /* Is it necessary? */
1222
Starting from the format, walk through its
1223
array of descriptors. If the descriptor has
1224
no address, its a computed field and we shouldn't
1225
try to fix it. Get a pointer to the actual data
1226
and see if that field is null by indexing into
1227
the null flags between the record header and the
1231
for (USHORT n = 0; n < format->fmt_count; n++)
1233
const dsc* desc = &format->fmt_desc[n];
1234
if (!desc->dsc_address)
1236
UCHAR* p = record->rec_data + (IPTR) desc->dsc_address;
1237
if (TEST_NULL(record, n))
1239
USHORT length = desc->dsc_length;
1241
memset(p, 0, length);
1244
else if (desc->dsc_dtype == dtype_varying)
1246
vary* varying = reinterpret_cast<vary*>(p);
1247
USHORT length = desc->dsc_length - sizeof(USHORT);
1248
if (length > varying->vary_length)
1250
p = reinterpret_cast<UCHAR*>(varying->vary_string + varying->vary_length);
1251
length -= varying->vary_length;
1252
memset(p, 0, length);
1258
inline void PreModifyEraseTriggers(thread_db* tdbb,
1265
/******************************************************
1267
* P r e M o d i f y E r a s e T r i g g e r s
1269
******************************************************
1271
* Functional description
1272
* Perform operation's pre-triggers,
1273
* storing active rpb in chain.
1275
******************************************************/
1276
if (! tdbb->getTransaction()->tra_rpblist) {
1277
tdbb->getTransaction()->tra_rpblist =
1278
FB_NEW(*tdbb->getTransaction()->tra_pool)
1279
traRpbList(*tdbb->getTransaction()->tra_pool);
1281
const int rpblevel =
1282
tdbb->getTransaction()->tra_rpblist->PushRpb(rpb);
1283
jrd_req* trigger = NULL;
1284
if ((*trigs) && (which_trig != POST_TRIG)) {
1285
trigger = execute_triggers(tdbb, trigs, rpb->rpb_record, rec, op);
1287
tdbb->getTransaction()->tra_rpblist->PopRpb(rpb, rpblevel);
1289
trigger_failure(tdbb, trigger);
1293
static jrd_nod* erase(thread_db* tdbb, jrd_nod* node, SSHORT which_trig)
1295
/**************************************
1299
**************************************
1301
* Functional description
1302
* Perform erase operation.
1304
**************************************/
1306
Database* dbb = tdbb->getDatabase();
1307
BLKCHK(node, type_nod);
1309
jrd_req* request = tdbb->getRequest();
1310
jrd_tra* transaction = request->req_transaction;
1311
record_param* rpb = &request->req_rpb[(int) (IPTR) node->nod_arg[e_erase_stream]];
1312
jrd_rel* relation = rpb->rpb_relation;
1314
if (rpb->rpb_number.isBof() ||
1315
(!relation->rel_view_rse && !rpb->rpb_number.isValid()))
1317
ERR_post(isc_no_cur_rec, 0);
1320
switch (request->req_operation) {
1321
case jrd_req::req_evaluate:
1323
request->req_records_affected.bumpModified(false);
1324
if (!node->nod_arg[e_erase_statement])
1326
const Format* format = MET_current(tdbb, rpb->rpb_relation);
1327
Record* record = VIO_record(tdbb, rpb, format, tdbb->getDefaultPool());
1328
rpb->rpb_address = record->rec_data;
1329
rpb->rpb_length = format->fmt_length;
1330
rpb->rpb_format_number = format->fmt_version;
1331
return node->nod_arg[e_erase_statement];
1334
case jrd_req::req_return:
1338
return node->nod_parent;
1341
request->req_operation = jrd_req::req_return;
1342
RLCK_reserve_relation(tdbb, transaction, relation, true, true);
1344
/* If the stream was sorted, the various fields in the rpb are
1345
probably junk. Just to make sure that everything is cool,
1346
refetch and release the record. */
1348
if (rpb->rpb_stream_flags & RPB_s_refetch) {
1349
VIO_refetch_record(tdbb, rpb, transaction);
1350
rpb->rpb_stream_flags &= ~RPB_s_refetch;
1353
if (transaction != dbb->dbb_sys_trans)
1354
++transaction->tra_save_point->sav_verb_count;
1356
/* Handle pre-operation trigger */
1357
PreModifyEraseTriggers(tdbb, &relation->rel_pre_erase,
1358
which_trig, rpb, NULL,
1359
jrd_req::req_trigger_delete);
1361
if (relation->rel_file) {
1362
EXT_erase(rpb, transaction);
1364
else if (relation->isVirtual()) {
1365
VirtualTable::erase(tdbb, rpb);
1367
else if (!relation->rel_view_rse) {
1368
VIO_erase(tdbb, rpb, transaction);
1371
/* Handle post operation trigger */
1373
if (relation->rel_post_erase &&
1374
which_trig != PRE_TRIG &&
1375
(trigger = execute_triggers(tdbb, &relation->rel_post_erase,
1376
rpb->rpb_record, NULL,
1377
jrd_req::req_trigger_delete)))
1379
trigger_failure(tdbb, trigger);
1382
/* call IDX_erase (which checks constraints) after all post erase triggers
1383
have fired. This is required for cascading referential integrity, which
1384
can be implemented as post_erase triggers */
1386
if (!relation->rel_file &&
1387
!relation->rel_view_rse &&
1388
!relation->isVirtual())
1390
jrd_rel* bad_relation = 0;
1393
const IDX_E error_code =
1394
IDX_erase(tdbb, rpb, transaction, &bad_relation, &bad_index);
1397
ERR_duplicate_error(error_code, bad_relation, bad_index);
1401
/* CVC: Increment the counter only if we called VIO/EXT_erase() and
1402
we were successful. */
1403
if (!(request->req_view_flags & req_first_erase_return)) {
1404
request->req_view_flags |= req_first_erase_return;
1405
if (relation->rel_view_rse) {
1406
request->req_top_view_erase = relation;
1409
if (relation == request->req_top_view_erase) {
1410
if (which_trig == ALL_TRIGS || which_trig == POST_TRIG) {
1411
request->req_records_deleted++;
1412
request->req_records_affected.bumpModified(true);
1415
else if (relation->rel_file || !relation->rel_view_rse) {
1416
request->req_records_deleted++;
1417
request->req_records_affected.bumpModified(true);
1420
if (transaction != dbb->dbb_sys_trans) {
1421
--transaction->tra_save_point->sav_verb_count;
1424
rpb->rpb_number.setValid(false);
1426
return node->nod_parent;
1430
static void execute_looper(
1433
jrd_tra* transaction, enum jrd_req::req_s next_state)
1435
/**************************************
1437
* e x e c u t e _ l o o p e r
1439
**************************************
1441
* Functional description
1442
* Wrapper around looper. This will execute
1443
* looper with the save point mechanism.
1445
**************************************/
1446
DEV_BLKCHK(request, type_req);
1449
Database* dbb = tdbb->getDatabase();
1451
/* Start a save point */
1453
if (!(request->req_flags & req_proc_fetch) && request->req_transaction)
1454
if (transaction && (transaction != dbb->dbb_sys_trans))
1455
VIO_start_save_point(tdbb, transaction);
1457
request->req_flags &= ~req_stall;
1458
request->req_operation = next_state;
1460
looper(tdbb, request, request->req_next);
1462
/* If any requested modify/delete/insert ops have completed, forget them */
1464
if (!(request->req_flags & req_proc_fetch) && request->req_transaction) {
1465
if (transaction && (transaction != dbb->dbb_sys_trans) &&
1466
transaction->tra_save_point &&
1467
!transaction->tra_save_point->sav_verb_count)
1469
/* Forget about any undo for this verb */
1471
VIO_verb_cleanup(tdbb, transaction);
1477
static void exec_sql(thread_db* tdbb, jrd_req* request, DSC* dsc)
1479
/**************************************
1483
**************************************
1485
* Functional description
1486
* Execute a string as SQL operator.
1488
**************************************/
1491
if (tdbb->getTransaction()->tra_callback_count >= MAX_CALLBACKS) {
1492
ERR_post(isc_exec_sql_max_call_exceeded, 0);
1495
Firebird::string SqlStatementText;
1496
ExecuteStatement::getString(tdbb, SqlStatementText, dsc, request);
1498
ISC_STATUS_ARRAY local;
1499
memset(local, 0, sizeof(local));
1500
ISC_STATUS* status = local;
1502
#if (defined DEV_BUILD && !defined MULTI_THREAD)
1503
tdbb->getDatabase()->dbb_flags |= DBB_exec_statement;
1505
tdbb->getTransaction()->tra_callback_count++;
1506
callback_execute_immediate(status,
1507
tdbb->getAttachment(),
1508
tdbb->getTransaction(),
1510
tdbb->getTransaction()->tra_callback_count--;
1511
#if (defined DEV_BUILD && !defined MULTI_THREAD)
1512
tdbb->getDatabase()->dbb_flags &= ~DBB_exec_statement;
1516
memcpy(tdbb->tdbb_status_vector, status, sizeof(local));
1522
static void execute_procedure(thread_db* tdbb, jrd_nod* node)
1524
/**************************************
1526
* e x e c u t e _ p r o c e d u r e
1528
**************************************
1530
* Functional description
1531
* Execute a stored procedure. Begin by
1532
* assigning the input parameters. End
1533
* by assigning the output parameters.
1535
**************************************/
1537
BLKCHK(node, type_nod);
1539
jrd_req* request = tdbb->getRequest();
1541
jrd_nod* temp = node->nod_arg[e_esp_inputs];
1545
for (ptr = temp->nod_arg, end = ptr + temp->nod_count; ptr < end;
1548
EXE_assignment(tdbb, *ptr);
1552
USHORT in_msg_length;
1554
jrd_nod* in_message = node->nod_arg[e_esp_in_msg];
1556
const Format* format = (Format*) in_message->nod_arg[e_msg_format];
1557
in_msg_length = format->fmt_length;
1558
in_msg = (SCHAR *) request + in_message->nod_impure;
1561
USHORT out_msg_length;
1563
jrd_nod* out_message = node->nod_arg[e_esp_out_msg];
1565
const Format* format = (Format*) out_message->nod_arg[e_msg_format];
1566
out_msg_length = format->fmt_length;
1567
out_msg = (SCHAR *) request + out_message->nod_impure;
1570
jrd_prc* procedure = (jrd_prc*) node->nod_arg[e_esp_procedure];
1571
jrd_req* proc_request = EXE_find_request(tdbb, procedure->prc_request, false);
1573
Firebird::Array<char> temp_buffer;
1576
const Format* format = (Format*) procedure->prc_output_msg->nod_arg[e_msg_format];
1577
out_msg_length = format->fmt_length;
1578
out_msg = temp_buffer.getBuffer(out_msg_length + DOUBLE_ALIGN - 1);
1579
out_msg = (SCHAR *) FB_ALIGN((U_IPTR) out_msg, DOUBLE_ALIGN);
1583
/* Catch errors so we can unwind cleanly */
1586
// Save the old pool
1587
Jrd::ContextPoolHolder context(tdbb, proc_request->req_pool);
1589
jrd_tra* transaction = request->req_transaction;
1590
const SLONG save_point_number = transaction->tra_save_point->sav_number;
1592
proc_request->req_timestamp = request->req_timestamp;
1593
EXE_start(tdbb, proc_request, transaction);
1595
EXE_send(tdbb, proc_request, 0, in_msg_length,
1596
reinterpret_cast<const UCHAR*>(in_msg));
1599
EXE_receive(tdbb, proc_request, 1, out_msg_length,
1600
reinterpret_cast<UCHAR*>(out_msg));
1602
/* Clean up all savepoints started during execution of the
1605
if (transaction != tdbb->getDatabase()->dbb_sys_trans) {
1606
for (const Savepoint* save_point = transaction->tra_save_point;
1607
save_point && save_point_number < save_point->sav_number;
1608
save_point = transaction->tra_save_point)
1610
VIO_verb_cleanup(tdbb, transaction);
1615
catch (const Firebird::Exception&) {
1616
tdbb->setRequest(request);
1617
EXE_unwind(tdbb, proc_request);
1618
proc_request->req_attachment = NULL;
1619
proc_request->req_flags &= ~(req_in_use | req_proc_fetch);
1620
proc_request->req_timestamp.invalidate();
1624
EXE_unwind(tdbb, proc_request);
1625
tdbb->setRequest(request);
1627
temp = node->nod_arg[e_esp_outputs];
1631
for (ptr = temp->nod_arg, end = ptr + temp->nod_count; ptr < end;
1634
EXE_assignment(tdbb, *ptr);
1638
proc_request->req_attachment = NULL;
1639
proc_request->req_flags &= ~(req_in_use | req_proc_fetch);
1640
proc_request->req_timestamp.invalidate();
1644
static jrd_req* execute_triggers(thread_db* tdbb,
1645
trig_vec** triggers,
1648
enum jrd_req::req_ta trigger_action)
1650
/**************************************
1652
* e x e c u t e _ t r i g g e r s
1654
**************************************
1656
* Functional description
1657
* Execute group of triggers. Return pointer to failing trigger
1660
**************************************/
1667
jrd_tra* transaction = (tdbb->getRequest() ? tdbb->getRequest()->req_transaction : tdbb->getTransaction());
1668
trig_vec* vector = *triggers;
1669
jrd_req* result = NULL;
1671
Record* null_rec = NULL;
1673
if (!old_rec && !new_rec)
1675
// this is a database trigger
1677
else if (!old_rec || !new_rec)
1679
const Record* record = old_rec ? old_rec : new_rec;
1680
fb_assert(record && record->rec_format);
1682
null_rec = FB_NEW_RPT(record->rec_pool, record->rec_length)
1683
Record(record->rec_pool);
1684
null_rec->rec_length = record->rec_length;
1685
null_rec->rec_format = record->rec_format;
1686
// zero the record buffer
1687
memset(null_rec->rec_data, 0, record->rec_length);
1688
// initialize all fields to missing
1689
const SSHORT n = (record->rec_format->fmt_count + 7) >> 3;
1690
memset(null_rec->rec_data, 0xFF, n);
1693
jrd_req* trigger = NULL;
1694
Firebird::TimeStamp timestamp;
1698
for (trig_vec::iterator ptr = vector->begin(); ptr != vector->end(); ++ptr)
1701
trigger = EXE_find_request(tdbb, ptr->request, false);
1702
trigger->req_rpb[0].rpb_record = old_rec ? old_rec : null_rec;
1703
trigger->req_rpb[1].rpb_record = new_rec ? new_rec : null_rec;
1705
if (tdbb->getRequest())
1706
trigger->req_timestamp = tdbb->getRequest()->req_timestamp;
1708
trigger->req_timestamp = timestamp;
1710
trigger->req_trigger_action = trigger_action;
1711
EXE_start(tdbb, trigger, transaction);
1712
trigger->req_attachment = NULL;
1713
trigger->req_flags &= ~req_in_use;
1714
trigger->req_timestamp.invalidate();
1715
if (trigger->req_operation == jrd_req::req_unwind) {
1723
if (vector != *triggers) {
1724
MET_release_triggers(tdbb, &vector);
1729
catch (const Firebird::Exception& ex)
1732
if (vector != *triggers) {
1733
MET_release_triggers(tdbb, &vector);
1736
throw; // trigger probally fails to compile
1738
Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
1744
static void stuff_stack_trace(const jrd_req* request)
1746
Firebird::string sTrace;
1747
bool isEmpty = true;
1749
for (const jrd_req* req = request; req; req = req->req_caller)
1751
Firebird::string name;
1753
if (req->req_trg_name.length()) {
1754
name = "At trigger '";
1755
name += req->req_trg_name.c_str();
1757
else if (req->req_procedure) {
1758
name = "At procedure '";
1759
name += req->req_procedure->prc_name.c_str();
1762
if (! name.isEmpty())
1766
if (sTrace.length() + name.length() + 2 > MAX_STACK_TRACE)
1771
sTrace += name + "'";
1774
sTrace += "\n" + name + "'";
1777
if (req->req_src_line)
1779
Firebird::string src_info;
1780
src_info.printf(" line: %u, col: %u", req->req_src_line, req->req_src_column);
1782
if (sTrace.length() + src_info.length() > MAX_STACK_TRACE)
1791
ERR_post_nothrow(isc_stack_trace, isc_arg_string, ERR_cstring(sTrace), 0);
1795
static jrd_nod* looper(thread_db* tdbb, jrd_req* request, jrd_nod* in_node)
1797
/**************************************
1801
**************************************
1803
* Functional description
1804
* Cycle thru request execution tree. Return next node for
1805
* execution on stall or request complete.
1807
**************************************/
1808
SSHORT which_erase_trig = 0;
1809
SSHORT which_sto_trig = 0;
1810
SSHORT which_mod_trig = 0;
1811
jrd_nod* top_node = 0;
1814
jrd_tra* transaction = request->req_transaction;
1816
ERR_post(isc_req_no_trans, 0);
1820
Database* dbb = tdbb->getDatabase();
1821
BLKCHK(in_node, type_nod);
1823
// Save the old pool and request to restore on exit
1824
JrdMemoryPool* old_pool = tdbb->getDefaultPool();
1825
Jrd::ContextPoolHolder context(tdbb, request->req_pool);
1827
jrd_req* old_request = tdbb->getRequest();
1828
tdbb->setRequest(request);
1829
jrd_tra* old_transaction = tdbb->getTransaction();
1830
tdbb->setTransaction(transaction);
1831
fb_assert(request->req_caller == NULL);
1832
request->req_caller = old_request;
1834
const SLONG save_point_number = (transaction->tra_save_point) ?
1835
transaction->tra_save_point->sav_number : 0;
1837
jrd_nod* node = in_node;
1839
// Catch errors so we can unwind cleanly
1841
bool error_pending = false;
1842
bool catch_disabled = false;
1843
tdbb->tdbb_flags &= ~(TDBB_stack_trace_done | TDBB_sys_error);
1845
// Execute stuff until we drop
1847
while (node && !(request->req_flags & req_stall))
1851
if (request->req_operation == jrd_req::req_evaluate &&
1852
(--tdbb->tdbb_quantum < 0))
1854
JRD_reschedule(tdbb, 0, true);
1857
#if defined(DEBUG_GDS_ALLOC) && FALSE
1858
int node_type = node->nod_type;
1861
switch (node->nod_type) {
1863
if (request->req_operation == jrd_req::req_evaluate) {
1864
jrd_nod** ptr = node->nod_arg;
1865
for (const jrd_nod* const* const end = ptr + node->nod_count;
1868
EXE_assignment(tdbb, *ptr);
1870
request->req_operation = jrd_req::req_return;
1872
node = node->nod_parent;
1875
case nod_assignment:
1876
if (request->req_operation == jrd_req::req_evaluate)
1877
EXE_assignment(tdbb, node);
1878
node = node->nod_parent;
1881
case nod_dcl_variable:
1883
impure_value* variable = (impure_value*) ((SCHAR *) request + node->nod_impure);
1884
variable->vlu_desc = *(DSC *) (node->nod_arg + e_dcl_desc);
1885
variable->vlu_desc.dsc_flags = 0;
1886
variable->vlu_desc.dsc_address =
1887
(UCHAR *) & variable->vlu_misc;
1888
if (variable->vlu_desc.dsc_dtype <= dtype_varying
1889
&& !variable->vlu_string)
1891
variable->vlu_string =
1892
FB_NEW_RPT(*tdbb->getDefaultPool(),
1893
variable->vlu_desc.dsc_length) VaryingString();
1894
variable->vlu_string->str_length =
1895
variable->vlu_desc.dsc_length;
1896
variable->vlu_desc.dsc_address =
1897
variable->vlu_string->str_data;
1900
request->req_operation = jrd_req::req_return;
1901
node = node->nod_parent;
1906
if (request->req_operation == jrd_req::req_unwind) {
1907
node = node->nod_parent;
1909
else if ((request->req_operation == jrd_req::req_return) &&
1910
(node->nod_arg[e_erase_sub_erase]))
1914
which_erase_trig = PRE_TRIG;
1917
node = erase(tdbb, node, which_erase_trig);
1918
if (which_erase_trig == PRE_TRIG) {
1919
node = prev_node->nod_arg[e_erase_sub_erase];
1920
node->nod_parent = prev_node;
1922
if (top_node == prev_node && which_erase_trig == POST_TRIG) {
1924
which_erase_trig = ALL_TRIGS;
1927
request->req_operation = jrd_req::req_evaluate;
1931
node = erase(tdbb, node, ALL_TRIGS);
1932
if (!(prev_node->nod_arg[e_erase_sub_erase]) &&
1933
which_erase_trig == PRE_TRIG)
1935
which_erase_trig = POST_TRIG;
1941
if (request->req_operation == jrd_req::req_unwind) {
1942
node = node->nod_parent;
1945
execute_procedure(tdbb, node);
1946
node = node->nod_parent;
1947
request->req_operation = jrd_req::req_return;
1951
switch (request->req_operation) {
1952
case jrd_req::req_evaluate:
1953
request->req_records_affected.clear();
1954
RSE_open(tdbb, (RecordSource*) node->nod_arg[e_for_rsb]);
1955
case jrd_req::req_return:
1956
if (node->nod_arg[e_for_stall]) {
1957
node = node->nod_arg[e_for_stall];
1960
case jrd_req::req_sync:
1961
if (RSE_get_record(tdbb, (RecordSource*) node->nod_arg[e_for_rsb],
1962
#ifdef SCROLLABLE_CURSORS
1968
node = node->nod_arg[e_for_statement];
1969
request->req_operation = jrd_req::req_evaluate;
1972
request->req_operation = jrd_req::req_return;
1974
RSE_close(tdbb, (RecordSource*) node->nod_arg[e_for_rsb]);
1975
node = node->nod_parent;
1979
case nod_dcl_cursor:
1980
if (request->req_operation == jrd_req::req_evaluate) {
1981
const USHORT number = (USHORT) (IPTR) node->nod_arg[e_dcl_cursor_number];
1982
// set up the cursors vector
1983
request->req_cursors = vec<RecordSource*>::newVector(*request->req_pool,
1984
request->req_cursors, number + 1);
1985
// store RecordSource in the vector
1986
(*request->req_cursors)[number] = (RecordSource*) node->nod_arg[e_dcl_cursor_rsb];
1987
request->req_operation = jrd_req::req_return;
1989
node = node->nod_parent;
1992
case nod_cursor_stmt:
1994
const UCHAR op = (UCHAR) (IPTR) node->nod_arg[e_cursor_stmt_op];
1995
const USHORT number = (USHORT) (IPTR) node->nod_arg[e_cursor_stmt_number];
1996
// get RecordSource and the impure area
1997
fb_assert(request->req_cursors && number < request->req_cursors->count());
1998
RecordSource* rsb = (*request->req_cursors)[number];
1999
IRSB impure = (IRSB) ((UCHAR*) tdbb->getRequest() + rsb->rsb_impure);
2001
case blr_cursor_open:
2002
if (request->req_operation == jrd_req::req_evaluate) {
2003
// check cursor state
2004
if (impure->irsb_flags & irsb_open) {
2005
ERR_post(isc_cursor_already_open, 0);
2008
RSE_open(tdbb, rsb);
2009
request->req_operation = jrd_req::req_return;
2011
node = node->nod_parent;
2013
case blr_cursor_close:
2014
if (request->req_operation == jrd_req::req_evaluate) {
2015
// check cursor state
2016
if (!(impure->irsb_flags & irsb_open)) {
2017
ERR_post(isc_cursor_not_open, 0);
2020
RSE_close(tdbb, rsb);
2021
request->req_operation = jrd_req::req_return;
2023
node = node->nod_parent;
2025
case blr_cursor_fetch:
2026
switch (request->req_operation) {
2027
case jrd_req::req_evaluate:
2028
// check cursor state
2029
if (!(impure->irsb_flags & irsb_open)) {
2030
ERR_post(isc_cursor_not_open, 0);
2032
request->req_records_affected.clear();
2033
// perform preliminary navigation, if specified
2034
if (node->nod_arg[e_cursor_stmt_seek]) {
2035
node = node->nod_arg[e_cursor_stmt_seek];
2039
if (RSE_get_record(tdbb, rsb,
2040
#ifdef SCROLLABLE_CURSORS
2046
node = node->nod_arg[e_cursor_stmt_into];
2047
request->req_operation = jrd_req::req_evaluate;
2050
request->req_operation = jrd_req::req_return;
2052
node = node->nod_parent;
2060
switch (request->req_operation) {
2061
case jrd_req::req_evaluate:
2063
PsqlException* xcp_node = reinterpret_cast<PsqlException*>(node->nod_arg[e_xcp_desc]);
2066
/* PsqlException is defined,
2067
so throw an exception */
2068
set_error(tdbb, &xcp_node->xcp_rpt[0], node->nod_arg[e_xcp_msg]);
2070
else if (!request->req_last_xcp.success())
2072
/* PsqlException is undefined, but there was a known exception before,
2073
so re-initiate it */
2074
set_error(tdbb, NULL, NULL);
2078
/* PsqlException is undefined and there weren't any exceptions before,
2079
so just do nothing */
2080
request->req_operation = jrd_req::req_return;
2085
node = node->nod_parent;
2089
case nod_user_savepoint:
2090
switch (request->req_operation) {
2091
case jrd_req::req_evaluate:
2092
if (transaction != dbb->dbb_sys_trans) {
2094
const UCHAR operation = (UCHAR) (IPTR) node->nod_arg[e_sav_operation];
2095
const TEXT* node_savepoint_name = (TEXT*) node->nod_arg[e_sav_name];
2097
// Skip the savepoint created by EXE_start
2098
Savepoint* savepoint = transaction->tra_save_point->sav_next;
2099
Savepoint* previous = transaction->tra_save_point;
2104
if (!savepoint || !(savepoint->sav_flags & SAV_user))
2107
if (!strcmp(node_savepoint_name, savepoint->sav_name)) {
2112
previous = savepoint;
2113
savepoint = savepoint->sav_next;
2115
if (!found && operation != blr_savepoint_set) {
2116
ERR_post(isc_invalid_savepoint,
2117
isc_arg_string, ERR_cstring(node_savepoint_name), 0);
2122
case blr_savepoint_set:
2123
// Release the savepoint
2125
Savepoint* const current = transaction->tra_save_point;
2126
transaction->tra_save_point = savepoint;
2127
verb_cleanup(tdbb, transaction);
2128
previous->sav_next = transaction->tra_save_point;
2129
transaction->tra_save_point = current;
2132
// Use the savepoint created by EXE_start
2133
transaction->tra_save_point->sav_flags |= SAV_user;
2134
strcpy(transaction->tra_save_point->sav_name, node_savepoint_name);
2136
case blr_savepoint_release_single:
2138
// Release the savepoint
2139
Savepoint* const current = transaction->tra_save_point;
2140
transaction->tra_save_point = savepoint;
2141
verb_cleanup(tdbb, transaction);
2142
previous->sav_next = transaction->tra_save_point;
2143
transaction->tra_save_point = current;
2146
case blr_savepoint_release:
2148
const SLONG sav_number = savepoint->sav_number;
2150
// Release the savepoint and all subsequent ones
2151
while (transaction->tra_save_point &&
2152
transaction->tra_save_point->sav_number >= sav_number)
2154
verb_cleanup(tdbb, transaction);
2157
// Restore the savepoint initially created by EXE_start
2158
VIO_start_save_point(tdbb, transaction);
2161
case blr_savepoint_undo:
2163
const SLONG sav_number = savepoint->sav_number;
2165
// Undo the savepoint
2166
while (transaction->tra_save_point &&
2167
transaction->tra_save_point->sav_number >= sav_number)
2169
transaction->tra_save_point->sav_verb_count++;
2170
verb_cleanup(tdbb, transaction);
2173
// Now set the savepoint again to allow to return to it later
2174
VIO_start_save_point(tdbb, transaction);
2175
transaction->tra_save_point->sav_flags |= SAV_user;
2176
strcpy(transaction->tra_save_point->sav_name, node_savepoint_name);
2185
node = node->nod_parent;
2186
request->req_operation = jrd_req::req_return;
2190
case nod_start_savepoint:
2191
switch (request->req_operation) {
2192
case jrd_req::req_evaluate:
2193
/* Start a save point */
2195
if (transaction != dbb->dbb_sys_trans)
2196
VIO_start_save_point(tdbb, transaction);
2199
node = node->nod_parent;
2200
request->req_operation = jrd_req::req_return;
2204
case nod_end_savepoint:
2205
switch (request->req_operation) {
2206
case jrd_req::req_evaluate:
2207
case jrd_req::req_unwind:
2208
/* If any requested modify/delete/insert
2209
ops have completed, forget them */
2210
if (transaction != dbb->dbb_sys_trans) {
2211
/* If an error is still pending when the savepoint is
2212
supposed to end, then the application didn't handle the
2213
error and the savepoint should be undone. */
2214
if (error_pending) {
2215
++transaction->tra_save_point->sav_verb_count;
2217
verb_cleanup(tdbb, transaction);
2221
node = node->nod_parent;
2222
request->req_operation = jrd_req::req_return;
2227
switch (request->req_operation) {
2228
case jrd_req::req_evaluate:
2229
node = node->nod_arg[0];
2232
case jrd_req::req_unwind:
2233
if (!request->req_label)
2234
request->req_operation = jrd_req::req_return;
2237
node = node->nod_parent;
2242
switch (request->req_operation)
2246
case jrd_req::req_evaluate:
2247
if (transaction != dbb->dbb_sys_trans) {
2248
VIO_start_save_point(tdbb, transaction);
2249
const Savepoint* save_point = transaction->tra_save_point;
2250
count = save_point->sav_number;
2252
(SCHAR *) request + node->nod_impure,
2255
node = node->nod_arg[e_blk_action];
2258
case jrd_req::req_unwind:
2260
if (request->req_flags & req_leave)
2262
// Although the req_operation is set to req_unwind,
2263
// it's not an error case if req_leave bit is set.
2264
// req_leave bit indicates that we hit an EXIT or
2265
// BREAK/LEAVE statement in the SP/trigger code.
2266
// Do not perform the error handling stuff.
2268
if (transaction != dbb->dbb_sys_trans) {
2269
MOVE_FAST((SCHAR *) request + node->nod_impure,
2270
&count, sizeof(SLONG));
2271
for (const Savepoint* save_point = transaction->tra_save_point;
2272
save_point && count <= save_point->sav_number;
2273
save_point = transaction->tra_save_point)
2275
verb_cleanup(tdbb, transaction);
2279
node = node->nod_parent;
2282
if (transaction != dbb->dbb_sys_trans)
2284
MOVE_FAST((SCHAR *) request + node->nod_impure,
2285
&count, sizeof(SLONG));
2286
/* Since there occurred an error (req_unwind), undo all savepoints
2287
up to, but not including, the savepoint of this block. The
2288
savepoint of this block will be dealt with below. */
2289
for (const Savepoint* save_point = transaction->tra_save_point;
2290
save_point && count < save_point->sav_number;
2291
save_point = transaction->tra_save_point)
2293
++transaction->tra_save_point->sav_verb_count;
2294
verb_cleanup(tdbb, transaction);
2298
jrd_nod* handlers = node->nod_arg[e_blk_handlers];
2301
node = node->nod_parent;
2302
jrd_nod** ptr = handlers->nod_arg;
2303
for (const jrd_nod* const* const end = ptr + handlers->nod_count;
2306
const PsqlException* xcp_node =
2307
reinterpret_cast<PsqlException*>((*ptr)->nod_arg[e_err_conditions]);
2308
if (test_and_fixup_error(tdbb, xcp_node, request))
2310
request->req_operation = jrd_req::req_evaluate;
2311
node = (*ptr)->nod_arg[e_err_action];
2312
error_pending = false;
2314
/* On entering looper old_request etc. are saved.
2315
On recursive calling we will loose the actual old
2316
request for that invocation of looper. Avoid this. */
2319
Jrd::ContextPoolHolder contextLooper(tdbb, old_pool);
2320
tdbb->setRequest(old_request);
2321
fb_assert(request->req_caller == old_request);
2322
request->req_caller = NULL;
2324
/* Save the previous state of req_error_handler
2325
bit. We need to restore it later. This is
2326
necessary if the error handler is deeply
2329
const ULONG prev_req_error_handler =
2330
request->req_flags & req_error_handler;
2331
request->req_flags |= req_error_handler;
2332
node = looper(tdbb, request, node);
2333
request->req_flags &= ~req_error_handler;
2334
request->req_flags |= prev_req_error_handler;
2336
/* Note: Previously the above call
2337
"node = looper (tdbb, request, node);"
2338
never returned back till the node tree
2339
was executed completely. Now that the looper
2340
has changed its behaviour such that it
2341
returns back after handling error. This
2342
makes it necessary that the jmpbuf be reset
2343
so that looper can proceede with the
2344
processing of execution tree. If this is
2345
not done then anymore errors will take the
2346
engine out of looper there by abruptly
2347
terminating the processing. */
2349
catch_disabled = false;
2350
tdbb->setRequest(request);
2351
fb_assert(request->req_caller == NULL);
2352
request->req_caller = old_request;
2355
/* The error is dealt with by the application, cleanup
2356
this block's savepoint. */
2358
if (transaction != dbb->dbb_sys_trans)
2360
for (const Savepoint* save_point = transaction->tra_save_point;
2361
save_point && count <= save_point->sav_number;
2362
save_point = transaction->tra_save_point)
2364
verb_cleanup(tdbb, transaction);
2372
node = node->nod_parent;
2375
/* If the application didn't have an error handler, then
2376
the error will still be pending. Undo the block by
2377
using its savepoint. */
2379
if (error_pending && transaction != dbb->dbb_sys_trans) {
2380
for (const Savepoint* save_point = transaction->tra_save_point;
2381
save_point && count <= save_point->sav_number;
2382
save_point = transaction->tra_save_point)
2384
++transaction->tra_save_point->sav_verb_count;
2385
verb_cleanup(tdbb, transaction);
2391
case jrd_req::req_return:
2392
if (transaction != dbb->dbb_sys_trans) {
2393
MOVE_FAST((SCHAR *) request + node->nod_impure,
2394
&count, sizeof(SLONG));
2395
for (const Savepoint* save_point = transaction->tra_save_point;
2396
save_point && count <= save_point->sav_number;
2397
save_point = transaction->tra_save_point)
2399
verb_cleanup(tdbb, transaction);
2403
node = node->nod_parent;
2407
case nod_error_handler:
2408
if (request->req_flags & req_error_handler && !error_pending)
2410
fb_assert(request->req_caller == old_request);
2411
request->req_caller = NULL;
2414
node = node->nod_parent;
2415
node = node->nod_parent;
2416
if (request->req_operation == jrd_req::req_unwind) {
2417
node = node->nod_parent;
2419
request->req_last_xcp.clear();
2423
switch (request->req_operation) {
2424
case jrd_req::req_evaluate:
2425
node = node->nod_arg[e_lbl_statement];
2428
case jrd_req::req_unwind:
2429
if ((request->req_label == (USHORT)(IPTR) node->nod_arg[e_lbl_label]) &&
2430
(request->req_flags & (req_leave | req_error_handler)))
2432
request->req_flags &= ~req_leave;
2433
request->req_operation = jrd_req::req_return;
2437
node = node->nod_parent;
2442
request->req_flags |= req_leave;
2443
request->req_operation = jrd_req::req_unwind;
2444
request->req_label = (USHORT)(IPTR) node->nod_arg[0];
2445
node = node->nod_parent;
2450
impure_state* impure = (impure_state*) ((SCHAR *) request + node->nod_impure);
2451
switch (request->req_operation) {
2452
case jrd_req::req_evaluate:
2453
impure->sta_state = 0;
2454
case jrd_req::req_return:
2455
case jrd_req::req_sync:
2456
if (impure->sta_state < node->nod_count) {
2457
request->req_operation = jrd_req::req_evaluate;
2458
node = node->nod_arg[impure->sta_state++];
2461
request->req_operation = jrd_req::req_return;
2463
node = node->nod_parent;
2469
switch (request->req_operation) {
2470
case jrd_req::req_evaluate:
2471
case jrd_req::req_return:
2472
node = node->nod_arg[0];
2473
request->req_operation = jrd_req::req_evaluate;
2477
node = node->nod_parent;
2482
if (request->req_operation == jrd_req::req_evaluate) {
2483
if (EVL_boolean(tdbb, node->nod_arg[e_if_boolean])) {
2484
node = node->nod_arg[e_if_true];
2485
request->req_operation = jrd_req::req_evaluate;
2488
else if (node->nod_arg[e_if_false]) {
2489
node = node->nod_arg[e_if_false];
2490
request->req_operation = jrd_req::req_evaluate;
2494
request->req_operation = jrd_req::req_return;
2497
node = node->nod_parent;
2502
impure_state* impure = (impure_state*) ((SCHAR *) request + node->nod_impure);
2503
if (request->req_operation == jrd_req::req_unwind) {
2504
node = node->nod_parent;
2506
else if ((request->req_operation == jrd_req::req_return) &&
2507
(!impure->sta_state) && (node->nod_arg[e_mod_sub_mod])) {
2510
which_mod_trig = PRE_TRIG;
2513
node = modify(tdbb, node, which_mod_trig);
2514
if (which_mod_trig == PRE_TRIG) {
2515
node = prev_node->nod_arg[e_mod_sub_mod];
2516
node->nod_parent = prev_node;
2518
if (top_node == prev_node && which_mod_trig == POST_TRIG) {
2520
which_mod_trig = ALL_TRIGS;
2523
request->req_operation = jrd_req::req_evaluate;
2528
node = modify(tdbb, node, ALL_TRIGS);
2529
if (!(prev_node->nod_arg[e_mod_sub_mod]) &&
2530
which_mod_trig == PRE_TRIG)
2532
which_mod_trig = POST_TRIG;
2539
request->req_operation = jrd_req::req_return;
2540
node = node->nod_parent;
2544
node = receive_msg(tdbb, node);
2548
if (request->req_operation == jrd_req::req_unwind) {
2549
node = node->nod_parent;
2552
exec_sql(tdbb, request, EVL_expr(tdbb, node->nod_arg[0]));
2553
if (request->req_operation == jrd_req::req_evaluate)
2554
request->req_operation = jrd_req::req_return;
2555
node = node->nod_parent;
2560
ExecuteStatement* impure =
2562
((SCHAR *) request + node->nod_impure);
2563
switch (request->req_operation) {
2564
case jrd_req::req_evaluate:
2565
impure->Open(tdbb, node->nod_arg[0], node->nod_count - 2,
2566
(!node->nod_arg[1]));
2567
case jrd_req::req_return:
2568
case jrd_req::req_sync:
2569
if (impure->Fetch(tdbb, &node->nod_arg[2])) {
2570
request->req_operation = jrd_req::req_evaluate;
2571
node = node->nod_arg[1];
2574
request->req_operation = jrd_req::req_return;
2576
// if have active opened request - close it
2577
impure->Close(tdbb);
2578
node = node->nod_parent;
2585
DeferredWork* work = DFW_post_work(transaction, dfw_post_event,
2586
EVL_expr(tdbb, node->nod_arg[0]), 0);
2587
if (node->nod_arg[1])
2588
DFW_post_work_arg(transaction, work,
2589
EVL_expr(tdbb, node->nod_arg[1]), 0);
2592
// for an autocommit transaction, events can be posted
2593
// without any updates
2595
if (transaction->tra_flags & TRA_autocommit)
2596
transaction->tra_flags |= TRA_perform_autocommit;
2598
if (request->req_operation == jrd_req::req_evaluate)
2599
request->req_operation = jrd_req::req_return;
2600
node = node->nod_parent;
2604
if (request->req_operation == jrd_req::req_evaluate)
2606
const Format* format = (Format*) node->nod_arg[e_msg_format];
2607
USHORT* impure_flags = (USHORT*) ((UCHAR *) request + (IPTR) node->nod_arg[e_msg_impure_flags]);
2608
memset(impure_flags, 0, sizeof(USHORT) * format->fmt_count);
2609
request->req_operation = jrd_req::req_return;
2611
node = node->nod_parent;
2615
node = stall(tdbb, node);
2619
node = selct(tdbb, node);
2623
node = send_msg(tdbb, node);
2628
impure_state* impure = (impure_state*) ((SCHAR *) request + node->nod_impure);
2629
if ((request->req_operation == jrd_req::req_return) &&
2630
(!impure->sta_state) && (node->nod_arg[e_sto_sub_store]))
2634
which_sto_trig = PRE_TRIG;
2637
node = store(tdbb, node, which_sto_trig);
2638
if (which_sto_trig == PRE_TRIG) {
2639
node = prev_node->nod_arg[e_sto_sub_store];
2640
node->nod_parent = prev_node;
2642
if (top_node == prev_node && which_sto_trig == POST_TRIG) {
2644
which_sto_trig = ALL_TRIGS;
2647
request->req_operation = jrd_req::req_evaluate;
2651
node = store(tdbb, node, ALL_TRIGS);
2652
if (!(prev_node->nod_arg[e_sto_sub_store]) &&
2653
which_sto_trig == PRE_TRIG)
2654
which_sto_trig = POST_TRIG;
2659
#ifdef SCROLLABLE_CURSORS
2661
node = seek_rse(tdbb, request, node);
2665
case nod_set_generator:
2666
if (request->req_operation == jrd_req::req_evaluate) {
2667
dsc* desc = EVL_expr(tdbb, node->nod_arg[e_gen_value]);
2668
DPM_gen_id(tdbb, (IPTR) node->nod_arg[e_gen_id], true,
2669
MOV_get_int64(desc, 0));
2670
request->req_operation = jrd_req::req_return;
2672
node = node->nod_parent;
2675
case nod_set_generator2:
2676
if (request->req_operation == jrd_req::req_evaluate) {
2677
dsc* desc = EVL_expr(tdbb, node->nod_arg[e_gen_value]);
2678
DPM_gen_id(tdbb, (IPTR) node->nod_arg[e_gen_id], true,
2679
MOV_get_int64(desc, 0));
2680
request->req_operation = jrd_req::req_return;
2682
node = node->nod_parent;
2686
if (request->req_operation == jrd_req::req_evaluate) {
2687
request->req_src_line = (USHORT) (IPTR) node->nod_arg[e_src_info_line];
2688
request->req_src_column = (USHORT) (IPTR) node->nod_arg[e_src_info_col];
2689
//request->req_operation = jrd_req::req_return;
2690
node = node->nod_arg[e_src_info_node];
2693
node = node->nod_parent;
2696
case nod_init_variable:
2697
if (request->req_operation == jrd_req::req_evaluate)
2699
const ItemInfo* itemInfo =
2700
reinterpret_cast<const ItemInfo*>(node->nod_arg[e_init_var_info]);
2703
jrd_nod* var_node = node->nod_arg[e_init_var_variable];
2704
DSC* to_desc = &((impure_value*) ((SCHAR *) request + var_node->nod_impure))->vlu_desc;
2706
to_desc->dsc_flags |= DSC_null;
2708
MapFieldInfo::ValueType fieldInfo;
2709
if (itemInfo->fullDomain &&
2710
request->req_map_field_info.get(itemInfo->field, fieldInfo) &&
2711
fieldInfo.defaultValue)
2713
dsc* value = EVL_expr(tdbb, fieldInfo.defaultValue);
2715
if (value && !(request->req_flags & req_null))
2717
to_desc->dsc_flags &= ~DSC_null;
2718
MOV_move(tdbb, value, to_desc);
2723
request->req_operation = jrd_req::req_return;
2725
node = node->nod_parent;
2729
BUGCHECK(168); /* msg 168 looper: action not yet implemented */
2733
catch (const Firebird::Exception& ex) {
2735
Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
2737
// Skip this handling for errors coming from the nested looper calls,
2738
// as they're already handled properly. The only need is to undo
2739
// our own savepoints.
2740
if (catch_disabled) {
2741
if (transaction != dbb->dbb_sys_trans) {
2742
for (const Savepoint* save_point = transaction->tra_save_point;
2743
((save_point) && (save_point_number <= save_point->sav_number));
2744
save_point = transaction->tra_save_point)
2746
++transaction->tra_save_point->sav_verb_count;
2747
verb_cleanup(tdbb, transaction);
2754
// If the database is already bug-checked, then get out
2755
if (dbb->dbb_flags & DBB_bugcheck) {
2756
Firebird::status_exception::raise(tdbb->tdbb_status_vector);
2759
// Since an error happened, the current savepoint needs to be undone
2760
if (transaction != dbb->dbb_sys_trans &&
2761
transaction->tra_save_point)
2763
++transaction->tra_save_point->sav_verb_count;
2764
verb_cleanup(tdbb, transaction);
2767
error_pending = true;
2768
catch_disabled = true;
2769
request->req_operation = jrd_req::req_unwind;
2770
request->req_label = 0;
2772
if (!(tdbb->tdbb_flags & TDBB_stack_trace_done) &&
2773
!(tdbb->tdbb_flags & TDBB_sys_error))
2775
stuff_stack_trace(request);
2776
tdbb->tdbb_flags |= TDBB_stack_trace_done;
2781
// If there is no node, assume we have finished processing the
2782
// request unless we are in the middle of processing an
2783
// asynchronous message
2786
#ifdef SCROLLABLE_CURSORS
2787
&& !(request->req_flags & req_async_processing)
2791
// Close active cursors
2792
if (request->req_cursors) {
2793
for (vec<RecordSource*>::iterator ptr = request->req_cursors->begin(),
2794
end = request->req_cursors->end(); ptr < end; ++ptr)
2797
RSE_close(tdbb, *ptr);
2801
request->req_flags &= ~(req_active | req_reserved);
2802
request->req_timestamp.invalidate();
2803
release_blobs(tdbb, request);
2806
request->req_next = node;
2807
tdbb->setTransaction(old_transaction);
2808
tdbb->setRequest(old_request);
2809
fb_assert(request->req_caller == old_request);
2810
request->req_caller = NULL;
2812
// In the case of a pending error condition (one which did not
2813
// result in a exception to the top of looper), we need to
2814
// delete the last savepoint
2816
if (error_pending) {
2817
if (transaction != dbb->dbb_sys_trans) {
2818
for (const Savepoint* save_point = transaction->tra_save_point;
2819
((save_point) && (save_point_number <= save_point->sav_number));
2820
save_point = transaction->tra_save_point)
2822
++transaction->tra_save_point->sav_verb_count;
2823
verb_cleanup(tdbb, transaction);
2830
// if the request was aborted, assume that we have already
2831
// longjmp'ed to the top of looper, and therefore that the
2832
// last savepoint has already been deleted
2834
if (request->req_flags & req_abort) {
2835
ERR_post(isc_req_sync, 0);
2842
static jrd_nod* modify(thread_db* tdbb, jrd_nod* node, SSHORT which_trig)
2844
/**************************************
2848
**************************************
2850
* Functional description
2851
* Execute a MODIFY statement.
2853
**************************************/
2855
Database* dbb = tdbb->getDatabase();
2856
BLKCHK(node, type_nod);
2858
jrd_req* request = tdbb->getRequest();
2859
jrd_tra* transaction = request->req_transaction;
2860
impure_state* impure = (impure_state*) ((SCHAR *) request + node->nod_impure);
2862
const SSHORT org_stream = (USHORT)(IPTR) node->nod_arg[e_mod_org_stream];
2863
record_param* org_rpb = &request->req_rpb[org_stream];
2864
jrd_rel* relation = org_rpb->rpb_relation;
2866
if (org_rpb->rpb_number.isBof() ||
2867
(!relation->rel_view_rse && !org_rpb->rpb_number.isValid()))
2869
ERR_post(isc_no_cur_rec, 0);
2872
const SSHORT new_stream = (USHORT)(IPTR) node->nod_arg[e_mod_new_stream];
2873
record_param* new_rpb = &request->req_rpb[new_stream];
2875
/* If the stream was sorted, the various fields in the rpb are
2876
probably junk. Just to make sure that everything is cool,
2877
refetch and release the record. */
2879
if (org_rpb->rpb_stream_flags & RPB_s_refetch) {
2880
VIO_refetch_record(tdbb, org_rpb, transaction);
2881
org_rpb->rpb_stream_flags &= ~RPB_s_refetch;
2884
switch (request->req_operation) {
2885
case jrd_req::req_evaluate:
2886
request->req_records_affected.bumpModified(false);
2889
case jrd_req::req_return:
2890
if (impure->sta_state == 1)
2892
impure->sta_state = 0;
2893
Record* org_record = org_rpb->rpb_record;
2894
const Record* new_record = new_rpb->rpb_record;
2895
MOVE_FASTER(new_record->rec_data, org_record->rec_data,
2896
new_record->rec_length);
2897
request->req_operation = jrd_req::req_evaluate;
2898
return node->nod_arg[e_mod_statement];
2901
if (impure->sta_state == 0)
2903
/* CVC: This call made here to clear the record in each NULL field and
2904
varchar field whose tail may contain garbage. */
2905
cleanup_rpb(tdbb, new_rpb);
2907
if (transaction != dbb->dbb_sys_trans)
2908
++transaction->tra_save_point->sav_verb_count;
2910
PreModifyEraseTriggers(tdbb, &relation->rel_pre_modify,
2911
which_trig, org_rpb, new_rpb->rpb_record,
2912
jrd_req::req_trigger_update);
2914
if (node->nod_arg[e_mod_validate]) {
2915
validate(tdbb, node->nod_arg[e_mod_validate]);
2918
if (relation->rel_file)
2920
EXT_modify(org_rpb, new_rpb, transaction);
2922
else if (relation->isVirtual()) {
2923
VirtualTable::modify(tdbb, org_rpb, new_rpb);
2925
else if (!relation->rel_view_rse)
2928
jrd_rel* bad_relation = 0;
2930
VIO_modify(tdbb, org_rpb, new_rpb, transaction);
2931
const IDX_E error_code =
2932
IDX_modify(tdbb, org_rpb, new_rpb, transaction,
2933
&bad_relation, &bad_index);
2936
ERR_duplicate_error(error_code, bad_relation, bad_index);
2941
if (relation->rel_post_modify &&
2942
which_trig != PRE_TRIG &&
2943
(trigger = execute_triggers(tdbb, &relation->rel_post_modify,
2944
org_rpb->rpb_record, new_rpb->rpb_record,
2945
jrd_req::req_trigger_update)))
2947
trigger_failure(tdbb, trigger);
2950
/* now call IDX_modify_check_constrints after all post modify triggers
2951
have fired. This is required for cascading referential integrity,
2952
which can be implemented as post_erase triggers */
2954
if (!relation->rel_file &&
2955
!relation->rel_view_rse &&
2956
!relation->isVirtual())
2959
jrd_rel* bad_relation = 0;
2961
const IDX_E error_code =
2962
IDX_modify_check_constraints(tdbb, org_rpb, new_rpb, transaction,
2963
&bad_relation, &bad_index);
2966
ERR_duplicate_error(error_code, bad_relation, bad_index);
2970
if (transaction != dbb->dbb_sys_trans) {
2971
--transaction->tra_save_point->sav_verb_count;
2974
/* CVC: Increment the counter only if we called VIO/EXT_modify() and
2975
we were successful. */
2976
if (!(request->req_view_flags & req_first_modify_return)) {
2977
request->req_view_flags |= req_first_modify_return;
2978
if (relation->rel_view_rse) {
2979
request->req_top_view_modify = relation;
2982
if (relation == request->req_top_view_modify) {
2983
if (which_trig == ALL_TRIGS || which_trig == POST_TRIG) {
2984
request->req_records_updated++;
2985
request->req_records_affected.bumpModified(true);
2988
else if (relation->rel_file || !relation->rel_view_rse) {
2989
request->req_records_updated++;
2990
request->req_records_affected.bumpModified(true);
2993
if (node->nod_arg[e_mod_statement2]) {
2994
impure->sta_state = 2;
2995
request->req_operation = jrd_req::req_evaluate;
2996
return node->nod_arg[e_mod_statement2];
3000
if (which_trig != PRE_TRIG) {
3001
Record* org_record = org_rpb->rpb_record;
3002
org_rpb->rpb_record = new_rpb->rpb_record;
3003
new_rpb->rpb_record = org_record;
3007
return node->nod_parent;
3010
impure->sta_state = 0;
3011
RLCK_reserve_relation(tdbb, transaction, relation, true, true);
3013
/* Fall thru on evaluate to set up for modify before executing sub-statement.
3014
This involves finding the appropriate format, making sure a record block
3015
exists for the stream and is big enough, and copying fields from the
3016
original record to the new record. */
3018
const Format* new_format = MET_current(tdbb, new_rpb->rpb_relation);
3019
Record* new_record = VIO_record(tdbb, new_rpb, new_format, tdbb->getDefaultPool());
3020
new_rpb->rpb_address = new_record->rec_data;
3021
new_rpb->rpb_length = new_format->fmt_length;
3022
new_rpb->rpb_format_number = new_format->fmt_version;
3024
const Format* org_format;
3025
Record* org_record = org_rpb->rpb_record;
3028
VIO_record(tdbb, org_rpb, new_format, tdbb->getDefaultPool());
3029
org_format = org_record->rec_format;
3030
org_rpb->rpb_address = org_record->rec_data;
3031
org_rpb->rpb_length = org_format->fmt_length;
3032
org_rpb->rpb_format_number = org_format->fmt_version;
3035
org_format = org_record->rec_format;
3037
/* Copy the original record to the new record. If the format hasn't changed,
3038
this is a simple move. If the format has changed, each field must be
3039
fetched and moved separately, remembering to set the missing flag. */
3041
if (new_format->fmt_version == org_format->fmt_version) {
3042
MOVE_FASTER(org_record->rec_data, new_rpb->rpb_address,
3043
new_rpb->rpb_length);
3046
DSC org_desc, new_desc;
3048
for (SSHORT i = 0; i < new_format->fmt_count; i++) {
3049
/* In order to "map a null to a default" value (in EVL_field()),
3050
* the relation block is referenced.
3051
* Reference: Bug 10116, 10424
3053
CLEAR_NULL(new_record, i);
3054
if (EVL_field(new_rpb->rpb_relation, new_record, i, &new_desc)) {
3056
(org_rpb->rpb_relation, org_record, i,
3059
MOV_move(tdbb, &org_desc, &new_desc);
3062
SET_NULL(new_record, i);
3063
if (new_desc.dsc_dtype) {
3064
UCHAR* p = new_desc.dsc_address;
3065
USHORT n = new_desc.dsc_length;
3068
} /* if (org_record) */
3069
} /* if (new_record) */
3070
} /* for (fmt_count) */
3073
if (node->nod_arg[e_mod_map_view]) {
3074
impure->sta_state = 1;
3075
return node->nod_arg[e_mod_map_view];
3078
return node->nod_arg[e_mod_statement];
3081
static jrd_nod* receive_msg(thread_db* tdbb, jrd_nod* node)
3083
/**************************************
3085
* r e c e i v e _ m s g
3087
**************************************
3089
* Functional description
3090
* Execute a RECEIVE statement. This can be entered either
3091
* with "req_evaluate" (ordinary receive statement) or
3092
* "req_proceed" (select statement). In the latter case,
3093
* the statement isn't every formalled evaluated.
3095
**************************************/
3097
jrd_req* request = tdbb->getRequest();
3098
BLKCHK(node, type_nod);
3100
switch (request->req_operation) {
3101
case jrd_req::req_evaluate:
3102
request->req_operation = jrd_req::req_receive;
3103
request->req_message = node->nod_arg[e_send_message];
3104
request->req_flags |= req_stall;
3107
case jrd_req::req_proceed:
3108
request->req_operation = jrd_req::req_evaluate;
3109
return (node->nod_arg[e_send_statement]);
3112
return (node->nod_parent);
3117
static void release_blobs(thread_db* tdbb, jrd_req* request)
3119
/**************************************
3121
* r e l e a s e _ b l o b s
3123
**************************************
3125
* Functional description
3126
* Release temporary blobs assigned by this request.
3128
**************************************/
3130
DEV_BLKCHK(request, type_req);
3132
jrd_tra* transaction = request->req_transaction;
3134
DEV_BLKCHK(transaction, type_tra);
3136
/* Release blobs bound to this request */
3138
if (request->req_blobs.getFirst())
3141
const ULONG blob_temp_id = request->req_blobs.current();
3142
if (transaction->tra_blobs.locate(blob_temp_id)) {
3143
BlobIndex *current = &transaction->tra_blobs.current();
3144
if (current->bli_materialized)
3146
request->req_blobs.fastRemove();
3147
current->bli_request = NULL;
3151
// Blob was created by request, is accounted for internal needs,
3152
// but is not materialized. Get rid of it.
3153
BLB_cancel(tdbb, current->bli_blob_object);
3154
// Since the routine above modifies req_blobs
3155
// we need to reestablish accessor position
3158
if (request->req_blobs.locate(Firebird::locGreat, blob_temp_id))
3164
// Blob accounting inconsistent
3167
if (!request->req_blobs.getNext())
3171
request->req_blobs.clear();
3173
/* Release arrays assigned by this request */
3175
for (ArrayField** array = &transaction->tra_arrays; *array;) {
3176
DEV_BLKCHK(*array, type_arr);
3177
if ((*array)->arr_request == request)
3178
BLB_release_array(*array);
3180
array = &(*array)->arr_next;
3186
static void release_proc_save_points(jrd_req* request)
3188
/**************************************
3190
* r e l e a s e _ p r o c _ s a v e _ p o i n t s
3192
**************************************
3194
* Functional description
3195
* Release temporary blobs assigned by this request.
3197
**************************************/
3198
Savepoint* sav_point = request->req_proc_sav_point;
3200
if (request->req_transaction) {
3202
Savepoint* const temp_sav_point = sav_point->sav_next;
3204
sav_point = temp_sav_point;
3207
request->req_proc_sav_point = NULL;
3211
#ifdef SCROLLABLE_CURSORS
3212
static jrd_nod* seek_rse(thread_db* tdbb, jrd_req* request, jrd_nod* node)
3214
/**************************************
3218
**************************************
3220
* Functional description
3221
* Execute a nod_seek, which specifies
3222
* a direction and offset in which to
3223
* scroll a record selection expression.
3225
**************************************/
3227
DEV_BLKCHK(node, type_nod);
3229
if (request->req_operation == jrd_req::req_proceed) {
3230
/* get input arguments */
3232
const dsc* desc = EVL_expr(tdbb, node->nod_arg[e_seek_direction]);
3233
const USHORT direction = (desc && !(request->req_flags & req_null)) ?
3234
MOV_get_long(desc, 0) : MAX_USHORT;
3236
desc = EVL_expr(tdbb, node->nod_arg[e_seek_offset]);
3237
const SLONG offset = (desc && !(request->req_flags & req_null)) ?
3238
MOV_get_long(desc, 0) : 0;
3240
RecordSelExpr* rse = (RecordSelExpr*) node->nod_arg[e_seek_rse];
3242
seek_rsb(tdbb, request, rse->rse_rsb, direction, offset);
3244
request->req_operation = jrd_req::req_return;
3247
return node->nod_parent;
3252
#ifdef SCROLLABLE_CURSORS
3253
static void seek_rsb(
3255
jrd_req* request, RecordSource* rsb, USHORT direction, SLONG offset)
3257
/**************************************
3261
**************************************
3263
* Functional description
3264
* Allow scrolling through a stream as defined
3265
* by the input rsb. Handles multiple seeking.
3266
* Uses RSE_get_record() to do the actual work.
3268
**************************************/
3270
DEV_BLKCHK(rsb, type_rsb);
3271
irsb* impure = (IRSB) ((UCHAR *) request + rsb->rsb_impure);
3273
/* look past any boolean to the actual stream */
3275
if (rsb->rsb_type == rsb_boolean) {
3276
seek_rsb(tdbb, request, rsb->rsb_next, direction, offset);
3278
/* set the backwards flag */
3280
const irsb* next_impure =
3281
(IRSB) ((UCHAR *) request + rsb->rsb_next->rsb_impure);
3283
if (next_impure->irsb_flags & irsb_last_backwards)
3284
impure->irsb_flags |= irsb_last_backwards;
3286
impure->irsb_flags &= ~irsb_last_backwards;
3290
/* do simple boundary checking for bof and eof */
3292
switch (direction) {
3294
if (impure->irsb_flags & irsb_eof)
3295
ERR_post(isc_stream_eof, 0);
3299
if (impure->irsb_flags & irsb_bof)
3300
ERR_post(isc_stream_bof, 0);
3303
case blr_bof_forward:
3304
case blr_eof_backward:
3308
// was: BUGCHECK(232);
3309
// replaced with this error to be consistent with find()
3310
ERR_post(isc_invalid_direction, 0);
3313
/* the actual offset to seek may be one less because the next time
3314
through the blr_for loop we will seek one record--flag the fact
3315
that a fetch is required on this stream in case it doesn't happen
3316
(for example when GPRE generates BLR which does not stall prior to
3317
the blr_for, as DSQL does) */
3320
switch (direction) {
3322
case blr_bof_forward:
3323
if (!(impure->irsb_flags & irsb_last_backwards)) {
3325
if (!(impure->irsb_flags & irsb_bof))
3326
request->req_flags |= req_fetch_required;
3331
case blr_eof_backward:
3332
if (impure->irsb_flags & irsb_last_backwards) {
3334
if (!(impure->irsb_flags & irsb_eof))
3335
request->req_flags |= req_fetch_required;
3340
/* now do the actual seek */
3342
switch (direction) {
3343
case blr_forward: /* go forward from the current location */
3345
/* the rsb_backwards flag is used to indicate the direction to seek in;
3346
this is sticky in the sense that after the user has seek'ed in the
3347
backward direction, the next retrieval from a blr_for loop will also
3348
be in the backward direction--this allows us to continue scrolling
3349
without constantly sending messages to the engine */
3351
impure->irsb_flags &= ~irsb_last_backwards;
3355
if (!(RSE_get_record(tdbb, rsb, RSE_get_next)))
3360
case blr_backward: /* go backward from the current location */
3362
impure->irsb_flags |= irsb_last_backwards;
3366
if (!(RSE_get_record(tdbb, rsb, RSE_get_next)))
3371
case blr_bof_forward: /* go forward from the beginning of the stream */
3373
RSE_close(tdbb, rsb);
3374
RSE_open(tdbb, rsb);
3376
impure->irsb_flags &= ~irsb_last_backwards;
3380
if (!(RSE_get_record(tdbb, rsb, RSE_get_next)))
3385
case blr_eof_backward: /* go backward from the end of the stream */
3387
RSE_close(tdbb, rsb);
3388
RSE_open(tdbb, rsb);
3390
/* if this is a stream type which uses bof and eof flags,
3391
reverse the sense of bof and eof in this case */
3393
if (impure->irsb_flags & irsb_bof) {
3394
impure->irsb_flags &= ~irsb_bof;
3395
impure->irsb_flags |= irsb_eof;
3398
impure->irsb_flags |= irsb_last_backwards;
3402
if (!(RSE_get_record(tdbb, rsb, RSE_get_next)))
3408
// Should never go here, because of the boundary
3409
// check above, but anyway...
3416
static jrd_nod* selct(thread_db* tdbb, jrd_nod* node)
3418
/**************************************
3422
**************************************
3424
* Functional description
3425
* Execute a SELECT statement. This is more than a little
3426
* obscure. We first set up the SELECT statement as the
3427
* "message" and stall on receive (waiting for user send).
3428
* EXE_send will then loop thru the sub-statements of select
3429
* looking for the appropriate RECEIVE statement. When (or if)
3430
* it finds it, it will set it up the next statement to be
3431
* executed. The RECEIVE, then, will be entered with the
3432
* operation "req_proceed."
3434
**************************************/
3436
jrd_req* request = tdbb->getRequest();
3437
BLKCHK(node, type_nod);
3439
switch (request->req_operation) {
3440
case jrd_req::req_evaluate:
3441
request->req_message = node;
3442
request->req_operation = jrd_req::req_receive;
3443
request->req_flags |= req_stall;
3447
return node->nod_parent;
3453
static jrd_nod* send_msg(thread_db* tdbb, jrd_nod* node)
3455
/**************************************
3459
**************************************
3461
* Functional description
3462
* Execute a SEND statement.
3464
**************************************/
3466
jrd_req* request = tdbb->getRequest();
3467
BLKCHK(node, type_nod);
3469
switch (request->req_operation) {
3470
case jrd_req::req_evaluate:
3471
return (node->nod_arg[e_send_statement]);
3473
case jrd_req::req_return:
3474
request->req_operation = jrd_req::req_send;
3475
request->req_message = node->nod_arg[e_send_message];
3476
request->req_flags |= req_stall;
3479
case jrd_req::req_proceed:
3480
request->req_operation = jrd_req::req_return;
3481
return node->nod_parent;
3484
return (node->nod_parent);
3489
static void set_error(thread_db* tdbb, const xcp_repeat* exception, jrd_nod* msg_node)
3491
/**************************************
3495
**************************************
3497
* Functional description
3498
* Set status vector according to specified error condition
3499
* and jump to handle error accordingly.
3501
**************************************/
3502
Firebird::MetaName name, relation_name;
3503
TEXT message[XCP_MESSAGE_LENGTH + 1];
3505
// since temp used as vary, we need size of vary::vary_length
3506
// (USHORT) extra chars
3507
TEXT temp[XCP_MESSAGE_LENGTH + sizeof(USHORT)];
3511
jrd_req* request = tdbb->getRequest();
3514
// retrieve the status vector and punt
3515
request->req_last_xcp.copyTo(tdbb->tdbb_status_vector);
3516
request->req_last_xcp.clear();
3524
const char* string = 0;
3525
// evaluate exception message and convert it to string
3526
DSC* desc = EVL_expr(tdbb, msg_node);
3527
if (desc && !(request->req_flags & req_null))
3529
length = MOV_make_string(desc,
3530
tdbb->getAttachment()->att_charset,
3532
reinterpret_cast<vary*>(temp),
3534
length = MIN(length, sizeof(message) - 1);
3536
/* dimitr: or should we throw an error here, i.e.
3537
replace the above assignment with the following lines:
3539
if (length > sizeof(message) - 1)
3540
ERR_post(isc_imp_exc, isc_arg_gds, isc_blktoobig, 0);
3543
memcpy(message, string, length);
3550
message[length] = 0;
3554
switch (exception->xcp_type)
3557
ERR_post(isc_sqlerr, isc_arg_number, exception->xcp_code, 0);
3560
if (exception->xcp_code == isc_check_constraint) {
3561
MET_lookup_cnstrt_for_trigger(tdbb, name, relation_name,
3562
request->req_trg_name);
3563
ERR_post(exception->xcp_code,
3564
isc_arg_string, ERR_cstring(name.c_str()),
3565
isc_arg_string, ERR_cstring(relation_name.c_str()), 0);
3568
ERR_post(exception->xcp_code, 0);
3571
// CVC: If we have the exception name, use it instead of the number.
3572
// Solves SF Bug #494981.
3573
MET_lookup_exception(tdbb, exception->xcp_code,
3574
name, temp, sizeof(temp));
3583
if (s && name.length())
3584
ERR_post(isc_except, isc_arg_number, exception->xcp_code,
3585
isc_arg_gds, isc_random, isc_arg_string, ERR_cstring(name.c_str()),
3586
isc_arg_gds, isc_random, isc_arg_string, ERR_cstring(s),
3589
ERR_post(isc_except, isc_arg_number, exception->xcp_code,
3590
isc_arg_gds, isc_random, isc_arg_string, ERR_cstring(s),
3592
else if (name.length())
3593
ERR_post(isc_except, isc_arg_number, exception->xcp_code,
3594
isc_arg_gds, isc_random, isc_arg_string, ERR_cstring(name.c_str()),
3597
ERR_post(isc_except, isc_arg_number, exception->xcp_code, 0);
3605
static jrd_nod* stall(thread_db* tdbb, jrd_nod* node)
3607
/**************************************
3611
**************************************
3613
* Functional description
3614
* Execute a stall statement.
3615
* This is like a blr_receive, except that there is no
3616
* need for a gds__send () from the user (i.e. EXE_send () in the engine).
3617
* A gds__receive () will unblock the user.
3619
**************************************/
3621
jrd_req* request = tdbb->getRequest();
3622
BLKCHK(node, type_nod);
3624
switch (request->req_operation) {
3625
case jrd_req::req_sync:
3626
return node->nod_parent;
3628
case jrd_req::req_proceed:
3629
request->req_operation = jrd_req::req_return;
3630
return node->nod_parent;
3633
request->req_message = node;
3634
request->req_operation = jrd_req::req_return;
3635
request->req_flags |= req_stall;
3641
static jrd_nod* store(thread_db* tdbb, jrd_nod* node, SSHORT which_trig)
3643
/**************************************
3647
**************************************
3649
* Functional description
3650
* Execute a STORE statement.
3652
**************************************/
3656
Database* dbb = tdbb->getDatabase();
3657
BLKCHK(node, type_nod);
3659
jrd_req* request = tdbb->getRequest();
3660
jrd_tra* transaction = request->req_transaction;
3661
impure_state* impure = (impure_state*) ((SCHAR *) request + node->nod_impure);
3662
SSHORT stream = (USHORT)(IPTR) node->nod_arg[e_sto_relation]->nod_arg[e_rel_stream];
3663
record_param* rpb = &request->req_rpb[stream];
3664
jrd_rel* relation = rpb->rpb_relation;
3666
switch (request->req_operation) {
3667
case jrd_req::req_evaluate:
3668
if (request->req_records_affected.isReadOnly() &&
3669
!request->req_records_affected.hasCursor())
3671
request->req_records_affected.clear();
3673
request->req_records_affected.bumpModified(false);
3674
impure->sta_state = 0;
3675
RLCK_reserve_relation(tdbb, transaction, relation, true, true);
3678
case jrd_req::req_return:
3679
if (impure->sta_state)
3680
return node->nod_parent;
3682
if (transaction != dbb->dbb_sys_trans)
3683
++transaction->tra_save_point->sav_verb_count;
3685
if (relation->rel_pre_store &&
3686
(which_trig != POST_TRIG) &&
3687
(trigger = execute_triggers(tdbb, &relation->rel_pre_store,
3688
NULL, rpb->rpb_record,
3689
jrd_req::req_trigger_insert)))
3691
trigger_failure(tdbb, trigger);
3694
if (node->nod_arg[e_sto_validate]) {
3695
validate(tdbb, node->nod_arg[e_sto_validate]);
3698
/* For optimum on-disk record compression, zero all unassigned
3699
fields. In addition, zero the tail of assigned varying fields
3700
so that previous remnants don't defeat compression efficiency. */
3702
/* CVC: The code that was here was moved to its own routine: cleanup_rpb()
3703
and replaced by the call shown below. */
3705
cleanup_rpb(tdbb, rpb);
3707
if (relation->rel_file) {
3708
EXT_store(tdbb, rpb, transaction);
3710
else if (relation->isVirtual()) {
3711
VirtualTable::store(tdbb, rpb);
3713
else if (!relation->rel_view_rse)
3716
jrd_rel* bad_relation = 0;
3718
VIO_store(tdbb, rpb, transaction);
3719
const IDX_E error_code =
3720
IDX_store(tdbb, rpb, transaction,
3721
&bad_relation, &bad_index);
3724
ERR_duplicate_error(error_code, bad_relation, bad_index);
3728
rpb->rpb_number.setValid(true);
3730
if (relation->rel_post_store &&
3731
(which_trig != PRE_TRIG) &&
3732
(trigger = execute_triggers(tdbb, &relation->rel_post_store,
3733
NULL, rpb->rpb_record,
3734
jrd_req::req_trigger_insert)))
3736
trigger_failure(tdbb, trigger);
3739
/* CVC: Increment the counter only if we called VIO/EXT_store() and
3740
we were successful. */
3741
if (!(request->req_view_flags & req_first_store_return)) {
3742
request->req_view_flags |= req_first_store_return;
3743
if (relation->rel_view_rse) {
3744
request->req_top_view_store = relation;
3747
if (relation == request->req_top_view_store) {
3748
if (which_trig == ALL_TRIGS || which_trig == POST_TRIG) {
3749
request->req_records_inserted++;
3750
request->req_records_affected.bumpModified(true);
3753
else if (relation->rel_file || !relation->rel_view_rse) {
3754
request->req_records_inserted++;
3755
request->req_records_affected.bumpModified(true);
3758
if (transaction != dbb->dbb_sys_trans) {
3759
--transaction->tra_save_point->sav_verb_count;
3762
if (node->nod_arg[e_sto_statement2]) {
3763
impure->sta_state = 1;
3764
request->req_operation = jrd_req::req_evaluate;
3765
return node->nod_arg[e_sto_statement2];
3769
return node->nod_parent;
3772
/* Fall thru on evaluate to set up for store before executing sub-statement.
3773
This involves finding the appropriate format, making sure a record block
3774
exists for the stream and is big enough, and initialize all null flags
3777
const Format* format = MET_current(tdbb, relation);
3778
Record* record = VIO_record(tdbb, rpb, format, tdbb->getDefaultPool());
3780
rpb->rpb_address = record->rec_data;
3781
rpb->rpb_length = format->fmt_length;
3782
rpb->rpb_format_number = format->fmt_version;
3784
/* CVC: This small block added by Ann Harrison to
3785
start with a clean empty buffer and so avoid getting
3786
new record buffer with misleading information. Fixes
3787
bug with incorrect blob sharing during insertion in
3788
a stored procedure. */
3790
memset(record->rec_data, 0, rpb->rpb_length);
3792
/* Initialize all fields to missing */
3794
SSHORT n = (format->fmt_count + 7) >> 3;
3796
memset(record->rec_data, 0xff, n);
3799
return node->nod_arg[e_sto_statement];
3803
static bool test_and_fixup_error(thread_db* tdbb, const PsqlException* conditions,
3806
/**************************************
3808
* t e s t _ a n d _ f i x u p _ e r r o r
3810
**************************************
3812
* Functional description
3813
* Test for match of current state with list of error conditions.
3814
* Fix type and code of the exception.
3816
**************************************/
3819
if (tdbb->tdbb_flags & TDBB_sys_error)
3822
ISC_STATUS* status_vector = tdbb->tdbb_status_vector;
3823
const SSHORT sqlcode = gds__sqlcode(status_vector);
3827
for (USHORT i = 0; i < conditions->xcp_count; i++)
3829
switch (conditions->xcp_rpt[i].xcp_type)
3832
if (sqlcode == conditions->xcp_rpt[i].xcp_code)
3839
if (status_vector[1] == conditions->xcp_rpt[i].xcp_code)
3847
// Look at set_error() routine to understand how the
3848
// exception ID info is encoded inside the status vector.
3849
if ((status_vector[1] == isc_except) &&
3850
(status_vector[3] == conditions->xcp_rpt[i].xcp_code))
3868
request->req_last_xcp.init(status_vector);
3869
status_vector[0] = 0;
3870
status_vector[1] = 0;
3879
static void trigger_failure(thread_db* tdbb, jrd_req* trigger)
3881
/**************************************
3883
* t r i g g e r _ f a i l u r e
3885
**************************************
3887
* Functional description
3888
* Trigger failed, report error.
3890
**************************************/
3893
EXE_unwind(tdbb, trigger);
3895
trigger->req_attachment = NULL;
3896
trigger->req_flags &= ~req_in_use;
3897
trigger->req_timestamp.invalidate();
3899
if (trigger->req_flags & req_leave)
3901
trigger->req_flags &= ~req_leave;
3903
MET_trigger_msg(tdbb, trigger->req_trg_name, trigger->req_label);
3906
if (trigger->req_flags & req_sys_trigger)
3908
ISC_STATUS code = PAR_symbol_to_gdscode(msg);
3911
ERR_post(isc_integ_fail,
3912
isc_arg_number, (SLONG) trigger->req_label,
3913
isc_arg_gds, code, 0);
3916
ERR_post(isc_integ_fail,
3917
isc_arg_number, (SLONG) trigger->req_label,
3918
isc_arg_gds, isc_random, isc_arg_string, msg, 0);
3922
ERR_post(isc_integ_fail, isc_arg_number,
3923
(SLONG) trigger->req_label, 0);
3933
static void validate(thread_db* tdbb, jrd_nod* list)
3935
/**************************************
3939
**************************************
3941
* Functional description
3942
* Execute a list of validation expressions.
3944
**************************************/
3947
BLKCHK(list, type_nod);
3949
jrd_nod** ptr1 = list->nod_arg;
3950
for (const jrd_nod* const* const end = ptr1 + list->nod_count;
3953
jrd_req* request = tdbb->getRequest();
3954
if (!EVL_boolean(tdbb, (*ptr1)->nod_arg[e_val_boolean]) &&
3955
!(request->req_flags & req_null))
3957
/* Validation error -- report result */
3961
jrd_nod* node = (*ptr1)->nod_arg[e_val_value];
3962
const dsc* desc = EVL_expr(tdbb, node);
3963
const USHORT length = (desc && !(request->req_flags & req_null)) ?
3964
MOV_make_string(desc, ttype_dynamic, &value,
3965
reinterpret_cast<vary*>(temp),
3968
if (!desc || (request->req_flags & req_null))
3970
value = NULL_STRING_MARK;
3978
value = ERR_string(value, length);
3981
const TEXT* name = 0;
3982
if (node->nod_type == nod_field)
3984
const USHORT stream = (USHORT)(IPTR) node->nod_arg[e_fld_stream];
3985
const USHORT id = (USHORT)(IPTR) node->nod_arg[e_fld_id];
3986
const jrd_rel* relation = request->req_rpb[stream].rpb_relation;
3988
const jrd_fld* field;
3989
const vec<jrd_fld*>* vector = relation->rel_fields;
3990
if (vector && id < vector->count() &&
3991
(field = (*vector)[id]))
3993
name = ERR_cstring(field->fld_name.c_str());
3999
name = UNKNOWN_STRING_MARK;
4002
ERR_post(isc_not_valid, isc_arg_string, name,
4003
isc_arg_string, value, 0);
4009
inline void verb_cleanup(thread_db* tdbb, jrd_tra* transaction)
4011
/**************************************
4013
* v e r b _ c l e a n u p
4015
**************************************
4017
* Functional description
4018
* If an error happens during the backout of a savepoint, then the transaction
4019
* must be marked 'dead' because that is the only way to clean up after a
4020
* failed backout. The easiest way to do this is to kill the application
4021
* by calling bugcheck.
4023
**************************************/
4025
VIO_verb_cleanup(tdbb, transaction);
4027
catch (const Firebird::Exception&) {
4028
if (tdbb->getDatabase()->dbb_flags & DBB_bugcheck) {
4029
Firebird::status_exception::raise(tdbb->tdbb_status_vector);
4031
BUGCHECK(290); // msg 290 error during savepoint backout