~ubuntu-branches/ubuntu/karmic/firebird2.1/karmic

« back to all changes in this revision

Viewing changes to src/jrd/exe.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Damyan Ivanov
  • Date: 2008-05-26 23:59:25 UTC
  • Revision ID: james.westby@ubuntu.com-20080526235925-2pnqj6nxpppoeaer
Tags: upstream-2.1.0.17798-0.ds2
ImportĀ upstreamĀ versionĀ 2.1.0.17798-0.ds2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *      PROGRAM:        JRD Access Method
 
3
 *      MODULE:         exe.cpp
 
4
 *      DESCRIPTION:    Statement execution
 
5
 *
 
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
 
10
 *
 
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.
 
15
 *
 
16
 * The Original Code was created by Inprise Corporation
 
17
 * and its predecessors. Portions created by Inprise Corporation are
 
18
 * Copyright (C) Inprise Corporation.
 
19
 *
 
20
 * All Rights Reserved.
 
21
 * Contributor(s): ______________________________________.
 
22
 *
 
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
 
35
 *
 
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
 
43
 *
 
44
 */
 
45
 
 
46
#include "firebird.h"
 
47
#if TIME_WITH_SYS_TIME
 
48
# include <sys/time.h>
 
49
# include <time.h>
 
50
#else
 
51
# if HAVE_SYS_TIME_H
 
52
#  include <sys/time.h>
 
53
# else
 
54
#  include <time.h>
 
55
# endif
 
56
#endif
 
57
 
 
58
#include "../jrd/common.h"
 
59
#include "../jrd/ibsetjmp.h"
 
60
#include <string.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"
 
88
 
 
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"
 
95
 
 
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"
 
101
 
 
102
#include "../jrd/execute_statement.h"
 
103
#include "../dsql/dsql_proto.h"
 
104
#include "../jrd/rpb_chain.h"
 
105
#include "../jrd/VirtualTable.h"
 
106
 
 
107
 
 
108
using namespace Jrd;
 
109
 
 
110
// AffectedRows class implementation
 
111
 
 
112
AffectedRows::AffectedRows()
 
113
{
 
114
        clear();
 
115
}
 
116
 
 
117
void AffectedRows::clear()
 
118
{
 
119
        writeFlag = false;
 
120
        fetchedRows = modifiedRows = 0;
 
121
}
 
122
 
 
123
void AffectedRows::bumpFetched()
 
124
{
 
125
        fetchedRows++;
 
126
}
 
127
 
 
128
void AffectedRows::bumpModified(bool increment)
 
129
{
 
130
        if (increment) {
 
131
                modifiedRows++;
 
132
        }
 
133
        else {
 
134
                writeFlag = true;
 
135
        }
 
136
}
 
137
 
 
138
bool AffectedRows::isReadOnly() const
 
139
{
 
140
        return !writeFlag;
 
141
}
 
142
 
 
143
bool AffectedRows::hasCursor() const
 
144
{
 
145
        return (fetchedRows > 0);
 
146
}
 
147
 
 
148
int AffectedRows::getCount() const
 
149
{
 
150
        return writeFlag ? modifiedRows : fetchedRows;
 
151
}
 
152
 
 
153
// StatusXcp class implementation
 
154
 
 
155
StatusXcp::StatusXcp()
 
156
{
 
157
        clear();
 
158
}
 
159
 
 
160
void StatusXcp::clear()
 
161
{
 
162
        status[0] = isc_arg_gds;
 
163
        status[1] = FB_SUCCESS;
 
164
        status[2] = isc_arg_end;
 
165
}
 
166
 
 
167
void StatusXcp::init(const ISC_STATUS* vector)
 
168
{
 
169
        memcpy(status, vector, sizeof(ISC_STATUS_ARRAY));
 
170
}
 
171
 
 
172
void StatusXcp::copyTo(ISC_STATUS* vector) const
 
173
{
 
174
        memcpy(vector, status, sizeof(ISC_STATUS_ARRAY));
 
175
}
 
176
 
 
177
bool StatusXcp::success() const
 
178
{
 
179
        return (status[1] == FB_SUCCESS);
 
180
}
 
181
 
 
182
SLONG StatusXcp::as_gdscode() const
 
183
{
 
184
        return status[1];
 
185
}
 
186
 
 
187
SLONG StatusXcp::as_sqlcode() const
 
188
{
 
189
        return gds__sqlcode(status);
 
190
}
 
191
 
 
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);
 
207
#endif
 
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*);
 
220
 
 
221
 
 
222
/* macro definitions */
 
223
 
 
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;
 
228
#else
 
229
const int MAX_CLONES    = 1000;
 
230
#endif
 
231
 
 
232
const int ALL_TRIGS     = 0;
 
233
const int PRE_TRIG      = 1;
 
234
const int POST_TRIG     = 2;
 
235
 
 
236
const size_t MAX_STACK_TRACE = 2048;
 
237
 
 
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
 
245
   locking a record */
 
246
 
 
247
const int RECORD_LOCK_CHECK_INTERVAL    = 10;
 
248
 
 
249
 
 
250
void EXE_assignment(thread_db* tdbb, jrd_nod* node)
 
251
{
 
252
/**************************************
 
253
 *
 
254
 *      E X E _ a s s i g n m e n t
 
255
 *
 
256
 **************************************
 
257
 *
 
258
 * Functional description
 
259
 *      Perform an assignment
 
260
 *
 
261
 **************************************/
 
262
        DEV_BLKCHK(node, type_nod);
 
263
 
 
264
        SET_TDBB(tdbb);
 
265
        jrd_req* request = tdbb->getRequest();
 
266
        BLKCHK(node, type_nod);
 
267
 
 
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]);
 
271
 
 
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]);
 
274
 
 
275
        request->req_operation = jrd_req::req_return;
 
276
}
 
277
 
 
278
 
 
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)
 
281
{
 
282
/**************************************
 
283
 *
 
284
 *      E X E _ a s s i g n m e n t
 
285
 *
 
286
 **************************************
 
287
 *
 
288
 * Functional description
 
289
 *      Perform an assignment
 
290
 *
 
291
 **************************************/
 
292
        DEV_BLKCHK(node, type_nod);
 
293
 
 
294
        SET_TDBB(tdbb);
 
295
        jrd_req* request = tdbb->getRequest();
 
296
 
 
297
        // Get descriptors of receiving and sending fields/parameters, variables, etc.
 
298
 
 
299
        dsc* missing = NULL;
 
300
        if (missing_node) {
 
301
                missing = EVL_expr(tdbb, missing_node);
 
302
        }
 
303
 
 
304
        // Get descriptor of target field/parameter/variable, etc.
 
305
        DSC* to_desc = EVL_assign_to(tdbb, to);
 
306
 
 
307
        request->req_flags &= ~req_null;
 
308
 
 
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.
 
312
        if (!to_desc)
 
313
                return;
 
314
 
 
315
        SSHORT null = from_null ? -1 : 0;
 
316
 
 
317
        if (!null && missing && MOV_compare(missing, from_desc) == 0) {
 
318
                null = -1;
 
319
        }
 
320
 
 
321
        USHORT* impure_flags = NULL;
 
322
 
 
323
        switch (to->nod_type)
 
324
        {
 
325
                case nod_variable:
 
326
                        if (to->nod_arg[e_var_info])
 
327
                        {
 
328
                                EVL_validate(tdbb, 
 
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);
 
332
                        }
 
333
                        impure_flags = &((impure_value*) ((SCHAR *) request +
 
334
                                to->nod_arg[e_var_variable]->nod_impure))->vlu_flags;
 
335
                        break;
 
336
 
 
337
                case nod_argument:
 
338
                        if (to->nod_arg[e_arg_info])
 
339
                        {
 
340
                                EVL_validate(tdbb,
 
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);
 
345
                        }
 
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]));
 
349
                        break;
 
350
        }
 
351
 
 
352
        if (impure_flags != NULL)
 
353
                *impure_flags |= VLU_checked;
 
354
 
 
355
        // If the value is non-missing, move/convert it.  Otherwise fill the
 
356
        // field with appropriate nulls.
 
357
        dsc temp;
 
358
 
 
359
        if (!null)
 
360
        {
 
361
                // if necessary and appropriate, use the indicator variable
 
362
 
 
363
                if (to->nod_type == nod_argument && to->nod_arg[e_arg_indicator])
 
364
                {
 
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);
 
368
                        temp.dsc_scale    = 0;
 
369
                        temp.dsc_sub_type = 0;
 
370
 
 
371
                        SSHORT len;
 
372
 
 
373
                        if ((from_desc->dsc_dtype <= dtype_varying) &&
 
374
                                (to_desc->dsc_dtype <= dtype_varying) &&
 
375
                                (TEXT_LEN(from_desc) > TEXT_LEN(to_desc)))
 
376
                        {
 
377
                                len = TEXT_LEN(from_desc);
 
378
                        }
 
379
                        else {
 
380
                                len = 0;
 
381
                        }
 
382
 
 
383
                        temp.dsc_address = (UCHAR *) &len;
 
384
                        MOV_move(tdbb, &temp, indicator);
 
385
 
 
386
                        if (len) {
 
387
                                temp = *from_desc;
 
388
                                temp.dsc_length = TEXT_LEN(to_desc);
 
389
                                if (temp.dsc_dtype == dtype_cstring) {
 
390
                                        temp.dsc_length += 1;
 
391
                                }
 
392
                                else if (temp.dsc_dtype == dtype_varying) {
 
393
                                        temp.dsc_length += 2;
 
394
                                }
 
395
                                from_desc = &temp;
 
396
                        }
 
397
                }
 
398
 
 
399
                // Validate range for datetime values
 
400
 
 
401
                if (DTYPE_IS_DATE(from_desc->dsc_dtype)) {
 
402
                        Firebird::TimeStamp ts(true);
 
403
                        switch (from_desc->dsc_dtype) {
 
404
                                case dtype_sql_date:
 
405
                                        ts.value().timestamp_date =
 
406
                                                *(GDS_DATE*) from_desc->dsc_address;
 
407
                                        break;
 
408
                                case dtype_sql_time:
 
409
                                        ts.value().timestamp_time =
 
410
                                                *(GDS_TIME*) from_desc->dsc_address;
 
411
                                        break;
 
412
                                case dtype_timestamp:
 
413
                                        ts.value() = *(GDS_TIMESTAMP*) from_desc->dsc_address;
 
414
                                        break;
 
415
                                default:
 
416
                                        fb_assert(false);
 
417
                        }
 
418
 
 
419
                        if (!ts.isRangeValid()) {
 
420
                                ERR_post(isc_date_range_exceeded, 0);
 
421
                        }
 
422
                }
 
423
 
 
424
                if (DTYPE_IS_BLOB_OR_QUAD(from_desc->dsc_dtype) ||
 
425
                        DTYPE_IS_BLOB_OR_QUAD(to_desc->dsc_dtype))
 
426
                {
 
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);
 
430
                }
 
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)
 
434
                {
 
435
                        *((SSHORT *) to_desc->dsc_address) =
 
436
                                *((SSHORT *) from_desc->dsc_address);
 
437
                }
 
438
                else if (from_desc->dsc_dtype == dtype_long)
 
439
                {
 
440
                        *((SLONG *) to_desc->dsc_address) =
 
441
                                *((SLONG *) from_desc->dsc_address);
 
442
                }
 
443
                else if (from_desc->dsc_dtype == dtype_int64)
 
444
                {
 
445
                        *((SINT64 *) to_desc->dsc_address) =
 
446
                                *((SINT64 *) from_desc->dsc_address);
 
447
                }
 
448
                else if (((U_IPTR) from_desc->dsc_address & (ALIGNMENT - 1)) ||
 
449
                                 ((U_IPTR) to_desc->dsc_address & (ALIGNMENT - 1)))
 
450
                {
 
451
                        MOVE_FAST(from_desc->dsc_address, to_desc->dsc_address,
 
452
                                          from_desc->dsc_length);
 
453
                }
 
454
                else
 
455
                {
 
456
                        MOVE_FASTER(from_desc->dsc_address, to_desc->dsc_address,
 
457
                                                from_desc->dsc_length);
 
458
                }
 
459
                to_desc->dsc_flags &= ~DSC_null;
 
460
        }
 
461
        else if (missing2_node && (missing = EVL_expr(tdbb, missing2_node)))
 
462
        {
 
463
                MOV_move(tdbb, missing, to_desc);
 
464
                to_desc->dsc_flags |= DSC_null;
 
465
        }
 
466
        else
 
467
        {
 
468
                USHORT l = to_desc->dsc_length;
 
469
                UCHAR* p = to_desc->dsc_address;
 
470
                switch (to_desc->dsc_dtype) {
 
471
                case dtype_text:
 
472
                        // YYY - not necessarily the right thing to do
 
473
                        // for text formats that don't have trailing spaces
 
474
                        if (l) {
 
475
                                const CHARSET_ID chid = DSC_GET_CHARSET(to_desc);
 
476
                                /*
 
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);
 
482
                                */
 
483
                                const char pad = chid == ttype_binary ? '\0' : ' ';
 
484
                                memset(p, pad, l);
 
485
                        }
 
486
                        break;
 
487
 
 
488
                case dtype_cstring:
 
489
                        *p = 0;
 
490
                        break;
 
491
 
 
492
                case dtype_varying:
 
493
                        *(SSHORT *) p = 0;
 
494
                        break;
 
495
 
 
496
                default:
 
497
                        memset(p, 0, l);
 
498
                        break;
 
499
                }
 
500
                to_desc->dsc_flags |= DSC_null;
 
501
        }
 
502
 
 
503
        // Handle the null flag as appropriate for fields and message arguments.
 
504
 
 
505
        if (to->nod_type == nod_field)
 
506
        {
 
507
                const SSHORT id = (USHORT)(IPTR) to->nod_arg[e_fld_id];
 
508
                Record* record =
 
509
                        request->req_rpb[(int) (IPTR) to->nod_arg[e_fld_stream]].rpb_record;
 
510
                if (null) {
 
511
                        SET_NULL(record, id);
 
512
                }
 
513
                else {
 
514
                        CLEAR_NULL(record, id);
 
515
                }
 
516
        }
 
517
        else if (to->nod_type == nod_argument && to->nod_arg[e_arg_flag])
 
518
        {
 
519
                to_desc = EVL_assign_to(tdbb, to->nod_arg[e_arg_flag]);
 
520
 
 
521
                // If the null flag is a string with an effective length of one,
 
522
                // then -1 will not fit.  Therefore, store 1 instead.
 
523
 
 
524
                if (null &&
 
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 :
 
529
                                                                                                        3)))
 
530
                {
 
531
                        null = 1;
 
532
                }
 
533
 
 
534
                temp.dsc_dtype = dtype_short;
 
535
                temp.dsc_length = sizeof(SSHORT);
 
536
                temp.dsc_scale = 0;
 
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);
 
543
                }
 
544
        }
 
545
}
 
546
 
 
547
 
 
548
void EXE_execute_db_triggers(thread_db* tdbb,
 
549
                                                         jrd_tra* transaction,
 
550
                                                         enum jrd_req::req_ta trigger_action)
 
551
{
 
552
/**************************************
 
553
 *
 
554
 *      E X E _ e x e c u t e _ d b _ t r i g g e r s
 
555
 *
 
556
 **************************************
 
557
 *
 
558
 * Functional description
 
559
 *      Execute database triggers
 
560
 *
 
561
 **************************************/
 
562
        // do nothing if user doesn't want database triggers
 
563
        if (tdbb->getAttachment()->att_flags & ATT_no_db_triggers)
 
564
                return;
 
565
 
 
566
        int type = 0;
 
567
 
 
568
        switch (trigger_action)
 
569
        {
 
570
                case jrd_req::req_trigger_connect:
 
571
                        type = DB_TRIGGER_CONNECT;
 
572
                        break;
 
573
 
 
574
                case jrd_req::req_trigger_disconnect:
 
575
                        type = DB_TRIGGER_DISCONNECT;
 
576
                        break;
 
577
 
 
578
                case jrd_req::req_trigger_trans_start:
 
579
                        type = DB_TRIGGER_TRANS_START;
 
580
                        break;
 
581
 
 
582
                case jrd_req::req_trigger_trans_commit:
 
583
                        type = DB_TRIGGER_TRANS_COMMIT;
 
584
                        break;
 
585
 
 
586
                case jrd_req::req_trigger_trans_rollback:
 
587
                        type = DB_TRIGGER_TRANS_ROLLBACK;
 
588
                        break;
 
589
 
 
590
                default:
 
591
                        fb_assert(false);
 
592
                        return;
 
593
        }
 
594
 
 
595
        jrd_req* trigger = NULL;
 
596
 
 
597
        if (tdbb->getDatabase()->dbb_triggers[type])
 
598
        {
 
599
                jrd_tra* old_transaction = tdbb->getTransaction();
 
600
                tdbb->setTransaction(transaction);
 
601
 
 
602
                try
 
603
                {
 
604
                        trigger = execute_triggers(tdbb, &tdbb->getDatabase()->dbb_triggers[type],
 
605
                                NULL, NULL, trigger_action);
 
606
                        tdbb->setTransaction(old_transaction);
 
607
                }
 
608
                catch (...)
 
609
                {
 
610
                        tdbb->setTransaction(old_transaction);
 
611
                        throw;
 
612
                }
 
613
        }
 
614
 
 
615
        if (trigger)
 
616
                trigger_failure(tdbb, trigger);
 
617
}
 
618
 
 
619
 
 
620
jrd_req* EXE_find_request(thread_db* tdbb, jrd_req* request, bool validate)
 
621
{
 
622
/**************************************
 
623
 *
 
624
 *      E X E _ f i n d _ r e q u e s t
 
625
 *
 
626
 **************************************
 
627
 *
 
628
 * Functional description
 
629
 *      Find an inactive incarnation of a trigger request.  If necessary,
 
630
 *      clone it.
 
631
 *
 
632
 **************************************/
 
633
        DEV_BLKCHK(request, type_req);
 
634
 
 
635
        SET_TDBB(tdbb);
 
636
        Database* dbb = tdbb->getDatabase();
 
637
 
 
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
 
640
 * figure it out
 
641
 */
 
642
        if (!request)
 
643
                BUGCHECK /* REQUEST */ (167);   /* msg 167 invalid SEND request */
 
644
 
 
645
        dbb->dbb_mutexes[DBB_MUTX_clone].enter();
 
646
        jrd_req* clone = NULL;
 
647
        USHORT count = 0;
 
648
        if (!(request->req_flags & req_in_use))
 
649
                clone = request;
 
650
        else {
 
651
                if (request->req_attachment == tdbb->getAttachment())
 
652
                        count++;
 
653
 
 
654
                /* Request exists and is in use.  Search clones for one in use by
 
655
                   this attachment. If not found, return first inactive request. */
 
656
 
 
657
                vec<jrd_req*>* vector = request->req_sub_requests;
 
658
                const USHORT clones = (vector) ? (vector->count() - 1) : 0;
 
659
 
 
660
                USHORT n;
 
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)) {
 
665
                                        clone = next;
 
666
                                        break;
 
667
                                }
 
668
                                else
 
669
                                        count++;
 
670
                        }
 
671
                        else if (!(next->req_flags & req_in_use) && !clone)
 
672
                                clone = next;
 
673
                }
 
674
 
 
675
                if (count > MAX_CLONES) {
 
676
                        dbb->dbb_mutexes[DBB_MUTX_clone].leave();
 
677
                        ERR_post(isc_req_max_clones_exceeded, 0);
 
678
                }
 
679
                if (!clone)
 
680
                        clone = CMP_clone_request(tdbb, request, n, validate);
 
681
        }
 
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();
 
686
        return clone;
 
687
}
 
688
 
 
689
 
 
690
void EXE_receive(thread_db*             tdbb,
 
691
                                 jrd_req*               request,
 
692
                                 USHORT         msg,
 
693
                                 USHORT         length,
 
694
                                 UCHAR*         buffer,
 
695
                                 bool           top_level)
 
696
{
 
697
/**************************************
 
698
 *
 
699
 *      E X E _ r e c e i v e
 
700
 *
 
701
 **************************************
 
702
 *
 
703
 * Functional description
 
704
 *      Move a message from JRD to the host program.  This corresponds to
 
705
 *      a JRD BLR/jrd_nod* send.
 
706
 *
 
707
 **************************************/
 
708
        SET_TDBB(tdbb);
 
709
 
 
710
        DEV_BLKCHK(request, type_req);
 
711
 
 
712
        if (--tdbb->tdbb_quantum < 0)
 
713
                JRD_reschedule(tdbb, 0, true);
 
714
 
 
715
        jrd_tra* transaction = request->req_transaction;
 
716
 
 
717
        if (!(request->req_flags & req_active)) {
 
718
                ERR_post(isc_req_sync, 0);
 
719
        }
 
720
 
 
721
        if (request->req_flags & req_proc_fetch)
 
722
        {
 
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. */
 
730
 
 
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;
 
734
 
 
735
                if (!transaction->tra_save_point) {
 
736
                        VIO_start_save_point(tdbb, transaction);
 
737
                }
 
738
        }
 
739
        
 
740
        try
 
741
        {
 
742
 
 
743
        if (request->req_message->nod_type == nod_stall
 
744
#ifdef SCROLLABLE_CURSORS
 
745
                || request->req_flags & req_fetch_required
 
746
#endif
 
747
                )
 
748
                execute_looper(tdbb, request, transaction, jrd_req::req_sync);
 
749
 
 
750
        if (!(request->req_flags & req_active) ||
 
751
                request->req_operation != jrd_req::req_send)
 
752
        {
 
753
                ERR_post(isc_req_sync, 0);
 
754
        }
 
755
 
 
756
        const jrd_nod* message = request->req_message;
 
757
        const Format* format = (Format*) message->nod_arg[e_msg_format];
 
758
 
 
759
        if (msg != (USHORT)(IPTR) message->nod_arg[e_msg_number])
 
760
                ERR_post(isc_req_sync, 0);
 
761
 
 
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);
 
766
        }
 
767
 
 
768
        if ((U_IPTR) buffer & (ALIGNMENT - 1))
 
769
                MOVE_FAST((SCHAR *) request + message->nod_impure, buffer, length);
 
770
        else
 
771
                MOVE_FASTER((SCHAR *) request + message->nod_impure, buffer, length);
 
772
 
 
773
        // ASF: temporary blobs returned to the client should not be released
 
774
        // with the request, but in the transaction end.
 
775
        if (top_level)
 
776
        {
 
777
                for (int i = 0; i < format->fmt_count; ++i)
 
778
                {
 
779
                        const DSC* desc = &format->fmt_desc[i];
 
780
 
 
781
                        if (desc->isBlob())
 
782
                        {
 
783
                                const bid* id = (bid*)
 
784
                                        ((UCHAR*)request + message->nod_impure + (ULONG)(IPTR)desc->dsc_address);
 
785
 
 
786
                                if (transaction->tra_blobs.locate(id->bid_temp_id()))
 
787
                                {
 
788
                                        BlobIndex* current = &transaction->tra_blobs.current();
 
789
 
 
790
                                        if (current->bli_request &&
 
791
                                                current->bli_request->req_blobs.locate(id->bid_temp_id()))
 
792
                                        {
 
793
                                                current->bli_request->req_blobs.fastRemove();
 
794
                                                current->bli_request = NULL;
 
795
                                        }
 
796
                                }
 
797
                        }
 
798
                }
 
799
        }
 
800
 
 
801
        execute_looper(tdbb, request, transaction, jrd_req::req_proceed);
 
802
 
 
803
        }       //try
 
804
        catch (const Firebird::Exception&)
 
805
        {
 
806
                if (request->req_flags & req_proc_fetch)
 
807
                {
 
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);
 
812
                }
 
813
                throw;
 
814
        }
 
815
 
 
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);
 
822
        }
 
823
 
 
824
}
 
825
 
 
826
 
 
827
#ifdef SCROLLABLE_CURSORS
 
828
void EXE_seek(thread_db* tdbb, jrd_req* request, USHORT direction, ULONG offset)
 
829
{
 
830
/**************************************
 
831
 *
 
832
 *      E X E _ s e e k
 
833
 *
 
834
 **************************************
 
835
 *
 
836
 * Functional description
 
837
 *      Seek a given request in a particular direction 
 
838
 *      for offset records. 
 
839
 *
 
840
 **************************************/
 
841
        SET_TDBB(tdbb);
 
842
        DEV_BLKCHK(request, type_req);
 
843
 
 
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 */
 
848
 
 
849
/* find the top-level rsb in the request and seek it */
 
850
 
 
851
        for (SLONG i = request->req_fors.getCount() - 1; i >= 0; i--) {
 
852
                RecordSource* rsb = request->req_fors[i];
 
853
                if (rsb) {
 
854
                        seek_rsb(tdbb, request, rsb, direction, offset);
 
855
                        break;
 
856
                }
 
857
        }
 
858
}
 
859
#endif
 
860
 
 
861
 
 
862
void EXE_send(thread_db*                tdbb,
 
863
                          jrd_req*              request,
 
864
                          USHORT        msg,
 
865
                          USHORT        length,
 
866
                          const UCHAR*  buffer)
 
867
{
 
868
/**************************************
 
869
 *
 
870
 *      E X E _ s e n d
 
871
 *
 
872
 **************************************
 
873
 *
 
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.
 
877
 *
 
878
 **************************************/
 
879
        SET_TDBB(tdbb);
 
880
        DEV_BLKCHK(request, type_req);
 
881
 
 
882
        if (--tdbb->tdbb_quantum < 0)
 
883
                JRD_reschedule(tdbb, 0, true);
 
884
 
 
885
        if (!(request->req_flags & req_active))
 
886
                ERR_post(isc_req_sync, 0);
 
887
 
 
888
        jrd_nod* message;
 
889
        jrd_nod* node;
 
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;
 
895
        
 
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]))
 
899
        {
 
900
                /* save the current state of the request so we can go 
 
901
                   back to what was interrupted */
 
902
 
 
903
                const USHORT save_operation = request->req_operation;
 
904
                save_message = request->req_message;
 
905
                save_next = request->req_next;
 
906
 
 
907
                request->req_operation = req_receive;
 
908
                request->req_message = node;
 
909
                request->req_next = message->nod_arg[e_send_statement];
 
910
 
 
911
                /* indicate that we are processing an asynchronous message */
 
912
 
 
913
                request->req_flags |= req_async_processing;
 
914
        }
 
915
        else {
 
916
#endif
 
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
 
921
        }
 
922
#endif
 
923
 
 
924
        jrd_tra* transaction = request->req_transaction;
 
925
 
 
926
        if (node->nod_type == nod_message)
 
927
                message = node;
 
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;
 
931
                         ptr++)
 
932
                {
 
933
                        message = (*ptr)->nod_arg[e_send_message];
 
934
                        if ((USHORT)(IPTR) message->nod_arg[e_msg_number] == msg) {
 
935
                                request->req_next = *ptr;
 
936
                                break;
 
937
                        }
 
938
                }
 
939
        }
 
940
        else
 
941
                BUGCHECK(167);                  /* msg 167 invalid SEND request */
 
942
 
 
943
        const Format* format = (Format*) message->nod_arg[e_msg_format];
 
944
 
 
945
        if (msg != (USHORT)(IPTR) message->nod_arg[e_msg_number])
 
946
                ERR_post(isc_req_sync, 0);
 
947
 
 
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);
 
952
        }
 
953
 
 
954
        if ((U_IPTR) buffer & (ALIGNMENT - 1))
 
955
                MOVE_FAST(buffer, (SCHAR *) request + message->nod_impure, length);
 
956
        else
 
957
                MOVE_FASTER(buffer, (SCHAR *) request + message->nod_impure, length);
 
958
 
 
959
        for (USHORT i = 0; i < format->fmt_count; ++i)
 
960
        {
 
961
                const DSC* desc = &format->fmt_desc[i];
 
962
 
 
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)
 
965
                {
 
966
                        const UCHAR* p = (UCHAR*)request + message->nod_impure + (ULONG)(IPTR)desc->dsc_address;
 
967
                        USHORT len;
 
968
 
 
969
                        switch (desc->dsc_dtype)
 
970
                        {
 
971
                                case dtype_text:
 
972
                                        len = desc->dsc_length;
 
973
                                        break;
 
974
 
 
975
                                case dtype_varying:
 
976
                                        len = reinterpret_cast<const vary*>(p)->vary_length;
 
977
                                        p += sizeof(USHORT);
 
978
                                        break;
 
979
                        }
 
980
 
 
981
                        CharSet* charSet = INTL_charset_lookup(tdbb, DSC_GET_CHARSET(desc));
 
982
 
 
983
                        if (!charSet->wellFormed(len, p))
 
984
                                ERR_post(isc_malformed_string, 0);
 
985
                }
 
986
                else if (desc->isBlob())
 
987
                {
 
988
                        if (desc->getCharSet() != CS_NONE && desc->getCharSet() != CS_BINARY)
 
989
                        {
 
990
                                const Jrd::bid* bid = (Jrd::bid*) ((UCHAR*)request +
 
991
                                        message->nod_impure + (ULONG)(IPTR)desc->dsc_address);
 
992
 
 
993
                                if (!bid->isEmpty())
 
994
                                {
 
995
                                        AutoBlb blob(tdbb, BLB_open(tdbb, tdbb->getTransaction(), bid));
 
996
                                        BLB_check_well_formed(tdbb, desc, blob.getBlb());
 
997
                                }
 
998
                        }
 
999
                }
 
1000
        }
 
1001
 
 
1002
        execute_looper(tdbb, request, transaction, jrd_req::req_proceed);
 
1003
 
 
1004
#ifdef SCROLLABLE_CURSORS
 
1005
        if (save_next) {
 
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 */
 
1009
 
 
1010
                request->req_operation = save_operation;
 
1011
                request->req_message = save_message;
 
1012
                request->req_next = save_next;
 
1013
        }
 
1014
#endif
 
1015
}
 
1016
 
 
1017
 
 
1018
void EXE_start(thread_db* tdbb, jrd_req* request, jrd_tra* transaction)
 
1019
{
 
1020
/**************************************
 
1021
 *
 
1022
 *      E X E _ s t a r t
 
1023
 *
 
1024
 **************************************
 
1025
 *
 
1026
 * Functional description
 
1027
 *      Start an execution running.
 
1028
 *
 
1029
 **************************************/
 
1030
        SET_TDBB(tdbb);
 
1031
        Database* dbb = tdbb->getDatabase();
 
1032
 
 
1033
        BLKCHK(request, type_req);
 
1034
        BLKCHK(transaction, type_tra);
 
1035
 
 
1036
        if (request->req_flags & req_active)
 
1037
                ERR_post(isc_req_sync, isc_arg_gds, isc_reqinuse, 0);
 
1038
 
 
1039
        if (transaction->tra_flags & TRA_prepared)
 
1040
                ERR_post(isc_req_no_trans, 0);
 
1041
 
 
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. */
 
1047
 
 
1048
        TRA_post_resources(tdbb, transaction, request->req_resources);
 
1049
 
 
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);
 
1053
 
 
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;
 
1059
 
 
1060
/* set up to count records affected by request */
 
1061
 
 
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;
 
1067
 
 
1068
        request->req_records_affected.clear();
 
1069
 
 
1070
/* CVC: set up to count virtual operations on SQL views. */
 
1071
 
 
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;
 
1076
 
 
1077
        // Store request start time for timestamp work
 
1078
        request->req_timestamp.validate();
 
1079
 
 
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;
 
1084
                ++ptr)
 
1085
        {
 
1086
                impure_value* impure = (impure_value*) ((SCHAR *) request + (*ptr)->nod_impure);
 
1087
                impure->vlu_flags = 0;
 
1088
        }
 
1089
 
 
1090
        tdbb->bumpStats(RuntimeStatistics::STMT_EXECUTES);
 
1091
 
 
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);
 
1095
        }
 
1096
 
 
1097
        request->req_src_line = 0;
 
1098
        request->req_src_column = 0;
 
1099
 
 
1100
#ifdef WIN_NT
 
1101
        START_CHECK_FOR_EXCEPTIONS(NULL);
 
1102
#endif
 
1103
        // TODO:
 
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)
 
1111
 
 
1112
        looper(tdbb, request, request->req_top_node);
 
1113
 
 
1114
#ifdef WIN_NT
 
1115
        END_CHECK_FOR_EXCEPTIONS(NULL);
 
1116
#endif
 
1117
 
 
1118
        // If any requested modify/delete/insert ops have completed, forget them
 
1119
 
 
1120
        if (transaction &&
 
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)
 
1125
        {
 
1126
                // Forget about any undo for this verb
 
1127
 
 
1128
                VIO_verb_cleanup(tdbb, transaction);
 
1129
        }
 
1130
}
 
1131
 
 
1132
 
 
1133
void EXE_unwind(thread_db* tdbb, jrd_req* request)
 
1134
{
 
1135
/**************************************
 
1136
 *
 
1137
 *      E X E _ u n w i n d
 
1138
 *
 
1139
 **************************************
 
1140
 *
 
1141
 * Functional description
 
1142
 *      Unwind a request, maybe active, maybe not.  This is particularly
 
1143
 *      simple since nothing really needs to be done.
 
1144
 *
 
1145
 **************************************/
 
1146
        DEV_BLKCHK(request, type_req);
 
1147
 
 
1148
        SET_TDBB(tdbb);
 
1149
 
 
1150
        if (request->req_flags & req_active) 
 
1151
        {
 
1152
                if (request->req_fors.getCount() || request->req_exec_sta.getCount()) 
 
1153
                {
 
1154
                        Jrd::ContextPoolHolder context(tdbb, request->req_pool);
 
1155
                        jrd_req* old_request = tdbb->getRequest();
 
1156
                        jrd_tra* old_transaction = tdbb->getTransaction();
 
1157
                        try {
 
1158
                                tdbb->setRequest(request);
 
1159
                                tdbb->setTransaction(request->req_transaction);
 
1160
 
 
1161
                                RecordSource** ptr = request->req_fors.begin();
 
1162
                                for (const RecordSource* const* const end =
 
1163
                                         request->req_fors.end(); ptr < end; ptr++)
 
1164
                                {
 
1165
                                        if (*ptr)
 
1166
                                                RSE_close(tdbb, *ptr);
 
1167
                                }
 
1168
 
 
1169
                                for (size_t i = 0; i < request->req_exec_sta.getCount(); ++i)
 
1170
                                {
 
1171
                                        jrd_nod* node = request->req_exec_sta[i];
 
1172
                                        ExecuteStatement* impure =
 
1173
                                                (ExecuteStatement*)     ((char*) request + node->nod_impure);
 
1174
                                        impure->Close(tdbb);
 
1175
                                }
 
1176
                        }
 
1177
                        catch (const Firebird::Exception&)
 
1178
                        {
 
1179
                                tdbb->setRequest(old_request);
 
1180
                                tdbb->setTransaction(old_transaction);
 
1181
                                throw;
 
1182
                        }
 
1183
 
 
1184
                        tdbb->setRequest(old_request);
 
1185
                        tdbb->setTransaction(old_transaction);
 
1186
                }
 
1187
                release_blobs(tdbb, request);
 
1188
        }
 
1189
 
 
1190
        if (request->req_proc_sav_point && (request->req_flags & req_proc_fetch))
 
1191
                release_proc_save_points(request);
 
1192
 
 
1193
        TRA_detach_request(request);
 
1194
 
 
1195
        request->req_flags &= ~(req_active | req_proc_fetch | req_reserved);
 
1196
        request->req_flags |= req_abort | req_stall;
 
1197
        request->req_timestamp.invalidate();
 
1198
}
 
1199
 
 
1200
 
 
1201
/* CVC: Moved to its own routine, originally in store(). */
 
1202
static void cleanup_rpb(thread_db* tdbb, record_param* rpb)
 
1203
{
 
1204
/**************************************
 
1205
 *
 
1206
 *      c l e a n u p _ r p b
 
1207
 *
 
1208
 **************************************
 
1209
 *
 
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.
 
1214
 *
 
1215
 **************************************/
 
1216
        Record* record = rpb->rpb_record;
 
1217
        const Format* format = record->rec_format;
 
1218
 
 
1219
        SET_TDBB(tdbb); /* Is it necessary? */
 
1220
 
 
1221
/*
 
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
 
1228
    record data.
 
1229
*/
 
1230
 
 
1231
        for (USHORT n = 0; n < format->fmt_count; n++)
 
1232
        {
 
1233
                const dsc* desc = &format->fmt_desc[n];
 
1234
                if (!desc->dsc_address)
 
1235
                        continue;
 
1236
                UCHAR* p = record->rec_data + (IPTR) desc->dsc_address;
 
1237
                if (TEST_NULL(record, n))
 
1238
                {
 
1239
                        USHORT length = desc->dsc_length;
 
1240
                        if (length) {
 
1241
                                memset(p, 0, length);
 
1242
                        }
 
1243
                }
 
1244
                else if (desc->dsc_dtype == dtype_varying)
 
1245
                {
 
1246
                        vary* varying = reinterpret_cast<vary*>(p);
 
1247
                        USHORT length = desc->dsc_length - sizeof(USHORT);
 
1248
                        if (length > varying->vary_length)
 
1249
                        {
 
1250
                                p = reinterpret_cast<UCHAR*>(varying->vary_string + varying->vary_length);
 
1251
                                length -= varying->vary_length;
 
1252
                                memset(p, 0, length);
 
1253
                        }
 
1254
                }
 
1255
        }
 
1256
}
 
1257
 
 
1258
inline void PreModifyEraseTriggers(thread_db* tdbb,
 
1259
                                                                   trig_vec** trigs, 
 
1260
                                                                   SSHORT which_trig, 
 
1261
                                                                   record_param* rpb, 
 
1262
                                                                   Record* rec,
 
1263
                                                                   jrd_req::req_ta op)
 
1264
{
 
1265
/******************************************************
 
1266
 *
 
1267
 *      P r e M o d i f y E r a s e T r i g g e r s
 
1268
 *
 
1269
 ******************************************************
 
1270
 *
 
1271
 * Functional description
 
1272
 *      Perform operation's pre-triggers, 
 
1273
 *  storing active rpb in chain.
 
1274
 *
 
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);
 
1280
        }
 
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);
 
1286
        }
 
1287
        tdbb->getTransaction()->tra_rpblist->PopRpb(rpb, rpblevel);
 
1288
        if (trigger) {
 
1289
                trigger_failure(tdbb, trigger);
 
1290
        }
 
1291
}
 
1292
 
 
1293
static jrd_nod* erase(thread_db* tdbb, jrd_nod* node, SSHORT which_trig)
 
1294
{
 
1295
/**************************************
 
1296
 *
 
1297
 *      e r a s e
 
1298
 *
 
1299
 **************************************
 
1300
 *
 
1301
 * Functional description
 
1302
 *      Perform erase operation.
 
1303
 *
 
1304
 **************************************/
 
1305
        SET_TDBB(tdbb);
 
1306
        Database* dbb = tdbb->getDatabase();
 
1307
        BLKCHK(node, type_nod);
 
1308
 
 
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;
 
1313
 
 
1314
        if (rpb->rpb_number.isBof() ||
 
1315
                (!relation->rel_view_rse && !rpb->rpb_number.isValid()))
 
1316
        {
 
1317
                ERR_post(isc_no_cur_rec, 0);
 
1318
        }
 
1319
 
 
1320
        switch (request->req_operation) {
 
1321
        case jrd_req::req_evaluate:
 
1322
                {
 
1323
                request->req_records_affected.bumpModified(false);
 
1324
                if (!node->nod_arg[e_erase_statement])
 
1325
                        break;
 
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];
 
1332
                }
 
1333
 
 
1334
        case jrd_req::req_return:
 
1335
                break;
 
1336
 
 
1337
        default:
 
1338
                return node->nod_parent;
 
1339
        }
 
1340
 
 
1341
        request->req_operation = jrd_req::req_return;
 
1342
        RLCK_reserve_relation(tdbb, transaction, relation, true, true);
 
1343
 
 
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. */
 
1347
 
 
1348
        if (rpb->rpb_stream_flags & RPB_s_refetch) {
 
1349
                VIO_refetch_record(tdbb, rpb, transaction);
 
1350
                rpb->rpb_stream_flags &= ~RPB_s_refetch;
 
1351
        }
 
1352
 
 
1353
        if (transaction != dbb->dbb_sys_trans)
 
1354
                ++transaction->tra_save_point->sav_verb_count;
 
1355
 
 
1356
/* Handle pre-operation trigger */
 
1357
        PreModifyEraseTriggers(tdbb, &relation->rel_pre_erase,
 
1358
                                                   which_trig, rpb, NULL,
 
1359
                                                   jrd_req::req_trigger_delete);
 
1360
 
 
1361
        if (relation->rel_file) {
 
1362
                EXT_erase(rpb, transaction);
 
1363
        }
 
1364
        else if (relation->isVirtual()) {
 
1365
                VirtualTable::erase(tdbb, rpb);
 
1366
        }
 
1367
        else if (!relation->rel_view_rse) {
 
1368
                VIO_erase(tdbb, rpb, transaction);
 
1369
        }
 
1370
 
 
1371
/* Handle post operation trigger */
 
1372
        jrd_req* 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)))
 
1378
        {
 
1379
                trigger_failure(tdbb, trigger);
 
1380
        }
 
1381
 
 
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 */
 
1385
 
 
1386
        if (!relation->rel_file &&
 
1387
                !relation->rel_view_rse &&
 
1388
                !relation->isVirtual())
 
1389
        {
 
1390
                jrd_rel* bad_relation = 0;
 
1391
                USHORT bad_index;
 
1392
 
 
1393
                const IDX_E error_code =
 
1394
                        IDX_erase(tdbb, rpb, transaction, &bad_relation, &bad_index);
 
1395
 
 
1396
                if (error_code) {
 
1397
                        ERR_duplicate_error(error_code, bad_relation, bad_index);
 
1398
                }
 
1399
        }
 
1400
 
 
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;
 
1407
                }
 
1408
        }
 
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);
 
1413
                }
 
1414
        }
 
1415
        else if (relation->rel_file || !relation->rel_view_rse) {
 
1416
                request->req_records_deleted++;
 
1417
                request->req_records_affected.bumpModified(true);
 
1418
        }
 
1419
 
 
1420
        if (transaction != dbb->dbb_sys_trans) {
 
1421
                --transaction->tra_save_point->sav_verb_count;
 
1422
        }
 
1423
 
 
1424
        rpb->rpb_number.setValid(false);
 
1425
 
 
1426
        return node->nod_parent;
 
1427
}
 
1428
 
 
1429
 
 
1430
static void execute_looper(
 
1431
                                                   thread_db* tdbb,
 
1432
                                                   jrd_req* request,
 
1433
                                                   jrd_tra* transaction, enum jrd_req::req_s next_state)
 
1434
{
 
1435
/**************************************
 
1436
 *
 
1437
 *      e x e c u t e _ l o o p e r
 
1438
 *
 
1439
 **************************************
 
1440
 *
 
1441
 * Functional description
 
1442
 *      Wrapper around looper. This will execute
 
1443
 *      looper with the save point mechanism.
 
1444
 *
 
1445
 **************************************/
 
1446
        DEV_BLKCHK(request, type_req);
 
1447
 
 
1448
        SET_TDBB(tdbb);
 
1449
        Database* dbb = tdbb->getDatabase();
 
1450
 
 
1451
/* Start a save point */
 
1452
 
 
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);
 
1456
 
 
1457
        request->req_flags &= ~req_stall;
 
1458
        request->req_operation = next_state;
 
1459
 
 
1460
        looper(tdbb, request, request->req_next);
 
1461
 
 
1462
/* If any requested modify/delete/insert ops have completed, forget them */
 
1463
 
 
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)
 
1468
                {
 
1469
                        /* Forget about any undo for this verb */
 
1470
 
 
1471
                        VIO_verb_cleanup(tdbb, transaction);
 
1472
                }
 
1473
        }
 
1474
}
 
1475
 
 
1476
 
 
1477
static void exec_sql(thread_db* tdbb, jrd_req* request, DSC* dsc)
 
1478
{
 
1479
/**************************************
 
1480
 *
 
1481
 *      e x e c _ s q l
 
1482
 *
 
1483
 **************************************
 
1484
 *
 
1485
 * Functional description
 
1486
 *      Execute a string as SQL operator.
 
1487
 *
 
1488
 **************************************/
 
1489
        SET_TDBB(tdbb);
 
1490
 
 
1491
        if (tdbb->getTransaction()->tra_callback_count >= MAX_CALLBACKS) {
 
1492
                ERR_post(isc_exec_sql_max_call_exceeded, 0);
 
1493
        }
 
1494
 
 
1495
        Firebird::string SqlStatementText;
 
1496
        ExecuteStatement::getString(tdbb, SqlStatementText, dsc, request);
 
1497
                
 
1498
        ISC_STATUS_ARRAY local;
 
1499
        memset(local, 0, sizeof(local));
 
1500
        ISC_STATUS* status = local;
 
1501
 
 
1502
#if (defined DEV_BUILD && !defined MULTI_THREAD)
 
1503
        tdbb->getDatabase()->dbb_flags |= DBB_exec_statement;
 
1504
#endif
 
1505
        tdbb->getTransaction()->tra_callback_count++;
 
1506
        callback_execute_immediate(status,
 
1507
                                                           tdbb->getAttachment(),
 
1508
                                                           tdbb->getTransaction(),
 
1509
                                                           SqlStatementText);
 
1510
        tdbb->getTransaction()->tra_callback_count--;
 
1511
#if (defined DEV_BUILD && !defined MULTI_THREAD)
 
1512
        tdbb->getDatabase()->dbb_flags &= ~DBB_exec_statement;
 
1513
#endif
 
1514
 
 
1515
        if (status[1]) {
 
1516
                memcpy(tdbb->tdbb_status_vector, status, sizeof(local));
 
1517
                ERR_punt();
 
1518
        }
 
1519
}
 
1520
 
 
1521
 
 
1522
static void execute_procedure(thread_db* tdbb, jrd_nod* node)
 
1523
{
 
1524
/**************************************
 
1525
 *
 
1526
 *      e x e c u t e _ p r o c e d u r e
 
1527
 *
 
1528
 **************************************
 
1529
 *
 
1530
 * Functional description
 
1531
 *      Execute a stored procedure.  Begin by
 
1532
 *      assigning the input parameters.  End
 
1533
 *      by assigning the output parameters.
 
1534
 *
 
1535
 **************************************/
 
1536
        SET_TDBB(tdbb);
 
1537
        BLKCHK(node, type_nod);
 
1538
 
 
1539
        jrd_req* request = tdbb->getRequest();
 
1540
 
 
1541
        jrd_nod* temp = node->nod_arg[e_esp_inputs];
 
1542
        if (temp) {
 
1543
                jrd_nod** ptr;
 
1544
                jrd_nod** end;
 
1545
                for (ptr = temp->nod_arg, end = ptr + temp->nod_count; ptr < end;
 
1546
                         ptr++)
 
1547
                {
 
1548
                        EXE_assignment(tdbb, *ptr);
 
1549
                }
 
1550
        }
 
1551
 
 
1552
        USHORT in_msg_length;
 
1553
        SCHAR* in_msg;
 
1554
        jrd_nod* in_message = node->nod_arg[e_esp_in_msg];
 
1555
        if (in_message) {
 
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;
 
1559
        }
 
1560
 
 
1561
        USHORT out_msg_length;
 
1562
        SCHAR* out_msg;
 
1563
        jrd_nod* out_message = node->nod_arg[e_esp_out_msg];
 
1564
        if (out_message) {
 
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;
 
1568
        }
 
1569
 
 
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);
 
1572
 
 
1573
        Firebird::Array<char> temp_buffer;
 
1574
        
 
1575
        if (!out_message) {
 
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);
 
1580
        }
 
1581
 
 
1582
 
 
1583
/* Catch errors so we can unwind cleanly */
 
1584
 
 
1585
        try {
 
1586
                // Save the old pool
 
1587
                Jrd::ContextPoolHolder context(tdbb, proc_request->req_pool);
 
1588
 
 
1589
                jrd_tra* transaction = request->req_transaction;
 
1590
                const SLONG save_point_number = transaction->tra_save_point->sav_number;
 
1591
 
 
1592
                proc_request->req_timestamp = request->req_timestamp;
 
1593
                EXE_start(tdbb, proc_request, transaction);
 
1594
                if (in_message) {
 
1595
                        EXE_send(tdbb, proc_request, 0, in_msg_length,
 
1596
                                         reinterpret_cast<const UCHAR*>(in_msg));
 
1597
                }
 
1598
 
 
1599
                EXE_receive(tdbb, proc_request, 1, out_msg_length,
 
1600
                                reinterpret_cast<UCHAR*>(out_msg));
 
1601
 
 
1602
/* Clean up all savepoints started during execution of the
 
1603
   procedure */
 
1604
 
 
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)
 
1609
                        {
 
1610
                                VIO_verb_cleanup(tdbb, transaction);
 
1611
                        }
 
1612
                }
 
1613
 
 
1614
        }       // try
 
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();
 
1621
                throw;
 
1622
        }
 
1623
 
 
1624
        EXE_unwind(tdbb, proc_request);
 
1625
        tdbb->setRequest(request);
 
1626
 
 
1627
        temp = node->nod_arg[e_esp_outputs];
 
1628
        if (temp) {
 
1629
                jrd_nod** ptr;
 
1630
                jrd_nod** end;
 
1631
                for (ptr = temp->nod_arg, end = ptr + temp->nod_count; ptr < end;
 
1632
                         ptr++)
 
1633
                {
 
1634
                        EXE_assignment(tdbb, *ptr);
 
1635
                }
 
1636
        }
 
1637
 
 
1638
        proc_request->req_attachment = NULL;
 
1639
        proc_request->req_flags &= ~(req_in_use | req_proc_fetch);
 
1640
        proc_request->req_timestamp.invalidate();
 
1641
}
 
1642
 
 
1643
 
 
1644
static jrd_req* execute_triggers(thread_db* tdbb,
 
1645
                                                                trig_vec** triggers,
 
1646
                                                                Record* old_rec,
 
1647
                                                                Record* new_rec,
 
1648
                                                                enum jrd_req::req_ta trigger_action)
 
1649
{
 
1650
/**************************************
 
1651
 *
 
1652
 *      e x e c u t e _ t r i g g e r s
 
1653
 *
 
1654
 **************************************
 
1655
 *
 
1656
 * Functional description
 
1657
 *      Execute group of triggers.  Return pointer to failing trigger
 
1658
 *      if any blow up.
 
1659
 *
 
1660
 **************************************/
 
1661
        if (!*triggers) {
 
1662
                return NULL;
 
1663
        }
 
1664
 
 
1665
        SET_TDBB(tdbb);
 
1666
 
 
1667
        jrd_tra* transaction = (tdbb->getRequest() ? tdbb->getRequest()->req_transaction : tdbb->getTransaction());
 
1668
        trig_vec* vector = *triggers;
 
1669
        jrd_req* result = NULL;
 
1670
 
 
1671
        Record* null_rec = NULL;
 
1672
 
 
1673
        if (!old_rec && !new_rec)
 
1674
        {
 
1675
                // this is a database trigger
 
1676
        }
 
1677
        else if (!old_rec || !new_rec)
 
1678
        {
 
1679
                const Record* record = old_rec ? old_rec : new_rec;
 
1680
                fb_assert(record && record->rec_format);
 
1681
                // copy the record
 
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);
 
1691
        }
 
1692
 
 
1693
        jrd_req* trigger = NULL;
 
1694
        Firebird::TimeStamp timestamp;
 
1695
 
 
1696
        try
 
1697
        {
 
1698
                for (trig_vec::iterator ptr = vector->begin(); ptr != vector->end(); ++ptr)
 
1699
                {
 
1700
                        ptr->compile(tdbb);
 
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;
 
1704
 
 
1705
                        if (tdbb->getRequest())
 
1706
                                trigger->req_timestamp = tdbb->getRequest()->req_timestamp;
 
1707
                        else
 
1708
                                trigger->req_timestamp = timestamp;
 
1709
                        
 
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) {
 
1716
                                result = trigger;
 
1717
                                break;
 
1718
                        }
 
1719
                        trigger = NULL;
 
1720
                }
 
1721
 
 
1722
                delete null_rec;
 
1723
                if (vector != *triggers) {
 
1724
                        MET_release_triggers(tdbb, &vector);
 
1725
                }
 
1726
 
 
1727
                return result;
 
1728
        }
 
1729
        catch (const Firebird::Exception& ex)
 
1730
        {
 
1731
                delete null_rec;
 
1732
                if (vector != *triggers) {
 
1733
                        MET_release_triggers(tdbb, &vector);
 
1734
                }
 
1735
                if (!trigger) {
 
1736
                  throw; // trigger probally fails to compile
 
1737
                }
 
1738
                Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
 
1739
                return trigger;
 
1740
        }
 
1741
}
 
1742
 
 
1743
 
 
1744
static void stuff_stack_trace(const jrd_req* request)
 
1745
{
 
1746
        Firebird::string sTrace;
 
1747
        bool isEmpty = true;
 
1748
 
 
1749
        for (const jrd_req* req = request; req; req = req->req_caller)
 
1750
        {
 
1751
                Firebird::string name;
 
1752
 
 
1753
                if (req->req_trg_name.length()) {
 
1754
                        name = "At trigger '";
 
1755
                        name += req->req_trg_name.c_str();
 
1756
                }
 
1757
                else if (req->req_procedure) {
 
1758
                        name = "At procedure '";
 
1759
                        name += req->req_procedure->prc_name.c_str();
 
1760
                }
 
1761
 
 
1762
                if (! name.isEmpty())
 
1763
                {
 
1764
                        name.trim();
 
1765
 
 
1766
                        if (sTrace.length() + name.length() + 2 > MAX_STACK_TRACE)
 
1767
                                break;
 
1768
 
 
1769
                        if (isEmpty) {
 
1770
                                isEmpty = false;
 
1771
                                sTrace += name + "'";
 
1772
                        }
 
1773
                        else {
 
1774
                                sTrace += "\n" + name + "'";
 
1775
                        }
 
1776
 
 
1777
                        if (req->req_src_line)
 
1778
                        {
 
1779
                                Firebird::string src_info;
 
1780
                                src_info.printf(" line: %u, col: %u", req->req_src_line, req->req_src_column);
 
1781
 
 
1782
                                if (sTrace.length() + src_info.length() > MAX_STACK_TRACE)
 
1783
                                        break;
 
1784
 
 
1785
                                sTrace += src_info;
 
1786
                        }
 
1787
                }
 
1788
        }
 
1789
 
 
1790
        if (!isEmpty)
 
1791
                ERR_post_nothrow(isc_stack_trace, isc_arg_string, ERR_cstring(sTrace), 0);
 
1792
}
 
1793
 
 
1794
 
 
1795
static jrd_nod* looper(thread_db* tdbb, jrd_req* request, jrd_nod* in_node)
 
1796
{
 
1797
/**************************************
 
1798
 *
 
1799
 *      l o o p e r
 
1800
 *
 
1801
 **************************************
 
1802
 *
 
1803
 * Functional description
 
1804
 *      Cycle thru request execution tree.  Return next node for
 
1805
 *      execution on stall or request complete.
 
1806
 *
 
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;
 
1812
        jrd_nod* prev_node;
 
1813
 
 
1814
        jrd_tra* transaction = request->req_transaction;
 
1815
        if (!transaction) {
 
1816
                ERR_post(isc_req_no_trans, 0);
 
1817
        }
 
1818
 
 
1819
        SET_TDBB(tdbb);
 
1820
        Database* dbb = tdbb->getDatabase();
 
1821
        BLKCHK(in_node, type_nod);
 
1822
 
 
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);
 
1826
 
 
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;
 
1833
 
 
1834
        const SLONG save_point_number = (transaction->tra_save_point) ?
 
1835
                transaction->tra_save_point->sav_number : 0;
 
1836
 
 
1837
        jrd_nod* node = in_node;
 
1838
 
 
1839
        // Catch errors so we can unwind cleanly
 
1840
 
 
1841
        bool error_pending = false;
 
1842
        bool catch_disabled = false;
 
1843
        tdbb->tdbb_flags &= ~(TDBB_stack_trace_done | TDBB_sys_error);
 
1844
 
 
1845
        // Execute stuff until we drop
 
1846
 
 
1847
        while (node && !(request->req_flags & req_stall))
 
1848
        {
 
1849
        try {
 
1850
 
 
1851
                if (request->req_operation == jrd_req::req_evaluate &&
 
1852
                        (--tdbb->tdbb_quantum < 0))
 
1853
                {
 
1854
                        JRD_reschedule(tdbb, 0, true);
 
1855
                }
 
1856
 
 
1857
#if defined(DEBUG_GDS_ALLOC) && FALSE
 
1858
                int node_type = node->nod_type;
 
1859
#endif
 
1860
 
 
1861
                switch (node->nod_type) {
 
1862
                case nod_asn_list:
 
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;
 
1866
                                         ptr < end; ptr++)
 
1867
                                {
 
1868
                                        EXE_assignment(tdbb, *ptr);
 
1869
                                }
 
1870
                                request->req_operation = jrd_req::req_return;
 
1871
                        }
 
1872
                        node = node->nod_parent;
 
1873
                        break;
 
1874
 
 
1875
                case nod_assignment:
 
1876
                        if (request->req_operation == jrd_req::req_evaluate)
 
1877
                                EXE_assignment(tdbb, node);
 
1878
                        node = node->nod_parent;
 
1879
                        break;
 
1880
 
 
1881
                case nod_dcl_variable:
 
1882
                        {
 
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)
 
1890
                                {
 
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;
 
1898
                                }
 
1899
 
 
1900
                                request->req_operation = jrd_req::req_return;
 
1901
                                node = node->nod_parent;
 
1902
                        }
 
1903
                        break;
 
1904
 
 
1905
                case nod_erase:
 
1906
                        if (request->req_operation == jrd_req::req_unwind) {
 
1907
                                node = node->nod_parent;
 
1908
                        }
 
1909
                        else if ((request->req_operation == jrd_req::req_return) &&
 
1910
                                         (node->nod_arg[e_erase_sub_erase]))
 
1911
                        {
 
1912
                                if (!top_node) {
 
1913
                                        top_node = node;
 
1914
                                        which_erase_trig = PRE_TRIG;
 
1915
                                }
 
1916
                                prev_node = node;
 
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;
 
1921
                                }
 
1922
                                if (top_node == prev_node && which_erase_trig == POST_TRIG) {
 
1923
                                        top_node = NULL;
 
1924
                                        which_erase_trig = ALL_TRIGS;
 
1925
                                }
 
1926
                                else
 
1927
                                        request->req_operation = jrd_req::req_evaluate;
 
1928
                        }
 
1929
                        else {
 
1930
                                prev_node = node;
 
1931
                                node = erase(tdbb, node, ALL_TRIGS);
 
1932
                                if (!(prev_node->nod_arg[e_erase_sub_erase]) &&
 
1933
                                        which_erase_trig == PRE_TRIG)
 
1934
                                {
 
1935
                                        which_erase_trig = POST_TRIG;
 
1936
                                }
 
1937
                        }
 
1938
                        break;
 
1939
                
 
1940
                case nod_exec_proc:
 
1941
                        if (request->req_operation == jrd_req::req_unwind) {
 
1942
                                node = node->nod_parent;
 
1943
                                break;
 
1944
                        }
 
1945
                        execute_procedure(tdbb, node);
 
1946
                        node = node->nod_parent;
 
1947
                        request->req_operation = jrd_req::req_return;
 
1948
                        break;
 
1949
 
 
1950
                case nod_for:
 
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];
 
1958
                                        break;
 
1959
                                }
 
1960
                        case jrd_req::req_sync:
 
1961
                                if (RSE_get_record(tdbb, (RecordSource*) node->nod_arg[e_for_rsb],
 
1962
#ifdef SCROLLABLE_CURSORS
 
1963
                                                                   RSE_get_next))
 
1964
#else
 
1965
                                                                   RSE_get_forward))
 
1966
#endif
 
1967
                                {
 
1968
                                        node = node->nod_arg[e_for_statement];
 
1969
                                        request->req_operation = jrd_req::req_evaluate;
 
1970
                                        break;
 
1971
                                }
 
1972
                                request->req_operation = jrd_req::req_return;
 
1973
                        default:
 
1974
                                RSE_close(tdbb, (RecordSource*) node->nod_arg[e_for_rsb]);
 
1975
                                node = node->nod_parent;
 
1976
                        }
 
1977
                        break;
 
1978
 
 
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;
 
1988
                        }
 
1989
                        node = node->nod_parent;
 
1990
                        break;
 
1991
 
 
1992
                case nod_cursor_stmt:
 
1993
                        {
 
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);
 
2000
                                switch (op) {
 
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);
 
2006
                                                }
 
2007
                                                // open cursor
 
2008
                                                RSE_open(tdbb, rsb);
 
2009
                                                request->req_operation = jrd_req::req_return;
 
2010
                                        }
 
2011
                                        node = node->nod_parent;
 
2012
                                        break;
 
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);
 
2018
                                                }
 
2019
                                                // close cursor
 
2020
                                                RSE_close(tdbb, rsb);
 
2021
                                                request->req_operation = jrd_req::req_return;
 
2022
                                        }
 
2023
                                        node = node->nod_parent;
 
2024
                                        break;
 
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);
 
2031
                                                }
 
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];
 
2036
                                                        break;
 
2037
                                                }
 
2038
                                                // fetch one record
 
2039
                                                if (RSE_get_record(tdbb, rsb,
 
2040
#ifdef SCROLLABLE_CURSORS
 
2041
                                                                                   RSE_get_next))
 
2042
#else
 
2043
                                                                                   RSE_get_forward))
 
2044
#endif
 
2045
                                                {
 
2046
                                                        node = node->nod_arg[e_cursor_stmt_into];
 
2047
                                                        request->req_operation = jrd_req::req_evaluate;
 
2048
                                                        break;
 
2049
                                                }
 
2050
                                                request->req_operation = jrd_req::req_return;
 
2051
                                        default:
 
2052
                                                node = node->nod_parent;
 
2053
                                        }
 
2054
                                        break;
 
2055
                                }
 
2056
                        }
 
2057
                        break;
 
2058
 
 
2059
                case nod_abort:
 
2060
                        switch (request->req_operation) {
 
2061
                        case jrd_req::req_evaluate:
 
2062
                                {
 
2063
                                        PsqlException* xcp_node = reinterpret_cast<PsqlException*>(node->nod_arg[e_xcp_desc]);
 
2064
                                        if (xcp_node)
 
2065
                                        {
 
2066
                                                /* PsqlException is defined,
 
2067
                                                   so throw an exception */
 
2068
                                                set_error(tdbb, &xcp_node->xcp_rpt[0], node->nod_arg[e_xcp_msg]);
 
2069
                                        }
 
2070
                                        else if (!request->req_last_xcp.success())
 
2071
                                        {
 
2072
                                                /* PsqlException is undefined, but there was a known exception before,
 
2073
                                                   so re-initiate it */
 
2074
                                                set_error(tdbb, NULL, NULL);
 
2075
                                        }
 
2076
                                        else
 
2077
                                        {
 
2078
                                                /* PsqlException is undefined and there weren't any exceptions before,
 
2079
                                                   so just do nothing */
 
2080
                                                request->req_operation = jrd_req::req_return;
 
2081
                                        }
 
2082
                                }
 
2083
 
 
2084
                        default:
 
2085
                                node = node->nod_parent;
 
2086
                        }
 
2087
                        break;
 
2088
 
 
2089
                case nod_user_savepoint:
 
2090
                        switch (request->req_operation) {
 
2091
                        case jrd_req::req_evaluate:
 
2092
                                if (transaction != dbb->dbb_sys_trans) {
 
2093
 
 
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];
 
2096
 
 
2097
                                        // Skip the savepoint created by EXE_start
 
2098
                                        Savepoint* savepoint = transaction->tra_save_point->sav_next;
 
2099
                                        Savepoint* previous = transaction->tra_save_point;
 
2100
 
 
2101
                                        // Find savepoint
 
2102
                                        bool found = false;
 
2103
                                        while (true) {
 
2104
                                                if (!savepoint || !(savepoint->sav_flags & SAV_user))
 
2105
                                                        break;
 
2106
 
 
2107
                                                if (!strcmp(node_savepoint_name, savepoint->sav_name)) {
 
2108
                                                        found = true;
 
2109
                                                        break;
 
2110
                                                }
 
2111
 
 
2112
                                                previous = savepoint;
 
2113
                                                savepoint = savepoint->sav_next;
 
2114
                                        }
 
2115
                                        if (!found && operation != blr_savepoint_set) {
 
2116
                                                ERR_post(isc_invalid_savepoint,
 
2117
                                                        isc_arg_string, ERR_cstring(node_savepoint_name), 0);
 
2118
                                        }
 
2119
 
 
2120
                                        switch (operation)
 
2121
                                        {
 
2122
                                        case blr_savepoint_set:
 
2123
                                                // Release the savepoint
 
2124
                                                if (found) {
 
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;
 
2130
                                                }
 
2131
 
 
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);
 
2135
                                                break;
 
2136
                                        case blr_savepoint_release_single:
 
2137
                                        {
 
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;
 
2144
                                                break;
 
2145
                                        }
 
2146
                                        case blr_savepoint_release:
 
2147
                                        {
 
2148
                                                const SLONG sav_number = savepoint->sav_number;
 
2149
 
 
2150
                                                // Release the savepoint and all subsequent ones
 
2151
                                                while (transaction->tra_save_point &&
 
2152
                                                        transaction->tra_save_point->sav_number >= sav_number) 
 
2153
                                                {
 
2154
                                                        verb_cleanup(tdbb, transaction);
 
2155
                                                }
 
2156
 
 
2157
                                                // Restore the savepoint initially created by EXE_start
 
2158
                                                VIO_start_save_point(tdbb, transaction);
 
2159
                                                break;
 
2160
                                        }
 
2161
                                        case blr_savepoint_undo:
 
2162
                                        {
 
2163
                                                const SLONG sav_number = savepoint->sav_number;
 
2164
 
 
2165
                                                // Undo the savepoint
 
2166
                                                while (transaction->tra_save_point &&
 
2167
                                                        transaction->tra_save_point->sav_number >= sav_number) 
 
2168
                                                {
 
2169
                                                        transaction->tra_save_point->sav_verb_count++;
 
2170
                                                        verb_cleanup(tdbb, transaction);
 
2171
                                                }
 
2172
 
 
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);
 
2177
                                                break;
 
2178
                                        }
 
2179
                                        default:
 
2180
                                                BUGCHECK(232);
 
2181
                                                break;
 
2182
                                        }
 
2183
                                }
 
2184
                        default:
 
2185
                                node = node->nod_parent;
 
2186
                                request->req_operation = jrd_req::req_return;
 
2187
                        }
 
2188
                        break;
 
2189
 
 
2190
                case nod_start_savepoint:
 
2191
                        switch (request->req_operation) {
 
2192
                        case jrd_req::req_evaluate:
 
2193
                                /* Start a save point */
 
2194
 
 
2195
                                if (transaction != dbb->dbb_sys_trans)
 
2196
                                        VIO_start_save_point(tdbb, transaction);
 
2197
 
 
2198
                        default:
 
2199
                                node = node->nod_parent;
 
2200
                                request->req_operation = jrd_req::req_return;
 
2201
                        }
 
2202
                        break;
 
2203
 
 
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;
 
2216
                                        }
 
2217
                                        verb_cleanup(tdbb, transaction);
 
2218
                                }
 
2219
 
 
2220
                        default:
 
2221
                                node = node->nod_parent;
 
2222
                                request->req_operation = jrd_req::req_return;
 
2223
                        }
 
2224
                        break;
 
2225
 
 
2226
                case nod_handler:
 
2227
                        switch (request->req_operation) {
 
2228
                        case jrd_req::req_evaluate:
 
2229
                                node = node->nod_arg[0];
 
2230
                                break;
 
2231
 
 
2232
                        case jrd_req::req_unwind:
 
2233
                                if (!request->req_label)
 
2234
                                        request->req_operation = jrd_req::req_return;
 
2235
 
 
2236
                        default:
 
2237
                                node = node->nod_parent;
 
2238
                        }
 
2239
                        break;
 
2240
 
 
2241
                case nod_block:
 
2242
                        switch (request->req_operation)
 
2243
                        {
 
2244
                        SLONG count;
 
2245
                        
 
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;
 
2251
                                        MOVE_FAST(&count,
 
2252
                                                          (SCHAR *) request + node->nod_impure,
 
2253
                                                          sizeof(SLONG));
 
2254
                                }
 
2255
                                node = node->nod_arg[e_blk_action];
 
2256
                                break;
 
2257
 
 
2258
                        case jrd_req::req_unwind:
 
2259
                                {
 
2260
                                        if (request->req_flags & req_leave)
 
2261
                                        {
 
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.
 
2267
 
 
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)
 
2274
                                                        {
 
2275
                                                                verb_cleanup(tdbb, transaction);
 
2276
                                                        }
 
2277
                                                }
 
2278
 
 
2279
                                                node = node->nod_parent;
 
2280
                                                break;
 
2281
                                        }
 
2282
                                        if (transaction != dbb->dbb_sys_trans)
 
2283
                                        {
 
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)
 
2292
                                                {
 
2293
                                                        ++transaction->tra_save_point->sav_verb_count;
 
2294
                                                        verb_cleanup(tdbb, transaction);
 
2295
                                                }
 
2296
                                        }
 
2297
 
 
2298
                                        jrd_nod* handlers = node->nod_arg[e_blk_handlers];
 
2299
                                        if (handlers)
 
2300
                                        {
 
2301
                                                node = node->nod_parent;
 
2302
                                                jrd_nod** ptr = handlers->nod_arg;
 
2303
                                                for (const jrd_nod* const* const end = ptr + handlers->nod_count;
 
2304
                                                        ptr < end; ptr++)
 
2305
                                                {
 
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))
 
2309
                                                        {
 
2310
                                                                request->req_operation = jrd_req::req_evaluate;
 
2311
                                                                node = (*ptr)->nod_arg[e_err_action];
 
2312
                                                                error_pending = false;
 
2313
 
 
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. */
 
2317
 
 
2318
                                                                {
 
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;
 
2323
 
 
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 
 
2327
                                                                           nested. */
 
2328
 
 
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;
 
2335
 
 
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. */
 
2348
 
 
2349
                                                                        catch_disabled = false;
 
2350
                                                                        tdbb->setRequest(request);
 
2351
                                                                        fb_assert(request->req_caller == NULL);
 
2352
                                                                        request->req_caller = old_request;
 
2353
                                                                }
 
2354
 
 
2355
                                                                /* The error is dealt with by the application, cleanup
 
2356
                                                                   this block's savepoint. */
 
2357
 
 
2358
                                                                if (transaction != dbb->dbb_sys_trans)
 
2359
                                                                {
 
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)
 
2363
                                                                        {
 
2364
                                                                                verb_cleanup(tdbb, transaction);
 
2365
                                                                        }
 
2366
                                                                }
 
2367
                                                        }
 
2368
                                                }
 
2369
                                        }
 
2370
                                        else
 
2371
                                        {
 
2372
                                                node = node->nod_parent;
 
2373
                                        }
 
2374
 
 
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. */
 
2378
 
 
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)
 
2383
                                                {
 
2384
                                                        ++transaction->tra_save_point->sav_verb_count;
 
2385
                                                        verb_cleanup(tdbb, transaction);
 
2386
                                                }
 
2387
                                        }
 
2388
                                }
 
2389
                                break;
 
2390
 
 
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)
 
2398
                                        {
 
2399
                                                verb_cleanup(tdbb, transaction);
 
2400
                                        }
 
2401
                                }
 
2402
                        default:
 
2403
                                node = node->nod_parent;
 
2404
                        }
 
2405
                        break;
 
2406
 
 
2407
                case nod_error_handler:
 
2408
                        if (request->req_flags & req_error_handler && !error_pending)
 
2409
                        {
 
2410
                                fb_assert(request->req_caller == old_request);
 
2411
                                request->req_caller = NULL;
 
2412
                                return node;
 
2413
                        }
 
2414
                        node = node->nod_parent;
 
2415
                        node = node->nod_parent;
 
2416
                        if (request->req_operation == jrd_req::req_unwind) {
 
2417
                                node = node->nod_parent;
 
2418
                        }
 
2419
                        request->req_last_xcp.clear();
 
2420
                        break;
 
2421
 
 
2422
                case nod_label:
 
2423
                        switch (request->req_operation) {
 
2424
                        case jrd_req::req_evaluate:
 
2425
                                node = node->nod_arg[e_lbl_statement];
 
2426
                                break;
 
2427
 
 
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)))
 
2431
                                {
 
2432
                                        request->req_flags &= ~req_leave;
 
2433
                                        request->req_operation = jrd_req::req_return;
 
2434
                                }
 
2435
 
 
2436
                        default:
 
2437
                                node = node->nod_parent;
 
2438
                        }
 
2439
                        break;
 
2440
 
 
2441
                case nod_leave:
 
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;
 
2446
                        break;
 
2447
 
 
2448
                case nod_list:
 
2449
                        {
 
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++];
 
2459
                                                break;
 
2460
                                        }
 
2461
                                        request->req_operation = jrd_req::req_return;
 
2462
                                default:
 
2463
                                        node = node->nod_parent;
 
2464
                                }
 
2465
                        }
 
2466
                        break;
 
2467
 
 
2468
                case nod_loop:
 
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;
 
2474
                                break;
 
2475
 
 
2476
                        default:
 
2477
                                node = node->nod_parent;
 
2478
                        }
 
2479
                        break;
 
2480
 
 
2481
                case nod_if:
 
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;
 
2486
                                        break;
 
2487
                                }
 
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;
 
2491
                                        break;
 
2492
                                }
 
2493
                                else {
 
2494
                                        request->req_operation = jrd_req::req_return;
 
2495
                                }
 
2496
                        }
 
2497
                        node = node->nod_parent;
 
2498
                        break;
 
2499
 
 
2500
                case nod_modify:
 
2501
                        {
 
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;
 
2505
                                }
 
2506
                                else if ((request->req_operation == jrd_req::req_return) &&
 
2507
                                                 (!impure->sta_state) && (node->nod_arg[e_mod_sub_mod])) {
 
2508
                                        if (!top_node) {
 
2509
                                                top_node = node;
 
2510
                                                which_mod_trig = PRE_TRIG;
 
2511
                                        }
 
2512
                                        prev_node = node;
 
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;
 
2517
                                        }
 
2518
                                        if (top_node == prev_node && which_mod_trig == POST_TRIG) {
 
2519
                                                top_node = NULL;
 
2520
                                                which_mod_trig = ALL_TRIGS;
 
2521
                                        }
 
2522
                                        else {
 
2523
                                                request->req_operation = jrd_req::req_evaluate;
 
2524
                                        }
 
2525
                                }
 
2526
                                else {
 
2527
                                        prev_node = node;
 
2528
                                        node = modify(tdbb, node, ALL_TRIGS);
 
2529
                                        if (!(prev_node->nod_arg[e_mod_sub_mod]) &&
 
2530
                                                which_mod_trig == PRE_TRIG)
 
2531
                                        {
 
2532
                                                which_mod_trig = POST_TRIG;
 
2533
                                        }
 
2534
                                }
 
2535
                        }
 
2536
                        break;
 
2537
 
 
2538
                case nod_nop:
 
2539
                        request->req_operation = jrd_req::req_return;
 
2540
                        node = node->nod_parent;
 
2541
                        break;
 
2542
 
 
2543
                case nod_receive:
 
2544
                        node = receive_msg(tdbb, node);
 
2545
                        break;
 
2546
 
 
2547
                case nod_exec_sql:
 
2548
                        if (request->req_operation == jrd_req::req_unwind) {
 
2549
                                node = node->nod_parent;
 
2550
                                break;
 
2551
                        }
 
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;
 
2556
                        break;
 
2557
 
 
2558
                case nod_exec_into: 
 
2559
                        {
 
2560
                                ExecuteStatement* impure =
 
2561
                                        (ExecuteStatement*)
 
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];
 
2572
                                                break;
 
2573
                                        }
 
2574
                                        request->req_operation = jrd_req::req_return;
 
2575
                                default:
 
2576
                                        // if have active opened request - close it
 
2577
                                        impure->Close(tdbb);
 
2578
                                        node = node->nod_parent;
 
2579
                                }
 
2580
                        }
 
2581
                        break;
 
2582
 
 
2583
                case nod_post:
 
2584
                        {
 
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);
 
2590
                        }
 
2591
 
 
2592
                        // for an autocommit transaction, events can be posted
 
2593
                        // without any updates
 
2594
 
 
2595
                        if (transaction->tra_flags & TRA_autocommit)
 
2596
                                transaction->tra_flags |= TRA_perform_autocommit;
 
2597
 
 
2598
                        if (request->req_operation == jrd_req::req_evaluate)
 
2599
                                request->req_operation = jrd_req::req_return;
 
2600
                        node = node->nod_parent;
 
2601
                        break;
 
2602
 
 
2603
                case nod_message:
 
2604
                        if (request->req_operation == jrd_req::req_evaluate)
 
2605
                        {
 
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;
 
2610
                        }
 
2611
                        node = node->nod_parent;
 
2612
                        break;
 
2613
 
 
2614
                case nod_stall:
 
2615
                        node = stall(tdbb, node);
 
2616
                        break;
 
2617
 
 
2618
                case nod_select:
 
2619
                        node = selct(tdbb, node);
 
2620
                        break;
 
2621
 
 
2622
                case nod_send:
 
2623
                        node = send_msg(tdbb, node);
 
2624
                        break;
 
2625
 
 
2626
                case nod_store:
 
2627
                        {
 
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]))
 
2631
                                {
 
2632
                                        if (!top_node) {
 
2633
                                                top_node = node;
 
2634
                                                which_sto_trig = PRE_TRIG;
 
2635
                                        }
 
2636
                                        prev_node = node;
 
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;
 
2641
                                        }
 
2642
                                        if (top_node == prev_node && which_sto_trig == POST_TRIG) {
 
2643
                                                top_node = NULL;
 
2644
                                                which_sto_trig = ALL_TRIGS;
 
2645
                                        }
 
2646
                                        else
 
2647
                                                request->req_operation = jrd_req::req_evaluate;
 
2648
                                }
 
2649
                                else {
 
2650
                                        prev_node = node;
 
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;
 
2655
                                }
 
2656
                        }
 
2657
                        break;
 
2658
 
 
2659
#ifdef SCROLLABLE_CURSORS
 
2660
                case nod_seek:
 
2661
                        node = seek_rse(tdbb, request, node);
 
2662
                        break;
 
2663
#endif
 
2664
 
 
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;
 
2671
                        }
 
2672
                        node = node->nod_parent;
 
2673
                        break;
 
2674
 
 
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;
 
2681
                        }
 
2682
                        node = node->nod_parent;
 
2683
                        break;
 
2684
 
 
2685
                case nod_src_info:
 
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];
 
2691
                        }
 
2692
                        else
 
2693
                                node = node->nod_parent;
 
2694
                        break;
 
2695
 
 
2696
                case nod_init_variable:
 
2697
                        if (request->req_operation == jrd_req::req_evaluate)
 
2698
                        {
 
2699
                                const ItemInfo* itemInfo = 
 
2700
                                        reinterpret_cast<const ItemInfo*>(node->nod_arg[e_init_var_info]);
 
2701
                                if (itemInfo)
 
2702
                                {
 
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;
 
2705
 
 
2706
                                        to_desc->dsc_flags |= DSC_null;
 
2707
 
 
2708
                                        MapFieldInfo::ValueType fieldInfo;
 
2709
                                        if (itemInfo->fullDomain &&
 
2710
                                                request->req_map_field_info.get(itemInfo->field, fieldInfo) &&
 
2711
                                                fieldInfo.defaultValue)
 
2712
                                        {
 
2713
                                                dsc* value = EVL_expr(tdbb, fieldInfo.defaultValue);
 
2714
 
 
2715
                                                if (value && !(request->req_flags & req_null))
 
2716
                                                {
 
2717
                                                        to_desc->dsc_flags &= ~DSC_null;
 
2718
                                                        MOV_move(tdbb, value, to_desc);
 
2719
                                                }
 
2720
                                        }
 
2721
                                }
 
2722
 
 
2723
                                request->req_operation = jrd_req::req_return;
 
2724
                        }
 
2725
                        node = node->nod_parent;
 
2726
                        break;
 
2727
 
 
2728
                default:
 
2729
                        BUGCHECK(168);          /* msg 168 looper: action not yet implemented */
 
2730
                }
 
2731
 
 
2732
        }       // try
 
2733
        catch (const Firebird::Exception& ex) {
 
2734
 
 
2735
                Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
 
2736
 
 
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)
 
2745
                                {
 
2746
                                        ++transaction->tra_save_point->sav_verb_count;
 
2747
                                        verb_cleanup(tdbb, transaction);
 
2748
                                }
 
2749
                        }
 
2750
 
 
2751
                        ERR_punt();
 
2752
                }
 
2753
 
 
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);
 
2757
                }
 
2758
 
 
2759
                // Since an error happened, the current savepoint needs to be undone
 
2760
                if (transaction != dbb->dbb_sys_trans &&
 
2761
                        transaction->tra_save_point)
 
2762
                {
 
2763
                        ++transaction->tra_save_point->sav_verb_count;
 
2764
                        verb_cleanup(tdbb, transaction);
 
2765
                }
 
2766
 
 
2767
                error_pending = true;
 
2768
                catch_disabled = true;
 
2769
                request->req_operation = jrd_req::req_unwind;
 
2770
                request->req_label = 0;
 
2771
 
 
2772
                if (!(tdbb->tdbb_flags & TDBB_stack_trace_done) &&
 
2773
                        !(tdbb->tdbb_flags & TDBB_sys_error))
 
2774
                {
 
2775
                        stuff_stack_trace(request); 
 
2776
                        tdbb->tdbb_flags |= TDBB_stack_trace_done;
 
2777
                }
 
2778
        }
 
2779
        } // while()
 
2780
 
 
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
 
2784
 
 
2785
        if (!node
 
2786
#ifdef SCROLLABLE_CURSORS
 
2787
                && !(request->req_flags & req_async_processing)
 
2788
#endif
 
2789
                )
 
2790
        {
 
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)
 
2795
                        {
 
2796
                                if (*ptr)
 
2797
                                        RSE_close(tdbb, *ptr);
 
2798
                        }
 
2799
                }
 
2800
 
 
2801
                request->req_flags &= ~(req_active | req_reserved);
 
2802
                request->req_timestamp.invalidate();
 
2803
                release_blobs(tdbb, request);
 
2804
        }
 
2805
 
 
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;
 
2811
 
 
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
 
2815
 
 
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)
 
2821
                        {
 
2822
                                ++transaction->tra_save_point->sav_verb_count;
 
2823
                                verb_cleanup(tdbb, transaction);
 
2824
                        }
 
2825
                }
 
2826
 
 
2827
                ERR_punt();
 
2828
        }
 
2829
 
 
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
 
2833
 
 
2834
        if (request->req_flags & req_abort) {
 
2835
                ERR_post(isc_req_sync, 0);
 
2836
        }
 
2837
 
 
2838
        return node;
 
2839
}
 
2840
 
 
2841
 
 
2842
static jrd_nod* modify(thread_db* tdbb, jrd_nod* node, SSHORT which_trig)
 
2843
{
 
2844
/**************************************
 
2845
 *
 
2846
 *      m o d i f y
 
2847
 *
 
2848
 **************************************
 
2849
 *
 
2850
 * Functional description
 
2851
 *      Execute a MODIFY statement.
 
2852
 *
 
2853
 **************************************/
 
2854
        SET_TDBB(tdbb);
 
2855
        Database* dbb = tdbb->getDatabase();
 
2856
        BLKCHK(node, type_nod);
 
2857
 
 
2858
        jrd_req* request = tdbb->getRequest();
 
2859
        jrd_tra* transaction = request->req_transaction;
 
2860
        impure_state* impure = (impure_state*) ((SCHAR *) request + node->nod_impure);
 
2861
 
 
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;
 
2865
 
 
2866
        if (org_rpb->rpb_number.isBof() ||
 
2867
                (!relation->rel_view_rse && !org_rpb->rpb_number.isValid()))
 
2868
        {
 
2869
                ERR_post(isc_no_cur_rec, 0);
 
2870
        }
 
2871
 
 
2872
        const SSHORT new_stream = (USHORT)(IPTR) node->nod_arg[e_mod_new_stream];
 
2873
        record_param* new_rpb = &request->req_rpb[new_stream];
 
2874
 
 
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. */
 
2878
 
 
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;
 
2882
        }
 
2883
 
 
2884
        switch (request->req_operation) {
 
2885
        case jrd_req::req_evaluate:
 
2886
                request->req_records_affected.bumpModified(false);
 
2887
                break;
 
2888
 
 
2889
        case jrd_req::req_return:
 
2890
                if (impure->sta_state == 1)
 
2891
                {
 
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];
 
2899
                }
 
2900
 
 
2901
                if (impure->sta_state == 0)
 
2902
                {
 
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);
 
2906
 
 
2907
                        if (transaction != dbb->dbb_sys_trans)
 
2908
                                ++transaction->tra_save_point->sav_verb_count;
 
2909
 
 
2910
                        PreModifyEraseTriggers(tdbb, &relation->rel_pre_modify,
 
2911
                                                                which_trig, org_rpb, new_rpb->rpb_record, 
 
2912
                                                                jrd_req::req_trigger_update);
 
2913
 
 
2914
                        if (node->nod_arg[e_mod_validate]) {
 
2915
                                validate(tdbb, node->nod_arg[e_mod_validate]);
 
2916
                        }
 
2917
 
 
2918
                        if (relation->rel_file)
 
2919
                        {
 
2920
                                EXT_modify(org_rpb, new_rpb, transaction);
 
2921
                        }
 
2922
                        else if (relation->isVirtual()) {
 
2923
                                VirtualTable::modify(tdbb, org_rpb, new_rpb);
 
2924
                        }
 
2925
                        else if (!relation->rel_view_rse)
 
2926
                        {
 
2927
                                USHORT bad_index;
 
2928
                                jrd_rel* bad_relation = 0;
 
2929
 
 
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);
 
2934
 
 
2935
                                if (error_code) {
 
2936
                                        ERR_duplicate_error(error_code, bad_relation, bad_index);
 
2937
                                }
 
2938
                        }
 
2939
 
 
2940
                        jrd_req* trigger;
 
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)))
 
2946
                        {
 
2947
                                trigger_failure(tdbb, trigger);
 
2948
                        }
 
2949
 
 
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 */
 
2953
 
 
2954
                        if (!relation->rel_file &&
 
2955
                                !relation->rel_view_rse &&
 
2956
                                !relation->isVirtual())
 
2957
                        {
 
2958
                                USHORT bad_index;
 
2959
                                jrd_rel* bad_relation = 0;
 
2960
 
 
2961
                                const IDX_E error_code =
 
2962
                                        IDX_modify_check_constraints(tdbb, org_rpb, new_rpb, transaction,
 
2963
                                                                                                &bad_relation, &bad_index);
 
2964
 
 
2965
                                if (error_code) {
 
2966
                                        ERR_duplicate_error(error_code, bad_relation, bad_index);
 
2967
                                }
 
2968
                        }
 
2969
 
 
2970
                        if (transaction != dbb->dbb_sys_trans) {
 
2971
                                --transaction->tra_save_point->sav_verb_count;
 
2972
                        }
 
2973
 
 
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;
 
2980
                                }
 
2981
                        }
 
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);
 
2986
                                }
 
2987
                        }
 
2988
                        else if (relation->rel_file || !relation->rel_view_rse) {
 
2989
                                request->req_records_updated++;
 
2990
                                request->req_records_affected.bumpModified(true);
 
2991
                        }
 
2992
 
 
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];
 
2997
                        }
 
2998
                }
 
2999
 
 
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;
 
3004
                }
 
3005
 
 
3006
        default:
 
3007
                return node->nod_parent;
 
3008
        }
 
3009
 
 
3010
        impure->sta_state = 0;
 
3011
        RLCK_reserve_relation(tdbb, transaction, relation, true, true);
 
3012
 
 
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. */
 
3017
 
 
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;
 
3023
 
 
3024
        const Format* org_format;
 
3025
        Record* org_record = org_rpb->rpb_record;
 
3026
        if (!org_record) {
 
3027
                org_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;
 
3033
        }
 
3034
        else
 
3035
                org_format = org_record->rec_format;
 
3036
 
 
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. */
 
3040
 
 
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);
 
3044
        }
 
3045
        else {
 
3046
                DSC org_desc, new_desc;
 
3047
 
 
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 
 
3052
                         */
 
3053
                        CLEAR_NULL(new_record, i);
 
3054
                        if (EVL_field(new_rpb->rpb_relation, new_record, i, &new_desc)) {
 
3055
                                if (EVL_field
 
3056
                                        (org_rpb->rpb_relation, org_record, i,
 
3057
                                         &org_desc))
 
3058
                                {
 
3059
                                        MOV_move(tdbb, &org_desc, &new_desc);
 
3060
                                }
 
3061
                                else {
 
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;
 
3066
                                                memset(p, 0, n);
 
3067
                                        }
 
3068
                                }                               /* if (org_record) */
 
3069
                        }                                       /* if (new_record) */
 
3070
                }                                               /* for (fmt_count) */
 
3071
        }
 
3072
 
 
3073
        if (node->nod_arg[e_mod_map_view]) {
 
3074
                impure->sta_state = 1;
 
3075
                return node->nod_arg[e_mod_map_view];
 
3076
        }
 
3077
 
 
3078
        return node->nod_arg[e_mod_statement];
 
3079
}
 
3080
 
 
3081
static jrd_nod* receive_msg(thread_db* tdbb, jrd_nod* node)
 
3082
{
 
3083
/**************************************
 
3084
 *
 
3085
 *      r e c e i v e _ m s g
 
3086
 *
 
3087
 **************************************
 
3088
 *
 
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.
 
3094
 *
 
3095
 **************************************/
 
3096
        SET_TDBB(tdbb);
 
3097
        jrd_req* request = tdbb->getRequest();
 
3098
        BLKCHK(node, type_nod);
 
3099
 
 
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;
 
3105
                return node;
 
3106
 
 
3107
        case jrd_req::req_proceed:
 
3108
                request->req_operation = jrd_req::req_evaluate;
 
3109
                return (node->nod_arg[e_send_statement]);
 
3110
 
 
3111
        default:
 
3112
                return (node->nod_parent);
 
3113
        }
 
3114
}
 
3115
 
 
3116
 
 
3117
static void release_blobs(thread_db* tdbb, jrd_req* request)
 
3118
{
 
3119
/**************************************
 
3120
 *
 
3121
 *      r e l e a s e _ b l o b s
 
3122
 *
 
3123
 **************************************
 
3124
 *
 
3125
 * Functional description
 
3126
 *      Release temporary blobs assigned by this request.
 
3127
 *
 
3128
 **************************************/
 
3129
        SET_TDBB(tdbb);
 
3130
        DEV_BLKCHK(request, type_req);
 
3131
 
 
3132
        jrd_tra* transaction = request->req_transaction;
 
3133
        if (transaction) {
 
3134
                DEV_BLKCHK(transaction, type_tra);
 
3135
 
 
3136
                /* Release blobs bound to this request */
 
3137
 
 
3138
                if (request->req_blobs.getFirst()) 
 
3139
                        while (true) 
 
3140
                        {
 
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)
 
3145
                                        {
 
3146
                                                request->req_blobs.fastRemove();
 
3147
                                                current->bli_request = NULL;
 
3148
                                        }
 
3149
                                        else
 
3150
                                        {
 
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
 
3156
                                        }
 
3157
 
 
3158
                                        if (request->req_blobs.locate(Firebird::locGreat, blob_temp_id))
 
3159
                                                continue;
 
3160
                                        else
 
3161
                                                break;
 
3162
                                } 
 
3163
                                else {
 
3164
                                        // Blob accounting inconsistent
 
3165
                                        fb_assert(false);
 
3166
                                }
 
3167
                                if (!request->req_blobs.getNext())
 
3168
                                        break;
 
3169
                        }
 
3170
 
 
3171
                request->req_blobs.clear();
 
3172
 
 
3173
                /* Release arrays assigned by this request */
 
3174
 
 
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);
 
3179
                        else
 
3180
                                array = &(*array)->arr_next;
 
3181
                }
 
3182
        }
 
3183
}
 
3184
 
 
3185
 
 
3186
static void release_proc_save_points(jrd_req* request)
 
3187
{
 
3188
/**************************************
 
3189
 *
 
3190
 *      r e l e a s e _ p r o c _ s a v e _ p o i n t s
 
3191
 *
 
3192
 **************************************
 
3193
 *
 
3194
 * Functional description
 
3195
 *      Release temporary blobs assigned by this request.
 
3196
 *
 
3197
 **************************************/
 
3198
        Savepoint* sav_point = request->req_proc_sav_point;
 
3199
 
 
3200
        if (request->req_transaction) {
 
3201
                while (sav_point) {
 
3202
                        Savepoint* const temp_sav_point = sav_point->sav_next;
 
3203
                        delete sav_point;
 
3204
                        sav_point = temp_sav_point;
 
3205
                }
 
3206
        }
 
3207
        request->req_proc_sav_point = NULL;
 
3208
}
 
3209
 
 
3210
 
 
3211
#ifdef SCROLLABLE_CURSORS
 
3212
static jrd_nod* seek_rse(thread_db* tdbb, jrd_req* request, jrd_nod* node)
 
3213
{
 
3214
/**************************************
 
3215
 *
 
3216
 *      s e e k _ r s e
 
3217
 *
 
3218
 **************************************
 
3219
 *
 
3220
 * Functional description
 
3221
 *      Execute a nod_seek, which specifies 
 
3222
 *      a direction and offset in which to 
 
3223
 *      scroll a record selection expression.
 
3224
 *
 
3225
 **************************************/
 
3226
        SET_TDBB(tdbb);
 
3227
        DEV_BLKCHK(node, type_nod);
 
3228
 
 
3229
        if (request->req_operation == jrd_req::req_proceed) {
 
3230
                /* get input arguments */
 
3231
 
 
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;
 
3235
 
 
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;
 
3239
 
 
3240
                RecordSelExpr* rse = (RecordSelExpr*) node->nod_arg[e_seek_rse];
 
3241
 
 
3242
                seek_rsb(tdbb, request, rse->rse_rsb, direction, offset);
 
3243
 
 
3244
                request->req_operation = jrd_req::req_return;
 
3245
        }
 
3246
 
 
3247
        return node->nod_parent;
 
3248
}
 
3249
#endif
 
3250
 
 
3251
 
 
3252
#ifdef SCROLLABLE_CURSORS
 
3253
static void seek_rsb(
 
3254
                                         thread_db* tdbb,
 
3255
                                         jrd_req* request, RecordSource* rsb, USHORT direction, SLONG offset)
 
3256
{
 
3257
/**************************************
 
3258
 *
 
3259
 *      s e e k _ r s b
 
3260
 *
 
3261
 **************************************
 
3262
 *
 
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.
 
3267
 *
 
3268
 **************************************/
 
3269
        SET_TDBB(tdbb);
 
3270
        DEV_BLKCHK(rsb, type_rsb);
 
3271
        irsb* impure = (IRSB) ((UCHAR *) request + rsb->rsb_impure);
 
3272
 
 
3273
/* look past any boolean to the actual stream */
 
3274
 
 
3275
        if (rsb->rsb_type == rsb_boolean) {
 
3276
                seek_rsb(tdbb, request, rsb->rsb_next, direction, offset);
 
3277
 
 
3278
                /* set the backwards flag */
 
3279
 
 
3280
                const irsb* next_impure =
 
3281
                        (IRSB) ((UCHAR *) request + rsb->rsb_next->rsb_impure);
 
3282
 
 
3283
                if (next_impure->irsb_flags & irsb_last_backwards)
 
3284
                        impure->irsb_flags |= irsb_last_backwards;
 
3285
                else
 
3286
                        impure->irsb_flags &= ~irsb_last_backwards;
 
3287
                return;
 
3288
        }
 
3289
 
 
3290
/* do simple boundary checking for bof and eof */
 
3291
 
 
3292
        switch (direction) {
 
3293
        case blr_forward:
 
3294
                if (impure->irsb_flags & irsb_eof)
 
3295
                        ERR_post(isc_stream_eof, 0);
 
3296
                break;
 
3297
 
 
3298
        case blr_backward:
 
3299
                if (impure->irsb_flags & irsb_bof)
 
3300
                        ERR_post(isc_stream_bof, 0);
 
3301
                break;
 
3302
 
 
3303
        case blr_bof_forward:
 
3304
        case blr_eof_backward:
 
3305
                break;
 
3306
 
 
3307
        default:
 
3308
                // was: BUGCHECK(232);
 
3309
                // replaced with this error to be consistent with find()
 
3310
                ERR_post(isc_invalid_direction, 0);
 
3311
        }
 
3312
 
 
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) */
 
3318
 
 
3319
        if (offset > 0)
 
3320
                switch (direction) {
 
3321
                case blr_forward:
 
3322
                case blr_bof_forward:
 
3323
                        if (!(impure->irsb_flags & irsb_last_backwards)) {
 
3324
                                offset--;
 
3325
                                if (!(impure->irsb_flags & irsb_bof))
 
3326
                                        request->req_flags |= req_fetch_required;
 
3327
                        }
 
3328
                        break;
 
3329
 
 
3330
                case blr_backward:
 
3331
                case blr_eof_backward:
 
3332
                        if (impure->irsb_flags & irsb_last_backwards) {
 
3333
                                offset--;
 
3334
                                if (!(impure->irsb_flags & irsb_eof))
 
3335
                                        request->req_flags |= req_fetch_required;
 
3336
                        }
 
3337
                        break;
 
3338
                }
 
3339
 
 
3340
/* now do the actual seek */
 
3341
 
 
3342
        switch (direction) {
 
3343
        case blr_forward:                       /* go forward from the current location */
 
3344
 
 
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 */
 
3350
 
 
3351
                impure->irsb_flags &= ~irsb_last_backwards;
 
3352
 
 
3353
                while (offset) {
 
3354
                        offset--;
 
3355
                        if (!(RSE_get_record(tdbb, rsb, RSE_get_next)))
 
3356
                                break;
 
3357
                }
 
3358
                break;
 
3359
 
 
3360
        case blr_backward:                      /* go backward from the current location */
 
3361
 
 
3362
                impure->irsb_flags |= irsb_last_backwards;
 
3363
 
 
3364
                while (offset) {
 
3365
                        offset--;
 
3366
                        if (!(RSE_get_record(tdbb, rsb, RSE_get_next)))
 
3367
                                break;
 
3368
                }
 
3369
                break;
 
3370
 
 
3371
        case blr_bof_forward:           /* go forward from the beginning of the stream */
 
3372
 
 
3373
                RSE_close(tdbb, rsb);
 
3374
                RSE_open(tdbb, rsb);
 
3375
 
 
3376
                impure->irsb_flags &= ~irsb_last_backwards;
 
3377
 
 
3378
                while (offset) {
 
3379
                        offset--;
 
3380
                        if (!(RSE_get_record(tdbb, rsb, RSE_get_next)))
 
3381
                                break;
 
3382
                }
 
3383
                break;
 
3384
 
 
3385
        case blr_eof_backward:          /* go backward from the end of the stream */
 
3386
 
 
3387
                RSE_close(tdbb, rsb);
 
3388
                RSE_open(tdbb, rsb);
 
3389
 
 
3390
                /* if this is a stream type which uses bof and eof flags, 
 
3391
                   reverse the sense of bof and eof in this case */
 
3392
 
 
3393
                if (impure->irsb_flags & irsb_bof) {
 
3394
                        impure->irsb_flags &= ~irsb_bof;
 
3395
                        impure->irsb_flags |= irsb_eof;
 
3396
                }
 
3397
 
 
3398
                impure->irsb_flags |= irsb_last_backwards;
 
3399
 
 
3400
                while (offset) {
 
3401
                        offset--;
 
3402
                        if (!(RSE_get_record(tdbb, rsb, RSE_get_next)))
 
3403
                                break;
 
3404
                }
 
3405
                break;
 
3406
 
 
3407
        default:
 
3408
                // Should never go here, because of the boundary
 
3409
                // check above, but anyway...
 
3410
                BUGCHECK(232);
 
3411
        }
 
3412
}
 
3413
#endif
 
3414
 
 
3415
 
 
3416
static jrd_nod* selct(thread_db* tdbb, jrd_nod* node)
 
3417
{
 
3418
/**************************************
 
3419
 *
 
3420
 *      s e l e c t
 
3421
 *
 
3422
 **************************************
 
3423
 *
 
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."
 
3433
 *
 
3434
 **************************************/
 
3435
        SET_TDBB(tdbb);
 
3436
        jrd_req* request = tdbb->getRequest();
 
3437
        BLKCHK(node, type_nod);
 
3438
 
 
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;
 
3444
                return node;
 
3445
 
 
3446
        default:
 
3447
                return node->nod_parent;
 
3448
        }
 
3449
}
 
3450
 
 
3451
 
 
3452
 
 
3453
static jrd_nod* send_msg(thread_db* tdbb, jrd_nod* node)
 
3454
{
 
3455
/**************************************
 
3456
 *
 
3457
 *      s e n d _ m s g
 
3458
 *
 
3459
 **************************************
 
3460
 *
 
3461
 * Functional description
 
3462
 *      Execute a SEND statement.
 
3463
 *
 
3464
 **************************************/
 
3465
        SET_TDBB(tdbb);
 
3466
        jrd_req* request = tdbb->getRequest();
 
3467
        BLKCHK(node, type_nod);
 
3468
 
 
3469
        switch (request->req_operation) {
 
3470
        case jrd_req::req_evaluate:
 
3471
                return (node->nod_arg[e_send_statement]);
 
3472
 
 
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;
 
3477
                return node;
 
3478
 
 
3479
        case jrd_req::req_proceed:
 
3480
                request->req_operation = jrd_req::req_return;
 
3481
                return node->nod_parent;
 
3482
 
 
3483
        default:
 
3484
                return (node->nod_parent);
 
3485
        }
 
3486
}
 
3487
 
 
3488
 
 
3489
static void set_error(thread_db* tdbb, const xcp_repeat* exception, jrd_nod* msg_node)
 
3490
{
 
3491
/**************************************
 
3492
 *
 
3493
 *      s e t _ e r r o r
 
3494
 *
 
3495
 **************************************
 
3496
 *
 
3497
 * Functional description
 
3498
 *      Set status vector according to specified error condition
 
3499
 *      and jump to handle error accordingly.
 
3500
 *
 
3501
 **************************************/
 
3502
        Firebird::MetaName name, relation_name;
 
3503
        TEXT message[XCP_MESSAGE_LENGTH + 1];
 
3504
 
 
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)]; 
 
3508
 
 
3509
        SET_TDBB(tdbb);
 
3510
 
 
3511
        jrd_req* request = tdbb->getRequest();
 
3512
 
 
3513
        if (!exception) {
 
3514
                // retrieve the status vector and punt
 
3515
                request->req_last_xcp.copyTo(tdbb->tdbb_status_vector);
 
3516
                request->req_last_xcp.clear();
 
3517
                ERR_punt();
 
3518
        }
 
3519
 
 
3520
        USHORT length = 0;
 
3521
        
 
3522
        if (msg_node)
 
3523
        {
 
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))
 
3528
                {
 
3529
                        length = MOV_make_string(desc,
 
3530
                                                                         tdbb->getAttachment()->att_charset,
 
3531
                                                                         &string,
 
3532
                                                                         reinterpret_cast<vary*>(temp),
 
3533
                                                                         sizeof(temp));
 
3534
                        length = MIN(length, sizeof(message) - 1);
 
3535
 
 
3536
                        /* dimitr: or should we throw an error here, i.e.
 
3537
                                        replace the above assignment with the following lines:
 
3538
 
 
3539
                         if (length > sizeof(message) - 1)
 
3540
                                ERR_post(isc_imp_exc, isc_arg_gds, isc_blktoobig, 0);
 
3541
                        */
 
3542
 
 
3543
                        memcpy(message, string, length);
 
3544
                }
 
3545
                else
 
3546
                {
 
3547
                        length = 0;
 
3548
                }
 
3549
        }
 
3550
        message[length] = 0;
 
3551
 
 
3552
        const TEXT* s;
 
3553
 
 
3554
        switch (exception->xcp_type)
 
3555
        {
 
3556
        case xcp_sql_code:
 
3557
                ERR_post(isc_sqlerr, isc_arg_number, exception->xcp_code, 0);
 
3558
 
 
3559
        case xcp_gds_code:
 
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);
 
3566
                }
 
3567
                else
 
3568
                        ERR_post(exception->xcp_code, 0);
 
3569
 
 
3570
        case xcp_xcp_code:
 
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));
 
3575
 
 
3576
                if (message[0])
 
3577
                        s = message;
 
3578
                else if (temp[0])
 
3579
                        s = temp;
 
3580
                else
 
3581
                        s = NULL;
 
3582
 
 
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),
 
3587
                                         0);
 
3588
                else if (s)
 
3589
                        ERR_post(isc_except, isc_arg_number, exception->xcp_code,
 
3590
                                         isc_arg_gds, isc_random, isc_arg_string, ERR_cstring(s),
 
3591
                                         0);
 
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()),
 
3595
                                         0);
 
3596
                else            
 
3597
                        ERR_post(isc_except, isc_arg_number, exception->xcp_code, 0);
 
3598
 
 
3599
        default:
 
3600
                fb_assert(false);
 
3601
        }
 
3602
}
 
3603
 
 
3604
 
 
3605
static jrd_nod* stall(thread_db* tdbb, jrd_nod* node)
 
3606
{
 
3607
/**************************************
 
3608
 *
 
3609
 *      s t a l l 
 
3610
 *
 
3611
 **************************************
 
3612
 *
 
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.
 
3618
 *
 
3619
 **************************************/
 
3620
        SET_TDBB(tdbb);
 
3621
        jrd_req* request = tdbb->getRequest();
 
3622
        BLKCHK(node, type_nod);
 
3623
 
 
3624
        switch (request->req_operation) {
 
3625
        case jrd_req::req_sync:
 
3626
                return node->nod_parent;
 
3627
 
 
3628
        case jrd_req::req_proceed:
 
3629
                request->req_operation = jrd_req::req_return;
 
3630
                return node->nod_parent;
 
3631
 
 
3632
        default:
 
3633
                request->req_message = node;
 
3634
                request->req_operation = jrd_req::req_return;
 
3635
                request->req_flags |= req_stall;
 
3636
                return node;
 
3637
        }
 
3638
}
 
3639
 
 
3640
 
 
3641
static jrd_nod* store(thread_db* tdbb, jrd_nod* node, SSHORT which_trig)
 
3642
{
 
3643
/**************************************
 
3644
 *
 
3645
 *      s t o r e
 
3646
 *
 
3647
 **************************************
 
3648
 *
 
3649
 * Functional description
 
3650
 *      Execute a STORE statement.
 
3651
 *
 
3652
 **************************************/
 
3653
        jrd_req* trigger;
 
3654
 
 
3655
        SET_TDBB(tdbb);
 
3656
        Database* dbb = tdbb->getDatabase();
 
3657
        BLKCHK(node, type_nod);
 
3658
 
 
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;
 
3665
 
 
3666
        switch (request->req_operation) {
 
3667
        case jrd_req::req_evaluate:
 
3668
                if (request->req_records_affected.isReadOnly() &&
 
3669
                        !request->req_records_affected.hasCursor())
 
3670
                {
 
3671
                        request->req_records_affected.clear();
 
3672
                }
 
3673
                request->req_records_affected.bumpModified(false);
 
3674
                impure->sta_state = 0;
 
3675
                RLCK_reserve_relation(tdbb, transaction, relation, true, true);
 
3676
                break;
 
3677
 
 
3678
        case jrd_req::req_return:
 
3679
                if (impure->sta_state)
 
3680
                        return node->nod_parent;
 
3681
 
 
3682
                if (transaction != dbb->dbb_sys_trans)
 
3683
                        ++transaction->tra_save_point->sav_verb_count;
 
3684
 
 
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)))
 
3690
                {
 
3691
                        trigger_failure(tdbb, trigger);
 
3692
                }
 
3693
 
 
3694
                if (node->nod_arg[e_sto_validate]) {
 
3695
                        validate(tdbb, node->nod_arg[e_sto_validate]);
 
3696
                }
 
3697
 
 
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. */
 
3701
 
 
3702
                /* CVC: The code that was here was moved to its own routine: cleanup_rpb()
 
3703
                                and replaced by the call shown below. */
 
3704
 
 
3705
                cleanup_rpb(tdbb, rpb);
 
3706
 
 
3707
                if (relation->rel_file) {
 
3708
                        EXT_store(tdbb, rpb, transaction);
 
3709
                }
 
3710
                else if (relation->isVirtual()) {
 
3711
                        VirtualTable::store(tdbb, rpb);
 
3712
                }
 
3713
                else if (!relation->rel_view_rse)
 
3714
                {
 
3715
                        USHORT bad_index;
 
3716
                        jrd_rel* bad_relation = 0;
 
3717
 
 
3718
                        VIO_store(tdbb, rpb, transaction);
 
3719
                        const IDX_E error_code =
 
3720
                                IDX_store(tdbb, rpb, transaction,
 
3721
                                                  &bad_relation, &bad_index);
 
3722
 
 
3723
                        if (error_code) {
 
3724
                                ERR_duplicate_error(error_code, bad_relation, bad_index);
 
3725
                        }
 
3726
                }
 
3727
 
 
3728
                rpb->rpb_number.setValid(true);
 
3729
 
 
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)))
 
3735
                {
 
3736
                        trigger_failure(tdbb, trigger);
 
3737
                }
 
3738
 
 
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;
 
3745
                        }
 
3746
                }
 
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);
 
3751
                        }
 
3752
                }
 
3753
                else if (relation->rel_file || !relation->rel_view_rse) {
 
3754
                        request->req_records_inserted++;
 
3755
                        request->req_records_affected.bumpModified(true);
 
3756
                }
 
3757
 
 
3758
                if (transaction != dbb->dbb_sys_trans) {
 
3759
                        --transaction->tra_save_point->sav_verb_count;
 
3760
                }
 
3761
 
 
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];
 
3766
                }
 
3767
 
 
3768
        default:
 
3769
                return node->nod_parent;
 
3770
        }
 
3771
 
 
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
 
3775
   to "missing." */
 
3776
 
 
3777
        const Format* format = MET_current(tdbb, relation);
 
3778
        Record* record = VIO_record(tdbb, rpb, format, tdbb->getDefaultPool());
 
3779
 
 
3780
        rpb->rpb_address = record->rec_data;
 
3781
        rpb->rpb_length = format->fmt_length;
 
3782
        rpb->rpb_format_number = format->fmt_version;
 
3783
 
 
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. */
 
3789
 
 
3790
        memset(record->rec_data, 0, rpb->rpb_length);
 
3791
 
 
3792
/* Initialize all fields to missing */
 
3793
 
 
3794
        SSHORT n = (format->fmt_count + 7) >> 3;
 
3795
        if (n) {
 
3796
                memset(record->rec_data, 0xff, n);
 
3797
        }
 
3798
 
 
3799
        return node->nod_arg[e_sto_statement];
 
3800
}
 
3801
 
 
3802
 
 
3803
static bool test_and_fixup_error(thread_db* tdbb, const PsqlException* conditions,
 
3804
                                                                 jrd_req* request)
 
3805
{
 
3806
/**************************************
 
3807
 *
 
3808
 *      t e s t _ a n d _ f i x u p _ e r r o r
 
3809
 *
 
3810
 **************************************
 
3811
 *
 
3812
 * Functional description
 
3813
 *      Test for match of current state with list of error conditions.
 
3814
 *  Fix type and code of the exception.
 
3815
 *
 
3816
 **************************************/
 
3817
        SET_TDBB(tdbb);
 
3818
 
 
3819
        if (tdbb->tdbb_flags & TDBB_sys_error)
 
3820
                return false;
 
3821
 
 
3822
        ISC_STATUS* status_vector = tdbb->tdbb_status_vector;
 
3823
        const SSHORT sqlcode = gds__sqlcode(status_vector);
 
3824
 
 
3825
        bool found = false;
 
3826
 
 
3827
        for (USHORT i = 0; i < conditions->xcp_count; i++)
 
3828
        {
 
3829
                switch (conditions->xcp_rpt[i].xcp_type)
 
3830
                {
 
3831
                case xcp_sql_code:
 
3832
                        if (sqlcode == conditions->xcp_rpt[i].xcp_code)
 
3833
                        {
 
3834
                                found = true;
 
3835
                        }
 
3836
                        break;
 
3837
 
 
3838
                case xcp_gds_code:
 
3839
                        if (status_vector[1] == conditions->xcp_rpt[i].xcp_code)
 
3840
                        {
 
3841
                                found = true;
 
3842
                        }
 
3843
                        break;
 
3844
 
 
3845
                case xcp_xcp_code:
 
3846
                        {
 
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))
 
3851
                        {
 
3852
                                found = true;
 
3853
                        }
 
3854
 
 
3855
                        }
 
3856
                        break;
 
3857
 
 
3858
                case xcp_default:
 
3859
                        found = true;
 
3860
                        break;
 
3861
 
 
3862
                default:
 
3863
                        fb_assert(false);
 
3864
                }
 
3865
 
 
3866
                if (found)
 
3867
                {
 
3868
                        request->req_last_xcp.init(status_vector);
 
3869
                        status_vector[0] = 0;
 
3870
                        status_vector[1] = 0;
 
3871
                        break;
 
3872
                }
 
3873
    }
 
3874
 
 
3875
        return found;
 
3876
}
 
3877
 
 
3878
 
 
3879
static void trigger_failure(thread_db* tdbb, jrd_req* trigger)
 
3880
{
 
3881
/**************************************
 
3882
 *
 
3883
 *      t r i g g e r _ f a i l u r e
 
3884
 *
 
3885
 **************************************
 
3886
 *
 
3887
 * Functional description
 
3888
 *      Trigger failed, report error.
 
3889
 *
 
3890
 **************************************/
 
3891
 
 
3892
        SET_TDBB(tdbb);
 
3893
        EXE_unwind(tdbb, trigger);
 
3894
 
 
3895
        trigger->req_attachment = NULL;
 
3896
        trigger->req_flags &= ~req_in_use;
 
3897
        trigger->req_timestamp.invalidate();
 
3898
 
 
3899
        if (trigger->req_flags & req_leave)
 
3900
        {
 
3901
                trigger->req_flags &= ~req_leave;
 
3902
                const TEXT* msg =
 
3903
                        MET_trigger_msg(tdbb, trigger->req_trg_name, trigger->req_label);
 
3904
                if (msg)
 
3905
                {
 
3906
                        if (trigger->req_flags & req_sys_trigger)
 
3907
                        {
 
3908
                                ISC_STATUS code = PAR_symbol_to_gdscode(msg);
 
3909
                                if (code)
 
3910
                                {
 
3911
                                        ERR_post(isc_integ_fail,
 
3912
                                                         isc_arg_number, (SLONG) trigger->req_label,
 
3913
                                                         isc_arg_gds, code, 0);
 
3914
                                }
 
3915
                        }
 
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);
 
3919
                }
 
3920
                else
 
3921
                {
 
3922
                        ERR_post(isc_integ_fail, isc_arg_number,
 
3923
                                         (SLONG) trigger->req_label, 0);
 
3924
                }
 
3925
        }
 
3926
        else
 
3927
        {
 
3928
                ERR_punt();
 
3929
        }
 
3930
}
 
3931
 
 
3932
 
 
3933
static void validate(thread_db* tdbb, jrd_nod* list)
 
3934
{
 
3935
/**************************************
 
3936
 *
 
3937
 *      v a l i d a t e
 
3938
 *
 
3939
 **************************************
 
3940
 *
 
3941
 * Functional description
 
3942
 *      Execute a list of validation expressions.
 
3943
 *
 
3944
 **************************************/
 
3945
 
 
3946
        SET_TDBB(tdbb);
 
3947
        BLKCHK(list, type_nod);
 
3948
 
 
3949
        jrd_nod** ptr1 = list->nod_arg;
 
3950
        for (const jrd_nod* const* const end = ptr1 + list->nod_count;
 
3951
                 ptr1 < end; ptr1++)
 
3952
        {
 
3953
                jrd_req* request = tdbb->getRequest();
 
3954
                if (!EVL_boolean(tdbb, (*ptr1)->nod_arg[e_val_boolean]) &&
 
3955
                        !(request->req_flags & req_null))
 
3956
                {
 
3957
                        /* Validation error -- report result */
 
3958
                        const char* value;
 
3959
                        TEXT temp[128];
 
3960
 
 
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),
 
3966
                                                                sizeof(temp)) : 0;
 
3967
 
 
3968
                        if (!desc || (request->req_flags & req_null))
 
3969
                        {
 
3970
                                value = NULL_STRING_MARK;
 
3971
                        }
 
3972
                        else if (!length)
 
3973
                        {
 
3974
                                value = "";
 
3975
                        }
 
3976
                        else
 
3977
                        {
 
3978
                                value = ERR_string(value, length);
 
3979
                        }
 
3980
 
 
3981
                        const TEXT*     name = 0;
 
3982
                        if (node->nod_type == nod_field)
 
3983
                        {
 
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;
 
3987
 
 
3988
                                const jrd_fld* field;
 
3989
                                const vec<jrd_fld*>* vector = relation->rel_fields;
 
3990
                                if (vector && id < vector->count() &&
 
3991
                                        (field = (*vector)[id]))
 
3992
                                {
 
3993
                                        name = ERR_cstring(field->fld_name.c_str());
 
3994
                                }
 
3995
                        }
 
3996
 
 
3997
                        if (!name)
 
3998
                        {
 
3999
                                name = UNKNOWN_STRING_MARK;
 
4000
                        }
 
4001
 
 
4002
                        ERR_post(isc_not_valid, isc_arg_string, name,
 
4003
                                         isc_arg_string, value, 0);
 
4004
                }
 
4005
        }
 
4006
}
 
4007
 
 
4008
 
 
4009
inline void verb_cleanup(thread_db* tdbb, jrd_tra* transaction)
 
4010
{
 
4011
/**************************************
 
4012
 *
 
4013
 *      v e r b _ c l e a n u p
 
4014
 *
 
4015
 **************************************
 
4016
 *
 
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.
 
4022
 *
 
4023
 **************************************/
 
4024
        try {
 
4025
            VIO_verb_cleanup(tdbb, transaction);
 
4026
    }
 
4027
        catch (const Firebird::Exception&) {
 
4028
                if (tdbb->getDatabase()->dbb_flags & DBB_bugcheck) {
 
4029
                        Firebird::status_exception::raise(tdbb->tdbb_status_vector);
 
4030
                }
 
4031
        BUGCHECK(290); // msg 290 error during savepoint backout
 
4032
        }
 
4033
}