8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-27 04:43:02 +01:00
firebird-mirror/src/jrd/blb.cpp

2460 lines
64 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: JRD Access Method
2003-10-08 10:42:48 +02:00
* MODULE: blb.cpp
2001-05-23 15:26:42 +02:00
* DESCRIPTION: Blob handler
*
* The contents of this file are subject to the Interbase Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy
* of the License at http://www.Inprise.com/IPL.html
*
* Software distributed under the License is distributed on an
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
* or implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code was created by Inprise Corporation
* and its predecessors. Portions created by Inprise Corporation are
* Copyright (C) Inprise Corporation.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
2002-06-29 15:03:13 +02:00
*
* 2001.6.23 Claudio Valderrama: BLB_move_from_string to accept assignments
* from string to blob field. First use was to allow inserting a literal string
* in a blob field without requiring an UDF.
*
* 2001.07.06 Sean Leyne - Code Cleanup, removed "#ifdef READONLY_DATABASE"
* conditionals, as the engine now fully supports
* readonly databases.
*
* 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "MPEXL" port
* 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "DecOSF" port
*
2001-05-23 15:26:42 +02:00
*/
/*
$Id: blb.cpp,v 1.64 2004-03-30 08:34:14 robocop Exp $
2001-05-23 15:26:42 +02:00
*/
#include "firebird.h"
2001-05-23 15:26:42 +02:00
#include <string.h>
2004-03-22 12:38:23 +01:00
#include "../jrd/common.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/jrd.h"
#include "../jrd/tra.h"
#include "../jrd/val.h"
#include "../jrd/exe.h"
#include "../jrd/req.h"
#include "../jrd/blb.h"
#include "../jrd/ods.h"
#include "../jrd/lls.h"
#include "gen/iberror.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/blob_filter.h"
#include "../jrd/sdl.h"
#include "../jrd/intl.h"
#include "../jrd/cch.h"
#include "../jrd/common.h"
#include "../jrd/constants.h"
#include "../jrd/gdsassert.h"
#include "../jrd/all_proto.h"
#include "../jrd/blb_proto.h"
#include "../jrd/blf_proto.h"
#include "../jrd/cch_proto.h"
#include "../jrd/dpm_proto.h"
#include "../jrd/err_proto.h"
#include "../jrd/evl_proto.h"
#include "../jrd/filte_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/jrd_proto.h"
#include "../jrd/met_proto.h"
#include "../jrd/mov_proto.h"
#include "../jrd/pag_proto.h"
#include "../jrd/sdl_proto.h"
#include "../jrd/thd_proto.h"
2002-06-29 15:03:13 +02:00
#include "../jrd/dsc_proto.h"
2001-05-23 15:26:42 +02:00
using namespace Jrd;
typedef Ods::blob_page blob_page;
2001-05-23 15:26:42 +02:00
inline bool SEGMENTED(const blb* blob)
{
return !(blob->blb_flags & BLB_stream);
}
2001-05-23 15:26:42 +02:00
static ArrayField* alloc_array(jrd_tra*, internal_array_desc*);
static blb* allocate_blob(thread_db*, jrd_tra*);
static ISC_STATUS blob_filter(USHORT, BlobControl*, SSHORT, SLONG);
static void check_BID_validity(const blb*, thread_db*);
static blb* copy_blob(thread_db*, const bid*, bid*);
static void delete_blob(thread_db*, blb*, ULONG);
static void delete_blob_id(thread_db*, const bid*, SLONG, jrd_rel*);
static ArrayField* find_array(jrd_tra*, const bid*);
static BlobFilter* find_filter(thread_db*, SSHORT, SSHORT);
static blob_page* get_next_page(thread_db*, blb*, WIN *);
#ifdef REPLAY_OSRI_API_CALLS_SUBSYSTEM
static void get_replay_blob(thread_db*, const bid*);
#endif
static void insert_page(thread_db*, blb*);
static void release_blob(blb*, const bool);
static void slice_callback(array_slice*, ULONG, dsc*);
static blb* store_array(thread_db*, jrd_tra*, bid*);
2001-05-23 15:26:42 +02:00
void BLB_cancel(thread_db* tdbb, blb* blob)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* B L B _ c a n c e l
*
**************************************
*
* Functional description
* Abort a blob operation. If the blob is a partially created
* temporary blob, free up any allocated pages. In any case,
* get rid of the blob block.
*
**************************************/
SET_TDBB(tdbb);
/* Release filter control resources */
if (blob->blb_flags & BLB_temporary)
delete_blob(tdbb, blob, 0);
2003-10-08 10:42:48 +02:00
release_blob(blob, true);
2001-05-23 15:26:42 +02:00
}
void BLB_close(thread_db* tdbb, class blb* blob)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* B L B _ c l o s e
*
**************************************
*
* Functional description
* Close a blob. If the blob is open for retrieval, release the
2001-05-23 15:26:42 +02:00
* blob block. If it's a temporary blob, flush out the last page
* (if necessary) in preparation for materialization.
*
**************************************/
SET_TDBB(tdbb);
/* Release filter control resources */
if (blob->blb_filter)
BLF_close_blob(tdbb, &blob->blb_filter);
blob->blb_flags |= BLB_closed;
if (!(blob->blb_flags & BLB_temporary)) {
2003-10-08 10:42:48 +02:00
release_blob(blob, true);
2001-05-23 15:26:42 +02:00
return;
}
if (blob->blb_level >= 1
&& blob->blb_space_remaining < blob->blb_clump_size)
{
insert_page(tdbb, blob);
}
2001-05-23 15:26:42 +02:00
}
blb* BLB_create(thread_db* tdbb, jrd_tra* transaction, bid* blob_id)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* B L B _ c r e a t e
*
**************************************
*
* Functional description
* Create a shiney, new, empty blob.
*
**************************************/
SET_TDBB(tdbb);
return BLB_create2(tdbb, transaction, blob_id, 0, NULL);
2001-05-23 15:26:42 +02:00
}
blb* BLB_create2(thread_db* tdbb,
jrd_tra* transaction, bid* blob_id,
USHORT bpb_length, const UCHAR* bpb)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* B L B _ c r e a t e 2
*
**************************************
*
* Functional description
* Create a shiney, new, empty blob.
* Basically BLB_create() with BPB structure.
*
**************************************/
SET_TDBB(tdbb);
2004-03-07 08:58:55 +01:00
Database* dbb = tdbb->tdbb_database;
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
if (dbb->dbb_flags & DBB_read_only)
ERR_post(isc_read_only_database, 0);
if (transaction->tra_flags & TRA_readonly)
ERR_post(isc_read_only_trans, 0);
2001-05-23 15:26:42 +02:00
/* Create a blob large enough to hold a single data page */
SSHORT from, to;
SSHORT from_charset, to_charset;
const SSHORT type = gds__parse_bpb2(bpb_length,
2001-05-23 15:26:42 +02:00
bpb,
&from,
&to,
reinterpret_cast<USHORT*>(&from_charset),
reinterpret_cast<USHORT*>(&to_charset));
blb* blob = allocate_blob(tdbb, transaction);
2001-05-23 15:26:42 +02:00
if (type)
blob->blb_flags |= BLB_stream;
blob->blb_source_interp = from_charset;
blob->blb_target_interp = to_charset;
blob->blb_sub_type = to;
bool filter_required = false;
BlobFilter* filter = NULL;
2001-05-23 15:26:42 +02:00
if (to && from != to) {
filter = find_filter(tdbb, from, to);
2003-10-08 10:42:48 +02:00
filter_required = true;
2001-05-23 15:26:42 +02:00
}
else if (to == BLOB_text && (from_charset != to_charset)) {
if (from_charset == CS_dynamic)
from_charset = tdbb->tdbb_attachment->att_charset;
if (to_charset == CS_dynamic)
to_charset = tdbb->tdbb_attachment->att_charset;
if ((to_charset != CS_NONE) && (from_charset != to_charset)) {
filter = FB_NEW(*dbb->dbb_permanent) BlobFilter(*dbb->dbb_permanent);
filter->blf_filter = filter_transliterate_text;
2003-10-08 10:42:48 +02:00
filter_required = true;
2001-05-23 15:26:42 +02:00
}
}
if (filter_required) {
if (BLF_create_blob(tdbb,
transaction,
&blob->blb_filter,
blob_id,
2001-05-23 15:26:42 +02:00
bpb_length,
bpb,
// CVC: This cast is very suspicious to me.
// We have to research if seek really gets params
// from the control struct instead. Maybe y-valve has a special case.
// Otherwise, blob_filter's sig would be like any filter.
reinterpret_cast<FPTR_BFILTER_CALLBACK>(blob_filter),
filter))
{
ERR_punt();
}
2001-05-23 15:26:42 +02:00
blob->blb_flags |= BLB_temporary;
return blob;
}
blob->blb_space_remaining = blob->blb_clump_size;
blob->blb_flags |= BLB_temporary;
/* Set up for a "small" blob -- a blob that fits on an ordinary data page */
2004-02-02 12:02:12 +01:00
blob_page* page = (blob_page*) blob->blb_data;
page->pag_type = pag_blob;
2004-02-02 12:02:12 +01:00
blob->blb_segment = (UCHAR *) page->blp_page;
2001-05-23 15:26:42 +02:00
/* Format blob id and return blob handle */
blob_id->bid_stuff.bid_temp_id = blob->blb_temp_id;
2001-05-23 15:26:42 +02:00
blob_id->bid_relation_id = 0;
return blob;
}
void BLB_garbage_collect(
thread_db* tdbb,
2001-05-23 15:26:42 +02:00
LLS going,
LLS staying, SLONG prior_page, jrd_rel* relation)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* B L B _ g a r b a g e _ c o l l e c t
*
**************************************
*
* Functional description
* Garbage collect indices and blobs. Garbage_collect is passed a
* stack of record blocks of records that are being deleted and a stack
* of records blocks of records that are staying. Garbage_collect
* can be called from four operations:
*
* VIO_backout -- removing top record
* expunge -- removing all versions of a record
* purge -- removing all but top version of a record
* update_in_place -- replace the top level record.
*
*
**************************************/
2001-12-24 03:51:06 +01:00
DSC desc1, desc2;
2001-05-23 15:26:42 +02:00
SET_TDBB(tdbb);
/* Loop thru records on the way out looking for blobs to garbage collect */
for (lls* stack1 = going; stack1; stack1 = stack1->lls_next) {
Record* rec1 = (Record*) stack1->lls_object;
if (!rec1)
2001-05-23 15:26:42 +02:00
continue;
2004-03-30 06:10:52 +02:00
const Format* format = (Format*) rec1->rec_format;
2001-05-23 15:26:42 +02:00
/* Look for active blob records */
for (USHORT id = 0; id < format->fmt_count; id++) {
2001-12-24 03:51:06 +01:00
if (!DTYPE_IS_BLOB(format->fmt_desc[id].dsc_dtype)
|| !EVL_field(0, rec1, id, &desc1))
{
2001-05-23 15:26:42 +02:00
continue;
}
const bid* blob = (bid*) desc1.dsc_address;
2001-05-23 15:26:42 +02:00
/* Got active blob, cancel it out of any remaining records on the way out */
lls* stack2;
2001-05-23 15:26:42 +02:00
for (stack2 = stack1->lls_next; stack2; stack2 = stack2->lls_next) {
Record* rec2 = (Record*) stack2->lls_object;
2001-05-23 15:26:42 +02:00
if (!EVL_field(0, rec2, id, &desc2))
continue;
const bid* blob2 = (bid*) desc2.dsc_address;
2001-05-23 15:26:42 +02:00
if (blob->bid_relation_id == blob2->bid_relation_id &&
blob->bid_stuff.bid_number == blob2->bid_stuff.bid_number)
{
2001-05-23 15:26:42 +02:00
SET_NULL(rec2, id);
}
2001-05-23 15:26:42 +02:00
}
/* Make sure the blob doesn't stack in any record remaining */
for (stack2 = staying; stack2; stack2 = stack2->lls_next) {
Record* rec2 = (Record*) stack2->lls_object;
2001-05-23 15:26:42 +02:00
if (!EVL_field(0, rec2, id, &desc2))
continue;
const bid* blob2 = (bid*) desc2.dsc_address;
2001-05-23 15:26:42 +02:00
if (blob->bid_relation_id == blob2->bid_relation_id &&
blob->bid_stuff.bid_number == blob2->bid_stuff.bid_number)
{
2001-05-23 15:26:42 +02:00
break;
}
2001-05-23 15:26:42 +02:00
}
if (stack2)
continue;
/* Get rid of blob */
delete_blob_id(tdbb, blob, prior_page, relation);
}
}
}
blb* BLB_get_array(thread_db* tdbb, jrd_tra* transaction, const bid* blob_id,
internal_array_desc* desc)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* B L B _ g e t _ a r r a y
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Get array blob and array descriptor.
*
**************************************/
SET_TDBB(tdbb);
blb* blob = BLB_open2(tdbb, transaction, blob_id, 0, 0);
2001-05-23 15:26:42 +02:00
if (blob->blb_length < sizeof(internal_array_desc)) {
2001-05-23 15:26:42 +02:00
BLB_close(tdbb, blob);
IBERROR(193); /* msg 193 null or invalid array */
}
BLB_get_segment(tdbb, blob, reinterpret_cast<UCHAR*>(desc), sizeof(internal_array_desc));
2001-05-23 15:26:42 +02:00
const USHORT n = desc->iad_length - sizeof(internal_array_desc);
if (n) {
BLB_get_segment(tdbb, blob, reinterpret_cast<UCHAR*>(desc) + sizeof(internal_array_desc), n);
}
2001-05-23 15:26:42 +02:00
return blob;
}
SLONG BLB_get_data(thread_db* tdbb, blb* blob, UCHAR* buffer, SLONG length)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* B L B _ g e t _ d a t a
*
**************************************
*
* Functional description
* Get a large hunk of data from a blob, which can then be
* closed. Return total number of bytes read. Don't worry
* about segment boundaries.
*
**************************************/
SET_TDBB(tdbb);
// Redundant cast.
BLOB_PTR* p = (BLOB_PTR*) buffer;
2001-05-23 15:26:42 +02:00
while (length > 0) {
/* I have no idea why this limit is 32768 instead of 32767
* 1994-August-12 David Schnepper
2001-05-23 15:26:42 +02:00
*/
USHORT n = (USHORT) MIN(length, (SLONG) 32768);
2001-05-23 15:26:42 +02:00
n = BLB_get_segment(tdbb, blob, p, n);
p += n;
length -= n;
if (blob->blb_flags & BLB_eof)
break;
}
BLB_close(tdbb, blob);
// Redundant cast
2001-05-23 15:26:42 +02:00
return (SLONG) ((BLOB_PTR *) p - (BLOB_PTR *) buffer);
}
USHORT BLB_get_segment(thread_db* tdbb,
blb* blob, UCHAR* segment, USHORT buffer_length)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* B L B _ g e t _ s e g m e n t
*
**************************************
*
* Functional description
* Get next segment or fragment from a blob. Return the number
* of bytes returned.
*
**************************************/
2003-04-10 08:49:16 +02:00
ISC_STATUS status;
USHORT l;
2001-05-23 15:26:42 +02:00
SET_TDBB(tdbb);
2004-03-07 08:58:55 +01:00
Database* dbb = tdbb->tdbb_database;
2001-05-23 15:26:42 +02:00
#ifdef SUPERSERVER
2001-05-23 15:26:42 +02:00
if (--tdbb->tdbb_quantum < 0 && !tdbb->tdbb_inhibit)
2003-12-22 11:00:59 +01:00
JRD_reschedule(tdbb, 0, true);
2001-05-23 15:26:42 +02:00
#endif
/* If we reached end of file, we're still there */
if (blob->blb_flags & BLB_eof)
return 0;
if (blob->blb_filter) {
blob->blb_fragment_size = 0;
USHORT tmp_len = 0;
2001-12-24 03:51:06 +01:00
if ( (status =
BLF_get_segment(tdbb, &blob->blb_filter, &tmp_len, buffer_length,
segment)) )
{
if (status == isc_segstr_eof)
2001-05-23 15:26:42 +02:00
blob->blb_flags |= BLB_eof;
else if (status == isc_segment)
2001-05-23 15:26:42 +02:00
blob->blb_fragment_size = 1;
else
ERR_punt();
}
return tmp_len;
2001-05-23 15:26:42 +02:00
}
/* If there is a seek pending, handle it here */
USHORT seek = 0;
2001-05-23 15:26:42 +02:00
if (blob->blb_flags & BLB_seek) {
if (blob->blb_seek >= blob->blb_length) {
blob->blb_flags |= BLB_eof;
return 0;
}
l = dbb->dbb_page_size - BLP_SIZE;
blob->blb_sequence = blob->blb_seek / l;
seek = (USHORT)(blob->blb_seek % l); // safe cast
blob->blb_flags &= ~BLB_seek;
blob->blb_fragment_size = 0;
if (blob->blb_level) {
blob->blb_space_remaining = 0;
blob->blb_segment = NULL;
}
else {
blob->blb_space_remaining = blob->blb_length - seek;
blob->blb_segment = blob->blb_data + seek;
}
}
if (!blob->blb_space_remaining && blob->blb_segment) {
blob->blb_flags |= BLB_eof;
return 0;
}
/* Figure out how much data is to be moved, move it, and if necessary,
advance to the next page. The length is a function of segment
size (or fragment size), buffer size, and amount of data left
in the blob. */
BLOB_PTR* to = segment;
const BLOB_PTR* from = blob->blb_segment;
USHORT length = blob->blb_space_remaining;
2003-10-08 10:42:48 +02:00
bool active_page = false;
WIN window(-1); // there was no initialization of win_page here.
2001-05-23 15:26:42 +02:00
if (blob->blb_flags & BLB_large_scan) {
window.win_flags = WIN_large_scan;
window.win_scans = 1;
}
2003-10-08 10:42:48 +02:00
while (true) {
2001-05-23 15:26:42 +02:00
/* If the blob is segmented, and this isn't a fragment, pick up
the length of the next segment. */
if (SEGMENTED(blob) && !blob->blb_fragment_size) {
2001-05-23 15:26:42 +02:00
while (length < 2) {
if (active_page) {
if (window.win_flags & WIN_large_scan)
CCH_RELEASE_TAIL(tdbb, &window);
else
CCH_RELEASE(tdbb, &window);
}
2004-02-02 12:02:12 +01:00
const blob_page* page = get_next_page(tdbb, blob, &window);
if (!page) {
2001-05-23 15:26:42 +02:00
blob->blb_flags |= BLB_eof;
return 0;
}
2004-02-02 12:02:12 +01:00
from = (const UCHAR*) page->blp_page;
2001-05-23 15:26:42 +02:00
length = page->blp_length;
2003-10-08 10:42:48 +02:00
active_page = true;
2001-05-23 15:26:42 +02:00
}
UCHAR* p = (UCHAR *) & blob->blb_fragment_size;
2001-05-23 15:26:42 +02:00
*p++ = *from++;
*p++ = *from++;
length -= 2;
}
/* Figure out how much data can be moved. Then account for the
space, and move the data */
l = MIN(buffer_length, length);
if (SEGMENTED(blob)) {
2001-05-23 15:26:42 +02:00
l = MIN(l, blob->blb_fragment_size);
blob->blb_fragment_size -= l;
}
length -= l;
buffer_length -= l;
if (((U_IPTR) from & (ALIGNMENT - 1))
|| ((U_IPTR) to & (ALIGNMENT - 1)))
{
2001-05-23 15:26:42 +02:00
MOVE_FAST(from, to, l);
}
2001-05-23 15:26:42 +02:00
else
MOVE_FASTER(from, to, l);
to += l;
from += l;
/* If we ran out of space in the data clump, and there is a next
clump, get it. */
if (!length) {
if (active_page) {
if (window.win_flags & WIN_large_scan)
CCH_RELEASE_TAIL(tdbb, &window);
else
CCH_RELEASE(tdbb, &window);
}
2004-02-02 12:02:12 +01:00
const blob_page* page = get_next_page(tdbb, blob, &window);
if (!page) {
2003-10-08 10:42:48 +02:00
active_page = false;
2001-05-23 15:26:42 +02:00
break;
}
2004-02-02 12:02:12 +01:00
from = reinterpret_cast<const UCHAR*>(page->blp_page) + seek;
2001-05-23 15:26:42 +02:00
length = page->blp_length - seek;
seek = 0;
2003-10-08 10:42:48 +02:00
active_page = true;
2001-05-23 15:26:42 +02:00
}
/* If either the buffer or the fragment is exhausted, we're
done. */
if (!buffer_length || (SEGMENTED(blob) && !blob->blb_fragment_size))
2001-05-23 15:26:42 +02:00
break;
}
if (active_page) {
if (((U_IPTR) from & (ALIGNMENT - 1))
|| ((U_IPTR) blob->blb_data & (ALIGNMENT - 1)))
{
2001-05-23 15:26:42 +02:00
MOVE_FAST(from, blob->blb_data, length);
}
2001-05-23 15:26:42 +02:00
else
MOVE_FASTER(from, blob->blb_data, length);
from = blob->blb_data;
if (window.win_flags & WIN_large_scan)
CCH_RELEASE_TAIL(tdbb, &window);
else
CCH_RELEASE(tdbb, &window);
}
blob->blb_segment = const_cast<BLOB_PTR*>(from); // safe cast
2001-05-23 15:26:42 +02:00
blob->blb_space_remaining = length;
length = to - segment;
blob->blb_seek += length;
/* If this is a stream blob, fake fragment unless we're at the end */
if (!SEGMENTED(blob)) { // stream blob
2001-05-23 15:26:42 +02:00
blob->blb_fragment_size =
(blob->blb_seek == blob->blb_length) ? 0 : 1;
}
2001-05-23 15:26:42 +02:00
return length;
}
SLONG BLB_get_slice(thread_db* tdbb,
jrd_tra* transaction,
const bid* blob_id,
const UCHAR* sdl,
2001-05-23 15:26:42 +02:00
USHORT param_length,
const SLONG* param, SLONG slice_length, UCHAR* slice_addr)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* B L B _ g e t _ s l i c e
*
**************************************
*
* Functional description
* Fetch a slice of an array.
*
**************************************/
2003-04-10 08:49:16 +02:00
ISC_STATUS status;
2001-05-23 15:26:42 +02:00
SET_TDBB(tdbb);
2004-03-07 08:58:55 +01:00
Database* database = GET_DBB;
2001-05-23 15:26:42 +02:00
tdbb->tdbb_default = transaction->tra_pool;
/* Checkout slice description language */
SLONG variables[64];
sdl_info info;
2001-05-23 15:26:42 +02:00
MOVE_FAST(param, variables, MIN(sizeof(variables), param_length));
if (SDL_info(tdbb->tdbb_status_vector, sdl, &info, variables)) {
2001-05-23 15:26:42 +02:00
ERR_punt();
}
2001-05-23 15:26:42 +02:00
SLONG stuff[IAD_LEN(16) / 4];
internal_array_desc* desc = (internal_array_desc*) stuff;
blb* blob = BLB_get_array(tdbb, transaction, blob_id, desc);
SLONG length = desc->iad_total_length;
2001-05-23 15:26:42 +02:00
/* Get someplace to put data */
UCHAR* data = (UCHAR*) database->dbb_permanent->allocate(desc->iad_total_length, 0
#ifdef DEBUG_GDS_ALLOC
,__FILE__, __LINE__
#endif
);
2001-05-23 15:26:42 +02:00
/* zero out memory, so that it does not have to be done for
each element */
memset(data, 0, desc->iad_total_length);
2001-05-23 15:26:42 +02:00
SLONG offset = 0;
2001-05-23 15:26:42 +02:00
array_slice arg;
2001-05-23 15:26:42 +02:00
/* Trap any potential errors */
2001-12-24 03:51:06 +01:00
try {
2001-05-23 15:26:42 +02:00
/* If we know something about the subscript bounds, prepare
to fetch only stuff we really care about */
if (info.sdl_info_dimensions) {
const SLONG from =
2001-05-23 15:26:42 +02:00
SDL_compute_subscript(tdbb->tdbb_status_vector, desc,
info.sdl_info_dimensions,
info.sdl_info_lower);
const SLONG to =
2001-05-23 15:26:42 +02:00
SDL_compute_subscript(tdbb->tdbb_status_vector, desc,
info.sdl_info_dimensions,
info.sdl_info_upper);
if (from != -1 && to != -1) {
if (from) {
offset = from * desc->iad_element_length;
BLB_lseek(blob, 0, offset + (SLONG) desc->iad_length);
2001-05-23 15:26:42 +02:00
}
length = (to - from + 1) * desc->iad_element_length;
2001-05-23 15:26:42 +02:00
}
}
length = BLB_get_data(tdbb, blob, data + offset, length) + offset;
/* Walk array */
arg.slice_desc = info.sdl_info_element;
2003-10-20 12:53:52 +02:00
arg.slice_desc.dsc_address = slice_addr;
arg.slice_end = (BLOB_PTR*) slice_addr + slice_length;
2001-05-23 15:26:42 +02:00
arg.slice_count = 0;
arg.slice_element_length = info.sdl_info_element.dsc_length;
arg.slice_direction = FALSE; /* fetching from array */
arg.slice_high_water = (BLOB_PTR*) data + length;
arg.slice_base = (BLOB_PTR*) data + offset;
2001-05-23 15:26:42 +02:00
status = SDL_walk(tdbb->tdbb_status_vector,
sdl,
true,
2001-05-23 15:26:42 +02:00
data,
desc,
variables,
slice_callback,
2003-10-20 12:53:52 +02:00
&arg);
2001-05-23 15:26:42 +02:00
2003-10-20 12:53:52 +02:00
database->dbb_permanent->deallocate(data);
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
if (status) {
2001-05-23 15:26:42 +02:00
ERR_punt();
2001-12-24 03:51:06 +01:00
}
} // try
2003-02-13 14:33:57 +01:00
catch (const std::exception&) {
2003-10-20 12:53:52 +02:00
database->dbb_permanent->deallocate(data);
throw;
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
return (SLONG) (arg.slice_count * arg.slice_element_length);
}
SLONG BLB_lseek(blb* blob, USHORT mode, SLONG offset)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* B L B _ l s e e k
*
**************************************
*
* Functional description
* Position a blob for direct access. Seek is only defined for stream
* type blobs.
*
**************************************/
if (!(blob->blb_flags & BLB_stream))
ERR_post(isc_bad_segstr_type, 0);
2001-05-23 15:26:42 +02:00
if (mode == 1)
offset += blob->blb_seek;
else if (mode == 2)
offset = blob->blb_length + offset;
if (offset < 0)
offset = 0;
if (offset > (SLONG) blob->blb_length)
2001-05-23 15:26:42 +02:00
offset = blob->blb_length;
blob->blb_seek = offset;
blob->blb_flags |= BLB_seek;
blob->blb_flags &= ~BLB_eof;
return offset;
}
#ifdef REPLAY_OSRI_API_CALLS_SUBSYSTEM
2001-12-24 03:51:06 +01:00
void BLB_map_blobs(thread_db* tdbb, blb* old_blob, blb* new_blob)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* B L B _ m a p _ b l o b s
*
**************************************
*
* Functional description
* Form a mapping between two blobs.
* Since the blobs have been newly created
2001-05-23 15:26:42 +02:00
* in this session, only the second part of
* the blob id is significant. At the moment
* this is intended solely for REPLAY, when
2001-05-23 15:26:42 +02:00
* replaying a log.
*
**************************************/
SET_TDBB(tdbb);
2004-03-07 08:58:55 +01:00
Database* dbb = tdbb->tdbb_database;
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
blb_map* new_map = FB_NEW(*dbb->dbb_permanent) blb_map();
2001-05-23 15:26:42 +02:00
new_map->map_old_blob = old_blob;
new_map->map_new_blob = new_blob;
new_map->map_next = dbb->dbb_blob_map;
dbb->dbb_blob_map = new_map;
}
2001-12-24 03:51:06 +01:00
#endif // REPLAY_OSRI_API_CALLS_SUBSYSTEM
2001-05-23 15:26:42 +02:00
// This function can't take from_desc as const because it may call store_array,
// which in turn calls BLB_create2 that writes in the blob id. Although the
// compiler allows to modify from_desc->dsc_address' contents when from_desc is
// constant, this is misleading so I didn't make the source descriptor constant.
void BLB_move(thread_db* tdbb, dsc* from_desc, dsc* to_desc, jrd_nod* field)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* B L B _ m o v e
*
**************************************
*
* Functional description
* Perform an assignment to a blob field. Unless the blob is null,
* this requires that either a temporary blob be materialized or that
* a permanent blob be copied. Note: it is illegal to have a blob
* field in a message.
*
**************************************/
SET_TDBB(tdbb);
if (field->nod_type != nod_field)
BUGCHECK(199); /* msg 199 expected field node */
if (from_desc->dsc_dtype != dtype_quad
2003-09-12 03:58:51 +02:00
&& from_desc->dsc_dtype != dtype_blob)
{
ERR_post(isc_convert_error, isc_arg_string, "BLOB", 0);
2003-09-12 03:58:51 +02:00
}
2001-05-23 15:26:42 +02:00
jrd_req* request = tdbb->tdbb_request;
bid* source = (bid*) from_desc->dsc_address;
bid* destination = (bid*) to_desc->dsc_address;
const USHORT id = (USHORT) (IPTR) field->nod_arg[e_fld_id];
record_param* rpb = &request->req_rpb[(IPTR)field->nod_arg[e_fld_stream]];
jrd_rel* relation = rpb->rpb_relation;
Record* record = rpb->rpb_record;
2001-05-23 15:26:42 +02:00
2003-10-08 10:42:48 +02:00
/* If nothing changed, do nothing. If it isn't broken,
2001-05-23 15:26:42 +02:00
don't fix it. */
if (source->bid_relation_id == destination->bid_relation_id &&
source->bid_stuff.bid_number == destination->bid_stuff.bid_number)
{
2001-05-23 15:26:42 +02:00
return;
}
2001-05-23 15:26:42 +02:00
/* If either the source value is null or the blob id itself is null (all
zeros, then the blob is null. */
if ((request->req_flags & req_null) || source->isEmpty())
{
2001-05-23 15:26:42 +02:00
SET_NULL(record, id);
destination->bid_relation_id = 0;
destination->bid_stuff.bid_number = 0;
return;
}
CLEAR_NULL(record, id);
jrd_tra* transaction = request->req_transaction;
2001-05-23 15:26:42 +02:00
/* If the target is a view, this must be from a view update trigger.
Just pass the blob id thru */
if (relation->rel_view_rse) {
*destination = *source;
return;
}
#ifdef REPLAY_OSRI_API_CALLS_SUBSYSTEM
/* for REPLAY, map blob id's from the original session */
get_replay_blob(tdbb, source);
#endif
/* If the source is a permanent blob, then the blob must be copied.
2001-05-23 15:26:42 +02:00
Otherwise find the temporary blob referenced. */
ArrayField* array = 0;
blb* blob = 0;
2003-10-08 10:42:48 +02:00
bool materialized_blob, refetch_flag;
2001-05-23 15:26:42 +02:00
do {
2003-10-08 10:42:48 +02:00
materialized_blob = refetch_flag = false;
2001-05-23 15:26:42 +02:00
if (source->bid_relation_id)
blob = copy_blob(tdbb, source, destination);
2001-05-23 15:26:42 +02:00
else if ((to_desc->dsc_dtype == dtype_array) &&
(array = find_array(transaction, source)) &&
(blob = store_array(tdbb, transaction, source)))
{
2003-10-08 10:42:48 +02:00
materialized_blob = true;
}
else {
2001-05-23 15:26:42 +02:00
for (blob = transaction->tra_blobs; blob; blob = blob->blb_next)
if (blob->blb_temp_id == source->bid_stuff.bid_temp_id)
2001-05-23 15:26:42 +02:00
{
2003-10-08 10:42:48 +02:00
materialized_blob = true;
2001-05-23 15:26:42 +02:00
break;
}
}
2001-05-23 15:26:42 +02:00
2003-09-12 03:58:51 +02:00
if (!blob || MemoryPool::blk_type(blob) != type_blb ||
2001-05-23 15:26:42 +02:00
blob->blb_attachment != tdbb->tdbb_attachment ||
!(blob->blb_flags & BLB_closed) ||
(blob->blb_request && blob->blb_request != request))
2003-09-12 03:58:51 +02:00
{
ERR_post(isc_bad_segstr_id, 0);
2003-09-12 03:58:51 +02:00
}
2001-05-23 15:26:42 +02:00
if (materialized_blob && !(blob->blb_flags & BLB_temporary)) {
2003-10-08 10:42:48 +02:00
refetch_flag = true;
2001-05-23 15:26:42 +02:00
source = &blob->blb_blob_id;
}
} while (refetch_flag);
blob->blb_relation = relation;
destination->bid_relation_id = relation->rel_id;
destination->bid_stuff.bid_number = DPM_store_blob(tdbb, blob, record);
if (materialized_blob) {
blob->blb_flags &= ~BLB_temporary;
blob->blb_blob_id = *destination;
blob->blb_request = request;
if (array)
array->arr_request = request;
}
2003-10-08 10:42:48 +02:00
release_blob(blob, !materialized_blob);
2001-05-23 15:26:42 +02:00
}
void BLB_move_from_string(thread_db* tdbb, const dsc* from_desc, dsc* to_desc, jrd_nod* field)
2002-06-29 15:03:13 +02:00
{
/**************************************
*
* B L B _ m o v e _ f r o m _ s t r i n g
*
**************************************
*
* Functional description
* Perform an assignment to a blob field. It's capable of handling
* strings by doing an internal conversion to blob and then calling
* BLB_move with that new blob.
*
**************************************/
SET_TDBB (tdbb);
if (from_desc->dsc_dtype > dtype_varying)
ERR_post(isc_convert_error, isc_arg_string,
2002-06-29 15:03:13 +02:00
DSC_dtype_tostring(from_desc->dsc_dtype), 0);
else
{
2003-02-20 01:10:25 +01:00
USHORT ttype = 0;
blb* blob = 0;
2002-06-29 15:03:13 +02:00
UCHAR *fromstr = 0;
2003-10-20 12:53:52 +02:00
bid temp_bid;
2002-06-29 15:03:13 +02:00
DSC blob_desc;
MOVE_CLEAR(&temp_bid, sizeof(temp_bid));
MOVE_CLEAR(&blob_desc, sizeof(blob_desc));
blob = BLB_create(tdbb, tdbb->tdbb_request->req_transaction, &temp_bid);
blob_desc.dsc_length = MOV_get_string_ptr(from_desc, &ttype, &fromstr, 0, 0);
if (from_desc->dsc_sub_type == BLOB_text)
{
/* I have doubts on the merits of this charset assignment since BLB_create2
calculates charset internally and assigns it to fields inside blb struct.
I really need to call BLB_create2 and provide more parameters.
This macro is useless here as it doesn't cater for blob fields because
desc.dsc_ttype is desc.dsc_sub_type but blobs use dsc_scale for the charset
and dsc_sub_type for blob sub_types, IE text.
INTL_ASSIGN_TTYPE (&blob_desc, ttype);
*/
blob_desc.dsc_scale = ttype;
}
else
{
blob_desc.dsc_scale = ttype_none;
}
blob_desc.dsc_dtype = dtype_blob;
blob_desc.dsc_address = reinterpret_cast<UCHAR*>(&temp_bid);
BLB_put_segment(tdbb, blob, fromstr, blob_desc.dsc_length);
BLB_close(tdbb, blob);
BLB_move(tdbb, &blob_desc, to_desc, field);
release_blob(blob, true);
2002-06-29 15:03:13 +02:00
}
}
blb* BLB_open(thread_db* tdbb, jrd_tra* transaction, const bid* blob_id)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* B L B _ o p e n
*
**************************************
*
* Functional description
* Open an existing blob.
*
**************************************/
SET_TDBB(tdbb);
return BLB_open2(tdbb, transaction, blob_id, 0, 0);
}
blb* BLB_open2(thread_db* tdbb,
jrd_tra* transaction, const bid* blob_id,
USHORT bpb_length, const UCHAR* bpb)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* B L B _ o p e n 2
*
**************************************
*
* Functional description
* Open an existing blob.
* Basically BLB_open() with BPB structure.
*
**************************************/
SET_TDBB(tdbb);
2004-03-07 08:58:55 +01:00
Database* dbb = tdbb->tdbb_database;
2001-05-23 15:26:42 +02:00
/* Handle filter case */
SSHORT from, to;
SSHORT from_charset, to_charset;
2001-05-23 15:26:42 +02:00
gds__parse_bpb2(bpb_length,
bpb,
&from,
&to,
reinterpret_cast<USHORT*>(&from_charset),
reinterpret_cast<USHORT*>(&to_charset));
2001-05-23 15:26:42 +02:00
blb* blob = allocate_blob(tdbb, transaction);
2001-05-23 15:26:42 +02:00
#ifdef REPLAY_OSRI_API_CALLS_SUBSYSTEM
/* for REPLAY, map blob id's from the original session */
get_replay_blob(tdbb, blob_id);
#endif
blob->blb_target_interp = to_charset;
blob->blb_source_interp = from_charset;
BlobFilter* filter = NULL;
bool filter_required = false;
2001-05-23 15:26:42 +02:00
if (to && from != to) {
filter = find_filter(tdbb, from, to);
2003-10-08 10:42:48 +02:00
filter_required = true;
2001-05-23 15:26:42 +02:00
}
else if (to == BLOB_text && (from_charset != to_charset)) {
if (from_charset == CS_dynamic)
from_charset = tdbb->tdbb_attachment->att_charset;
if (to_charset == CS_dynamic)
to_charset = tdbb->tdbb_attachment->att_charset;
if ((to_charset != CS_NONE) && (from_charset != to_charset)) {
filter = FB_NEW(*dbb->dbb_permanent) BlobFilter(*dbb->dbb_permanent);
filter->blf_filter = filter_transliterate_text;
2003-10-08 10:42:48 +02:00
filter_required = true;
2001-05-23 15:26:42 +02:00
}
}
if (filter_required) {
BlobControl* control = 0;
2001-05-23 15:26:42 +02:00
if (BLF_open_blob(tdbb,
transaction,
&control,
blob_id,
2001-05-23 15:26:42 +02:00
bpb_length,
bpb,
reinterpret_cast<FPTR_BFILTER_CALLBACK>(blob_filter),
filter))
{
ERR_punt();
}
2001-05-23 15:26:42 +02:00
blob->blb_filter = control;
blob->blb_max_segment = control->ctl_max_segment;
blob->blb_count = control->ctl_number_segments;
blob->blb_length = control->ctl_total_length;
return blob;
}
if (!blob_id->bid_relation_id)
if (!blob_id->bid_stuff.bid_number)
{
blob->blb_flags |= BLB_eof;
return blob;
}
else {
/* Note: Prior to 1991, we would immediately report bad_segstr_id here,
* but then we decided to allow a newly created blob to be opened,
2001-05-23 15:26:42 +02:00
* leaving the possibility of receiving a garbage blob ID from
* the application.
* The following does some checks to try and product ourselves
* better. 94-Jan-07 Daves.
*/
/* Search the list of transaction blobs for a match */
const blb* new_blob;
for (new_blob = transaction->tra_blobs; new_blob;
new_blob = new_blob->blb_next)
{
if (new_blob->blb_temp_id == blob_id->bid_stuff.bid_temp_id) {
2001-05-23 15:26:42 +02:00
break;
}
}
2001-05-23 15:26:42 +02:00
check_BID_validity(new_blob, tdbb);
2001-05-23 15:26:42 +02:00
blob->blb_lead_page = new_blob->blb_lead_page;
blob->blb_max_sequence = new_blob->blb_max_sequence;
blob->blb_count = new_blob->blb_count;
blob->blb_length = new_blob->blb_length;
blob->blb_max_segment = new_blob->blb_max_segment;
blob->blb_level = new_blob->blb_level;
blob->blb_flags = new_blob->blb_flags & BLB_stream;
const vcl* pages = new_blob->blb_pages;
2001-12-24 03:51:06 +01:00
if (pages) {
vcl* new_pages = vcl::newVector(*transaction->tra_pool, *pages);
2001-12-24 03:51:06 +01:00
blob->blb_pages = new_pages;
2001-05-23 15:26:42 +02:00
}
if (blob->blb_level == 0) {
blob->blb_space_remaining =
new_blob->blb_clump_size - new_blob->blb_space_remaining;
2001-05-23 15:26:42 +02:00
blob->blb_segment =
2004-02-02 12:02:12 +01:00
(UCHAR *) ((blob_page*) new_blob->blb_data)->blp_page;
2001-05-23 15:26:42 +02:00
}
return blob;
}
2001-05-23 15:26:42 +02:00
/* Ordinarily, we would call MET_relation to get the relation id.
However, since the blob id must be consider suspect, this is
2001-05-23 15:26:42 +02:00
not a good idea. On the other hand, if we don't already
know about the relation, the blob id has got to be invalid
anyway. */
vec* vector = dbb->dbb_relations;
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
if (blob_id->bid_relation_id >= vector->count() ||
2001-05-23 15:26:42 +02:00
!(blob->blb_relation =
static_cast<jrd_rel*>( (*vector)[blob_id->bid_relation_id]) ) )
2001-12-24 03:51:06 +01:00
{
ERR_post(isc_bad_segstr_id, 0);
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
DPM_get_blob(tdbb, blob, blob_id->bid_stuff.bid_number, false, (SLONG) 0);
2001-05-23 15:26:42 +02:00
/* If the blob is known to be damaged, ignore it. */
if (blob->blb_flags & BLB_damaged) {
if (!(dbb->dbb_flags & DBB_damaged))
IBERROR(194); /* msg 194 blob not found */
blob->blb_flags |= BLB_eof;
return blob;
}
/* Get first data page in anticipation of reading. */
if (blob->blb_level == 0)
blob->blb_segment = blob->blb_data;
return blob;
}
void BLB_put_segment(thread_db* tdbb, blb* blob, const UCHAR* seg, USHORT segment_length)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* B L B _ p u t _ s e g m e n t
*
**************************************
*
* Functional description
* Add a segment to a blob.
*
**************************************/
SET_TDBB(tdbb);
2004-03-07 08:58:55 +01:00
Database* dbb = tdbb->tdbb_database;
// Anyway, BLOB_PTR is UCHAR, so this is redundant.
const BLOB_PTR* segment = reinterpret_cast<const BLOB_PTR*>(seg);
2001-05-23 15:26:42 +02:00
/* Make sure blob is a temporary blob. If not, complain bitterly. */
if (!(blob->blb_flags & BLB_temporary))
IBERROR(195); /* msg 195 cannot update old blob */
if (blob->blb_filter) {
if (BLF_put_segment(tdbb, &blob->blb_filter, segment_length, segment))
ERR_punt();
return;
}
/* Account for new segment */
blob->blb_count++;
blob->blb_length += segment_length;
if (segment_length > blob->blb_max_segment)
blob->blb_max_segment = segment_length;
/* Compute the effective length of the segment (counts length unless
the blob is a stream blob). */
ULONG length; // length of segment + overhead
bool length_flag;
if (SEGMENTED(blob)) {
2001-05-23 15:26:42 +02:00
length = segment_length + 2;
length_flag = true;
2001-05-23 15:26:42 +02:00
}
else {
length = segment_length;
length_flag = false;
2001-05-23 15:26:42 +02:00
}
/* Case 0: Transition from small blob to medium size blob. This really
just does a form transformation and drops into the next case. */
if (blob->blb_level == 0 && length > (ULONG) blob->blb_space_remaining) {
jrd_tra* transaction = blob->blb_transaction;
blob->blb_pages = vcl::newVector(*transaction->tra_pool, 0);
const USHORT l = dbb->dbb_page_size - BLP_SIZE;
2001-05-23 15:26:42 +02:00
blob->blb_space_remaining += l - blob->blb_clump_size;
blob->blb_clump_size = l;
blob->blb_level = 1;
}
/* Case 1: The segment fits. In what is immaterial. Just move the segment
and get out! */
BLOB_PTR* p = blob->blb_segment;
2001-05-23 15:26:42 +02:00
if (length_flag && blob->blb_space_remaining >= 2) {
const BLOB_PTR* q = (UCHAR*) &segment_length;
2001-05-23 15:26:42 +02:00
*p++ = *q++;
*p++ = *q++;
blob->blb_space_remaining -= 2;
length_flag = false;
2001-05-23 15:26:42 +02:00
}
if (!length_flag && segment_length <= blob->blb_space_remaining) {
blob->blb_space_remaining -= segment_length;
if (((U_IPTR) segment & (ALIGNMENT - 1))
|| ((U_IPTR) p & (ALIGNMENT - 1)))
{
2001-05-23 15:26:42 +02:00
MOVE_FAST(segment, p, segment_length);
}
2001-05-23 15:26:42 +02:00
else
MOVE_FASTER(segment, p, segment_length);
blob->blb_segment = p + segment_length;
return;
}
/* The segment cannot be contained in the current clump. What does
bit is copied to the current page image. Then allocate a page to
hold the current page, copy the page image to the buffer, and release
the page. Since a segment can be much larger than a page, this whole
mess is done in a loop. */
while (length_flag || segment_length) {
/* Move what fits. At this point, the length is known not to fit. */
const USHORT l = MIN(segment_length, blob->blb_space_remaining);
2001-05-23 15:26:42 +02:00
if (!length_flag && l) {
segment_length -= l;
blob->blb_space_remaining -= l;
if (((U_IPTR) segment & (ALIGNMENT - 1))
|| ((U_IPTR) p & (ALIGNMENT - 1)))
{
2001-05-23 15:26:42 +02:00
MOVE_FAST(segment, p, l);
}
2001-05-23 15:26:42 +02:00
else
MOVE_FASTER(segment, p, l);
p += l;
segment += l;
if (segment_length == 0) {
blob->blb_segment = p;
return;
}
}
/* Data page is full. Add the page to the blob data structure. */
insert_page(tdbb, blob);
blob->blb_sequence++;
/* Get ready to start filling the next page. */
2004-02-02 12:02:12 +01:00
blob_page* page = (blob_page*) blob->blb_data;
p = blob->blb_segment = (UCHAR *) page->blp_page;
2001-05-23 15:26:42 +02:00
blob->blb_space_remaining = blob->blb_clump_size;
/* If there's still a length waiting to be moved, move it already! */
if (length_flag) {
const BLOB_PTR* q = (UCHAR*) &segment_length;
2001-05-23 15:26:42 +02:00
*p++ = *q++;
*p++ = *q++;
blob->blb_space_remaining -= 2;
length_flag = false;
2001-05-23 15:26:42 +02:00
blob->blb_segment = p;
}
}
}
void BLB_put_slice( thread_db* tdbb,
jrd_tra* transaction,
bid* blob_id,
const UCHAR* sdl,
2002-01-05 17:28:59 +01:00
USHORT param_length,
const SLONG* param,
2002-01-05 17:28:59 +01:00
SLONG slice_length,
2003-10-20 12:53:52 +02:00
UCHAR* slice_addr)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* B L B _ p u t _ s l i c e
*
**************************************
*
* Functional description
* Put a slice of an array.
*
**************************************/
SET_TDBB(tdbb);
tdbb->tdbb_default = transaction->tra_pool;
/* Do initial parse of slice description to get relation and field identification */
sdl_info info;
2001-05-23 15:26:42 +02:00
if (SDL_info(tdbb->tdbb_status_vector, sdl, &info, 0))
ERR_punt();
jrd_rel* relation;
2002-01-05 17:28:59 +01:00
if (info.sdl_info_relation[0]) {
2001-05-23 15:26:42 +02:00
relation = MET_lookup_relation(tdbb, info.sdl_info_relation);
}
else {
2001-05-23 15:26:42 +02:00
relation = MET_relation(tdbb, info.sdl_info_rid);
2002-01-05 17:28:59 +01:00
}
2001-05-23 15:26:42 +02:00
2002-01-05 17:28:59 +01:00
if (!relation) {
2001-05-23 15:26:42 +02:00
IBERROR(196); /* msg 196 relation for array not known */
2002-01-05 17:28:59 +01:00
}
2001-05-23 15:26:42 +02:00
SSHORT n;
2002-01-05 17:28:59 +01:00
if (info.sdl_info_field[0]) {
2002-06-29 15:03:13 +02:00
n = MET_lookup_field(tdbb, relation, info.sdl_info_field, 0);
}
else {
2001-05-23 15:26:42 +02:00
n = info.sdl_info_fid;
2002-01-05 17:28:59 +01:00
}
2001-05-23 15:26:42 +02:00
/* Make sure relation is scanned */
MET_scan_relation(tdbb, relation);
jrd_fld* field;
2002-01-05 17:28:59 +01:00
if (n < 0 || !(field = MET_get_field(relation, n))) {
2001-05-23 15:26:42 +02:00
IBERROR(197); /* msg 197 field for array not known */
2002-01-05 17:28:59 +01:00
}
2001-05-23 15:26:42 +02:00
ArrayField* array_desc = field->fld_array;
2002-01-05 17:28:59 +01:00
if (!array_desc)
{
ERR_post(isc_invalid_dimension, isc_arg_number, (SLONG) 0,
isc_arg_number, (SLONG) 1, 0);
2002-01-05 17:28:59 +01:00
}
2001-05-23 15:26:42 +02:00
/* Find and/or allocate array block. There are three distinct cases:
1. Array is totally new.
2. Array is still in "temporary" state.
3. Array exists and is being updated.
*/
ArrayField* array = 0;
array_slice arg;
2002-01-05 17:28:59 +01:00
if (blob_id->bid_relation_id)
{
2001-05-23 15:26:42 +02:00
for (array = transaction->tra_arrays; array; array = array->arr_next)
2002-01-05 17:28:59 +01:00
{
2001-05-23 15:26:42 +02:00
if (array->arr_blob &&
array->arr_blob->blb_blob_id.bid_relation_id ==
blob_id->bid_relation_id &&
array->arr_blob->blb_blob_id.bid_stuff.bid_number ==
2002-01-05 17:28:59 +01:00
blob_id->bid_stuff.bid_number)
{
break;
}
}
2001-05-23 15:26:42 +02:00
if (array)
2002-01-05 17:28:59 +01:00
{
2001-05-23 15:26:42 +02:00
arg.slice_high_water =
2002-01-05 17:28:59 +01:00
(BLOB_PTR*) array->arr_data + array->arr_effective_length;
}
else
{
// CVC: maybe char temp[ADS_LEN(16)]; may work.
SLONG temp[IAD_LEN(16) / 4];
internal_array_desc* p_ads = reinterpret_cast<internal_array_desc*>(temp);
blb* blob = BLB_get_array(tdbb, transaction, blob_id, p_ads);
array = alloc_array(transaction, p_ads);
2001-05-23 15:26:42 +02:00
array->arr_effective_length =
blob->blb_length - array->arr_desc.iad_length;
2001-05-23 15:26:42 +02:00
BLB_get_data(tdbb, blob, array->arr_data,
array->arr_desc.iad_total_length);
2001-05-23 15:26:42 +02:00
arg.slice_high_water =
2002-01-05 17:28:59 +01:00
(BLOB_PTR*) array->arr_data + array->arr_effective_length;
2001-05-23 15:26:42 +02:00
array->arr_blob = allocate_blob(tdbb, transaction);
(array->arr_blob)->blb_blob_id = *blob_id;
}
}
else if (blob_id->bid_stuff.bid_temp_id)
2001-05-23 15:26:42 +02:00
{
2002-01-05 17:28:59 +01:00
array = find_array(transaction, blob_id);
if (!array) {
2001-05-23 15:26:42 +02:00
ERR_post(isc_invalid_array_id, 0);
2002-01-05 17:28:59 +01:00
}
2001-05-23 15:26:42 +02:00
arg.slice_high_water =
(BLOB_PTR *) array->arr_data + array->arr_effective_length;
}
else {
array = alloc_array(transaction, &array_desc->arr_desc);
arg.slice_high_water = (BLOB_PTR *) array->arr_data;
}
/* Walk array */
arg.slice_desc = info.sdl_info_element;
2003-10-20 12:53:52 +02:00
arg.slice_desc.dsc_address = slice_addr;
arg.slice_end = (BLOB_PTR*) slice_addr + slice_length;
2001-05-23 15:26:42 +02:00
arg.slice_count = 0;
arg.slice_element_length = info.sdl_info_element.dsc_length;
arg.slice_direction = TRUE; /* storing INTO array */
arg.slice_base = (BLOB_PTR*) array->arr_data;
SLONG variables[64];
2001-05-23 15:26:42 +02:00
MOVE_FAST(param, variables, MIN(sizeof(variables), param_length));
if (SDL_walk(tdbb->tdbb_status_vector,
sdl,
true,
2001-05-23 15:26:42 +02:00
array->arr_data,
&array_desc->arr_desc,
variables,
slice_callback,
2002-01-05 17:28:59 +01:00
&arg))
{
ERR_punt();
}
2001-05-23 15:26:42 +02:00
const SLONG length = arg.slice_high_water - (BLOB_PTR*)array->arr_data;
2001-05-23 15:26:42 +02:00
2002-01-05 17:28:59 +01:00
if (length > array->arr_effective_length) {
2001-05-23 15:26:42 +02:00
array->arr_effective_length = length;
2002-01-05 17:28:59 +01:00
}
2001-05-23 15:26:42 +02:00
blob_id->bid_stuff.bid_temp_id = array->arr_temp_id;
2001-05-23 15:26:42 +02:00
blob_id->bid_relation_id = 0;
}
void BLB_release_array(ArrayField* array)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* B L B _ r e l e a s e _ a r r a y
*
**************************************
*
* Functional description
* Release an array block and friends and relations.
*
**************************************/
2002-01-05 17:28:59 +01:00
if (array->arr_data) {
MemoryPool::globalFree(array->arr_data); // But know that it comes from permanent pool
2002-01-05 17:28:59 +01:00
}
2001-05-23 15:26:42 +02:00
jrd_tra* transaction = array->arr_transaction;
2002-01-05 17:28:59 +01:00
if (transaction)
{
for (ArrayField** ptr = &transaction->tra_arrays; *ptr;
ptr = &(*ptr)->arr_next)
{
2001-05-23 15:26:42 +02:00
if (*ptr == array) {
*ptr = array->arr_next;
break;
}
2002-01-05 17:28:59 +01:00
}
}
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
delete array;
2001-05-23 15:26:42 +02:00
}
void BLB_scalar(thread_db* tdbb,
jrd_tra* transaction,
const bid* blob_id,
USHORT count,
SLONG* subscripts,
impure_value* value)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* B L B _ s c a l a r
*
**************************************
*
* Functional description
*
**************************************/
2002-01-05 17:28:59 +01:00
SLONG stuff[IAD_LEN(16) / 4];
2001-05-23 15:26:42 +02:00
SET_TDBB(tdbb);
internal_array_desc* array_desc = (internal_array_desc*) stuff;
blb* blob = BLB_get_array(tdbb, transaction, blob_id, array_desc);
2001-05-23 15:26:42 +02:00
/* Get someplace to put data. If the local buffer isn't large enough,
allocate one that is. */
double temp[64];
UCHAR* const temp_ptr = reinterpret_cast<UCHAR*>(temp);
2001-05-23 15:26:42 +02:00
str* temp_str = 0;
dsc desc = array_desc->iad_rpt[0].iad_desc;
2001-05-23 15:26:42 +02:00
if (desc.dsc_length <= sizeof(temp)) {
desc.dsc_address = temp_ptr;
}
else {
2001-05-23 15:26:42 +02:00
temp_str =
FB_NEW_RPT(*tdbb->tdbb_default, desc.dsc_length + DOUBLE_ALIGN - 1) str;
2001-05-23 15:26:42 +02:00
desc.dsc_address =
(UCHAR *) FB_ALIGN((U_IPTR) temp_str->str_data, DOUBLE_ALIGN);
}
const SLONG number =
2001-05-23 15:26:42 +02:00
SDL_compute_subscript(tdbb->tdbb_status_vector, array_desc, count,
subscripts);
if (number < 0) {
BLB_close(tdbb, blob);
if (desc.dsc_address != temp_ptr) {
2001-12-24 03:51:06 +01:00
delete temp_str;
2002-01-05 17:28:59 +01:00
}
2001-05-23 15:26:42 +02:00
ERR_punt();
}
const SLONG offset = number * array_desc->iad_element_length;
BLB_lseek(blob, 0, offset + (SLONG) array_desc->iad_length);
BLB_get_segment(tdbb, blob, temp_ptr,
2001-05-23 15:26:42 +02:00
desc.dsc_length);
/* If we have run out of data, then clear the data buffer. */
2002-01-05 17:28:59 +01:00
if (blob->blb_flags & BLB_eof) {
2001-05-23 15:26:42 +02:00
memset(desc.dsc_address, 0, (int) desc.dsc_length);
2002-01-05 17:28:59 +01:00
}
2001-05-23 15:26:42 +02:00
EVL_make_value(tdbb, &desc, value);
BLB_close(tdbb, blob);
if (desc.dsc_address != temp_ptr) {
2001-12-24 03:51:06 +01:00
delete temp_str;
2002-01-05 17:28:59 +01:00
}
2001-05-23 15:26:42 +02:00
}
static ArrayField* alloc_array(jrd_tra* transaction, internal_array_desc* proto_desc)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* a l l o c _ a r r a y
*
**************************************
*
* Functional description
* Allocate an array block based on a prototype array descriptor.
*
**************************************/
2001-12-24 03:51:06 +01:00
2004-03-07 08:58:55 +01:00
Database* dbb = GET_DBB;
2001-05-23 15:26:42 +02:00
2002-01-05 17:28:59 +01:00
// Compute size and allocate block
2001-05-23 15:26:42 +02:00
const USHORT n = MAX(proto_desc->iad_struct_count, proto_desc->iad_dimensions);
ArrayField* array = FB_NEW_RPT(*transaction->tra_pool, n) ArrayField();
2001-05-23 15:26:42 +02:00
2002-01-05 17:28:59 +01:00
// Copy prototype descriptor
2001-05-23 15:26:42 +02:00
MOVE_FAST(proto_desc, &array->arr_desc, proto_desc->iad_length);
2001-05-23 15:26:42 +02:00
2002-01-05 17:28:59 +01:00
// Link into transaction block
2001-05-23 15:26:42 +02:00
array->arr_next = transaction->tra_arrays;
transaction->tra_arrays = array;
array->arr_transaction = transaction;
2002-01-05 17:28:59 +01:00
// Allocate large block to hold array
2001-05-23 15:26:42 +02:00
array->arr_data =
(UCHAR*)dbb->dbb_permanent->allocate(array->arr_desc.iad_total_length, 0
#ifdef DEBUG_GDS_ALLOC
,__FILE__, __LINE__
#endif
);
2004-01-22 07:51:37 +01:00
array->arr_temp_id = ++transaction->tra_next_blob_id;
2001-05-23 15:26:42 +02:00
return array;
}
static blb* allocate_blob(thread_db* tdbb, jrd_tra* transaction)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* a l l o c a t e _ b l o b
*
**************************************
*
* Functional description
* Create a shiney, new, empty blob.
*
**************************************/
SET_TDBB(tdbb);
2004-03-07 08:58:55 +01:00
Database* dbb = tdbb->tdbb_database;
2001-05-23 15:26:42 +02:00
/* Create a blob large enough to hold a single data page */
blb* blob = FB_NEW_RPT(*transaction->tra_pool, dbb->dbb_page_size) blb();
2001-05-23 15:26:42 +02:00
blob->blb_attachment = tdbb->tdbb_attachment;
blob->blb_next = transaction->tra_blobs;
transaction->tra_blobs = blob;
blob->blb_transaction = transaction;
/* Compute some parameters governing various maximum sizes based on
database page size. */
2002-01-05 17:28:59 +01:00
blob->blb_clump_size = dbb->dbb_page_size -
sizeof(Ods::data_page) -
sizeof(Ods::data_page::dpg_repeat) -
sizeof(Ods::blh);
2001-05-23 15:26:42 +02:00
blob->blb_max_pages = blob->blb_clump_size >> SHIFTLONG;
blob->blb_pointers = (dbb->dbb_page_size - BLP_SIZE) >> SHIFTLONG;
2004-01-22 07:51:37 +01:00
blob->blb_temp_id = ++transaction->tra_next_blob_id;
2001-05-23 15:26:42 +02:00
return blob;
}
2003-04-10 08:49:16 +02:00
static ISC_STATUS blob_filter( USHORT action,
BlobControl* control,
2002-01-05 17:28:59 +01:00
SSHORT mode,
SLONG offset)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* b l o b _ f i l t e r
*
**************************************
*
* Functional description
* Filter of last resort for filtered blob access handled by Y-valve.
*
**************************************/
/* Note: Cannot remove this GET_THREAD_DATA without API change to
blob filter routines */
thread_db* tdbb = GET_THREAD_DATA;
2001-05-23 15:26:42 +02:00
jrd_tra* transaction = (jrd_tra*) control->ctl_internal[1];
bid* blob_id = reinterpret_cast<bid*>(control->ctl_internal[2]);
2001-05-23 15:26:42 +02:00
#ifdef DEV_BUILD
2002-01-05 17:28:59 +01:00
if (transaction) {
2001-05-23 15:26:42 +02:00
BLKCHK(transaction, type_tra);
2002-01-05 17:28:59 +01:00
}
2001-05-23 15:26:42 +02:00
#endif
blb* blob = 0;
2001-05-23 15:26:42 +02:00
switch (action) {
case ACTION_open:
blob = BLB_open2(tdbb, transaction, blob_id, 0, 0);
control->source_handle = blob;
2001-05-23 15:26:42 +02:00
control->ctl_total_length = blob->blb_length;
control->ctl_max_segment = blob->blb_max_segment;
control->ctl_number_segments = blob->blb_count;
return FB_SUCCESS;
2001-05-23 15:26:42 +02:00
case ACTION_get_segment:
blob = control->source_handle;
2001-05-23 15:26:42 +02:00
control->ctl_segment_length =
BLB_get_segment(tdbb, blob, control->ctl_buffer,
control->ctl_buffer_length);
2002-01-05 17:28:59 +01:00
if (blob->blb_flags & BLB_eof) {
return isc_segstr_eof;
2002-01-05 17:28:59 +01:00
}
if (blob->blb_fragment_size) {
return isc_segment;
2002-01-05 17:28:59 +01:00
}
return FB_SUCCESS;
2001-05-23 15:26:42 +02:00
case ACTION_create:
control->source_handle =
BLB_create2(tdbb, transaction, blob_id, 0, NULL);
return FB_SUCCESS;
2001-05-23 15:26:42 +02:00
case ACTION_put_segment:
blob = control->source_handle;
2001-05-23 15:26:42 +02:00
BLB_put_segment(tdbb, blob, control->ctl_buffer,
control->ctl_buffer_length);
return FB_SUCCESS;
2001-05-23 15:26:42 +02:00
case ACTION_close:
BLB_close(tdbb, control->source_handle);
return FB_SUCCESS;
2001-05-23 15:26:42 +02:00
case ACTION_alloc:
2003-12-22 11:00:59 +01:00
// pointer to ISC_STATUS!!!
return (ISC_STATUS) FB_NEW(*transaction->tra_pool) BlobControl(*transaction->tra_pool);
2001-05-23 15:26:42 +02:00
case ACTION_free:
2001-12-24 03:51:06 +01:00
delete control;
return FB_SUCCESS;
2001-05-23 15:26:42 +02:00
case ACTION_seek:
return BLB_lseek(control->source_handle, mode, offset);
2001-05-23 15:26:42 +02:00
default:
ERR_post(isc_uns_ext, 0);
return FB_SUCCESS;
2001-05-23 15:26:42 +02:00
}
}
static void check_BID_validity(const blb* blob, thread_db* tdbb)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* c h e c k _ B I D _ v a l i d i t y
*
**************************************
*
* Functional description
* There are times when an application passes the engine
* a bid, which we then assume points to a valid blb structure.
2001-05-23 15:26:42 +02:00
* Specifically, this can occur when an application is trying
* to open a newly created blob (that doesn't have a relation
* ID assigned).
*
* However, it is quite possible that garbage bid is passed in
2001-05-23 15:26:42 +02:00
* from an application; resulting in core dumps within the engine
* due to trying to make use of the information.
*
* This function takes a bid's pointer to a blb, and performs
2001-05-23 15:26:42 +02:00
* some validity checks on it. It can't catch all possible
* garbage inputs from an application, but should catch
* many of them.
*
* Note that we can't BUGCHECK or BLKCHK here, this is an
* application level error.
*
* 94-Jan-07 Daves
*
**************************************/
if (!blob ||
// Nickolay Samofatov. These checks are now unnecessary since we
// look up blob using temp_id inside the transaction blobs only.
// They were unreliable, anyway.
// MemoryPool::blk_type(blob) != type_blb ||
// blob->blb_attachment != tdbb->tdbb_attachment ||
2001-05-23 15:26:42 +02:00
blob->blb_level > 2 || !(blob->blb_flags & BLB_temporary))
2002-01-05 17:28:59 +01:00
{
ERR_post(isc_bad_segstr_id, 0);
2002-01-05 17:28:59 +01:00
}
2001-05-23 15:26:42 +02:00
}
static blb* copy_blob(thread_db* tdbb, const bid* source, bid* destination)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* c o p y _ b l o b
*
**************************************
*
* Functional description
* Make a copy of an existing blob.
*
**************************************/
SET_TDBB(tdbb);
jrd_req* request = tdbb->tdbb_request;
blb* input = BLB_open(tdbb, request->req_transaction, source);
blb* output = BLB_create(tdbb, request->req_transaction, destination);
2001-05-23 15:26:42 +02:00
output->blb_sub_type = input->blb_sub_type;
2002-01-05 17:28:59 +01:00
if (input->blb_flags & BLB_stream) {
2001-05-23 15:26:42 +02:00
output->blb_flags |= BLB_stream;
2002-01-05 17:28:59 +01:00
}
2001-05-23 15:26:42 +02:00
UCHAR buffer[2000];
UCHAR* buff;
str* string;
2001-05-23 15:26:42 +02:00
if (input->blb_max_segment > sizeof(buffer))
{
string = FB_NEW_RPT(*tdbb->tdbb_default, input->blb_max_segment) str();
2001-05-23 15:26:42 +02:00
buff = (UCHAR *) string->str_data;
}
else {
string = NULL;
buff = buffer;
}
2003-10-08 10:42:48 +02:00
while (true) {
const USHORT length =
BLB_get_segment(tdbb, input, buff, input->blb_max_segment);
2002-01-05 17:28:59 +01:00
if (input->blb_flags & BLB_eof) {
2001-05-23 15:26:42 +02:00
break;
2002-01-05 17:28:59 +01:00
}
2001-05-23 15:26:42 +02:00
BLB_put_segment(tdbb, output, buff, length);
}
2002-01-05 17:28:59 +01:00
delete string;
2001-05-23 15:26:42 +02:00
BLB_close(tdbb, input);
BLB_close(tdbb, output);
return output;
}
static void delete_blob(thread_db* tdbb, blb* blob, ULONG prior_page)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* d e l e t e _ b l o b
*
**************************************
*
* Functional description
* Delete all disk storage associated with blob. This can be used
* to either abort a temporary blob or get rid of an unwanted and
* unloved permanent blob. The routine deletes only blob page --
* somebody else will have to worry about the blob root.
*
**************************************/
SET_TDBB(tdbb);
2004-03-07 08:58:55 +01:00
Database* dbb = tdbb->tdbb_database;
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
if (dbb->dbb_flags & DBB_read_only)
ERR_post(isc_read_only_database, 0);
/* Level 0 blobs don't need cleanup */
if (blob->blb_level == 0)
return;
/* Level 1 blobs just need the root page level released */
vcl* vector = blob->blb_pages;
vcl::iterator ptr = vector->begin();
const vcl::iterator end = vector->end();
2001-05-23 15:26:42 +02:00
if (blob->blb_level == 1) {
for (; ptr < end; ptr++) {
if (*ptr) {
2001-05-23 15:26:42 +02:00
PAG_release_page(*ptr, prior_page);
}
}
2001-05-23 15:26:42 +02:00
return;
}
/* Level 2 blobs need a little more work to keep the page precedence
in order. The basic problem is that the pointer page has to be
released before the data pages that it points to. Sigh. */
WIN window(-1);
2001-05-23 15:26:42 +02:00
window.win_flags = WIN_large_scan;
window.win_scans = 1;
for (; ptr < end; ptr++)
2001-12-24 03:51:06 +01:00
if ( (window.win_page = *ptr) ) {
2004-02-02 12:02:12 +01:00
blob_page* page = (blob_page*) CCH_FETCH(tdbb, &window, LCK_read, pag_blob);
2001-05-23 15:26:42 +02:00
MOVE_FASTER(page, blob->blb_data, dbb->dbb_page_size);
CCH_RELEASE_TAIL(tdbb, &window);
PAG_release_page(*ptr, prior_page);
2004-02-02 12:02:12 +01:00
page = (blob_page*) blob->blb_data;
SLONG* ptr2 = page->blp_page;
for (const SLONG* const end2 = ptr2 + blob->blb_pointers;
2001-05-23 15:26:42 +02:00
ptr2 < end2; ptr2++)
{
if (*ptr2) {
2001-05-23 15:26:42 +02:00
PAG_release_page(*ptr2, *ptr);
}
}
2001-05-23 15:26:42 +02:00
}
}
static void delete_blob_id(
thread_db* tdbb,
const bid* blob_id, SLONG prior_page, jrd_rel* relation)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* d e l e t e _ b l o b _ i d
*
**************************************
*
* Functional description
* Delete an existing blob for purposed of garbage collection.
*
**************************************/
SET_TDBB(tdbb);
2004-03-07 08:58:55 +01:00
Database* dbb = tdbb->tdbb_database;
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
/* If the blob is null, don't bother to delete it. Reasonable? */
2001-05-23 15:26:42 +02:00
if (blob_id->isEmpty())
2001-05-23 15:26:42 +02:00
return;
if (blob_id->bid_relation_id != relation->rel_id)
CORRUPT(200); /* msg 200 invalid blob id */
/* Fetch blob */
blb* blob = allocate_blob(tdbb, dbb->dbb_sys_trans);
2001-05-23 15:26:42 +02:00
blob->blb_relation = relation;
prior_page =
DPM_get_blob(tdbb, blob, blob_id->bid_stuff.bid_number, true,
2001-05-23 15:26:42 +02:00
prior_page);
if (!(blob->blb_flags & BLB_damaged))
delete_blob(tdbb, blob, prior_page);
2003-10-08 10:42:48 +02:00
release_blob(blob, true);
2001-05-23 15:26:42 +02:00
}
static ArrayField* find_array(jrd_tra* transaction, const bid* blob_id)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* f i n d _ a r r a y
*
**************************************
*
* Functional description
* Find array from temporary blob id.
*
**************************************/
ArrayField* array = transaction->tra_arrays;
2001-05-23 15:26:42 +02:00
for (; array; array = array->arr_next) {
if (array->arr_temp_id == blob_id->bid_stuff.bid_temp_id) {
2001-05-23 15:26:42 +02:00
break;
}
}
2001-05-23 15:26:42 +02:00
return array;
}
static BlobFilter* find_filter(thread_db* tdbb, SSHORT from, SSHORT to)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* f i n d _ f i l t e r
*
**************************************
*
* Functional description
* Find blob filter.
*
**************************************/
SET_TDBB(tdbb);
2004-03-07 08:58:55 +01:00
Database* dbb = tdbb->tdbb_database;
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
BlobFilter* cache = dbb->dbb_blob_filters;
for (; cache; cache = cache->blf_next) {
2001-05-23 15:26:42 +02:00
if (cache->blf_from == from && cache->blf_to == to)
return cache;
}
2001-05-23 15:26:42 +02:00
cache = BLF_lookup_internal_filter(tdbb, from, to);
if (!cache) {
2001-05-23 15:26:42 +02:00
cache = MET_lookup_filter(tdbb, from, to);
}
2001-05-23 15:26:42 +02:00
if (cache) {
cache->blf_next = dbb->dbb_blob_filters;
dbb->dbb_blob_filters = cache;
}
return cache;
}
static blob_page* get_next_page(thread_db* tdbb, blb* blob, WIN * window)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g e t _ n e x t _ p a g e
*
**************************************
*
* Functional description
* Read a blob page and copy it into the blob data area. Return
* the next page. if there's no next page, return NULL.
2001-05-23 15:26:42 +02:00
*
**************************************/
if (blob->blb_level == 0 || blob->blb_sequence > blob->blb_max_sequence) {
blob->blb_space_remaining = 0;
return NULL;
}
SET_TDBB(tdbb);
2004-03-07 08:58:55 +01:00
Database* dbb = tdbb->tdbb_database;
vcl* vector = blob->blb_pages;
#ifdef SUPERSERVER_V2
SLONG pages[PREFETCH_MAX_PAGES];
#endif
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
blob_page* page = 0;
/* Level 1 blobs are much easier -- page number is in vector. */
2001-05-23 15:26:42 +02:00
if (blob->blb_level == 1) {
#ifdef SUPERSERVER_V2
/* Perform prefetch of blob level 1 data pages. */
if (!(blob->blb_sequence % dbb->dbb_prefetch_sequence)) {
USHORT sequence = blob->blb_sequence;
USHORT i = 0;
while (i < dbb->dbb_prefetch_pages &&
sequence <= blob->blb_max_sequence)
{
pages[i++] =
2002-04-29 17:05:11 +02:00
(*vector)[sequence++];
}
2001-05-23 15:26:42 +02:00
CCH_PREFETCH(tdbb, pages, i);
}
#endif
2001-12-24 03:51:06 +01:00
window->win_page = (*vector)[blob->blb_sequence];
2004-02-02 12:02:12 +01:00
page = (blob_page*) CCH_FETCH(tdbb, window, LCK_read, pag_blob);
2001-05-23 15:26:42 +02:00
}
else {
window->win_page =
2001-12-24 03:51:06 +01:00
(*vector)[blob->blb_sequence / blob->blb_pointers];
2004-02-02 12:02:12 +01:00
page = (blob_page*) CCH_FETCH(tdbb, window, LCK_read, pag_blob);
2001-05-23 15:26:42 +02:00
#ifdef SUPERSERVER_V2
/* Perform prefetch of blob level 2 data pages. */
USHORT sequence = blob->blb_sequence % blob->blb_pointers;
2001-05-23 15:26:42 +02:00
if (!(sequence % dbb->dbb_prefetch_sequence))
{
ULONG abs_sequence = blob->blb_sequence;
USHORT i = 0;
while (i < dbb->dbb_prefetch_pages &&
2001-05-23 15:26:42 +02:00
sequence < blob->blb_pointers &&
abs_sequence <= blob->blb_max_sequence)
2001-05-23 15:26:42 +02:00
{
pages[i++] = page->blp_page[sequence++];
abs_sequence++;
}
CCH_PREFETCH(tdbb, pages, i);
}
#endif
2004-02-02 12:02:12 +01:00
page = (blob_page*)CCH_HANDOFF(tdbb,
2001-05-23 15:26:42 +02:00
window,
page->blp_page[blob->blb_sequence %
blob->blb_pointers],
LCK_read,
pag_blob);
}
if (page->blp_sequence != (SLONG) blob->blb_sequence)
2001-05-23 15:26:42 +02:00
CORRUPT(201); /* msg 201 cannot find blob page */
blob->blb_sequence++;
return page;
}
#ifdef REPLAY_OSRI_API_CALLS_SUBSYSTEM
static void get_replay_blob(thread_db* tdbb, bid* blob_id)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g e t _ r e p l a y _ b l o b
*
**************************************
*
* Functional description
* Replace the blob id passed with the
* blob id used in the original session.
*
**************************************/
SET_TDBB(tdbb);
2004-03-07 08:58:55 +01:00
Database* dbb = tdbb->tdbb_database;
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
/* we're only interested in newly created blobs */
if (blob_id->bid_relation_id != 0)
return;
/* search the linked list for the old blob id */
for (map* map_ptr = dbb->dbb_blob_map; map_ptr; map_ptr = map_ptr->map_next)
{
if (blob_id->bid_stuff.bid_temp_id == map_ptr->map_old_blob)
2001-05-23 15:26:42 +02:00
{
blob_id->bid_stuff.bid_temp_id = map_ptr->map_new_blob;
2001-05-23 15:26:42 +02:00
break;
}
}
}
#endif
static void insert_page(thread_db* tdbb, blb* blob)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* i n s e r t _ p a g e
*
**************************************
*
* Functional description
* A data page has been formatted. Allocate a physical page,
* move the data page to the buffer, and insert the page number
* of the new page into the blob data structure.
*
**************************************/
SET_TDBB(tdbb);
2004-03-07 08:58:55 +01:00
Database* dbb = tdbb->tdbb_database;
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
const USHORT length = dbb->dbb_page_size - blob->blb_space_remaining;
vcl* vector = blob->blb_pages;
2001-05-23 15:26:42 +02:00
blob->blb_max_sequence = blob->blb_sequence;
/* Allocate a page for the now full blob data page. Move the page
image to the buffer, and release the page. */
WIN window(-1);
2004-02-02 12:02:12 +01:00
blob_page* page = (blob_page*) DPM_allocate(tdbb, &window);
const ULONG page_number = window.win_page;
2001-05-23 15:26:42 +02:00
if (blob->blb_sequence == 0)
blob->blb_lead_page = page_number;
MOVE_FASTER(blob->blb_data, page, length);
page->blp_sequence = blob->blb_sequence;
page->blp_lead_page = blob->blb_lead_page;
page->blp_length = length - BLP_SIZE;
CCH_RELEASE(tdbb, &window);
/* If the blob is at level 1, there are two cases. First, and easiest,
is that there is still room in the page vector to hold the pointer.
The second case is that the vector is full, and the blob must be
transformed into a level 2 blob. */
if (blob->blb_level == 1) {
/* See if there is room in the page vector. If so, just update
the vector. */
if (blob->blb_sequence < blob->blb_max_pages) {
if (blob->blb_sequence >= vector->count()) {
vector->resize(blob->blb_sequence + 1);
}
2001-12-24 03:51:06 +01:00
(*vector)[blob->blb_sequence] = page_number;
2001-05-23 15:26:42 +02:00
return;
}
/* The vector just overflowed. Sigh. Transform blob to level 2. */
blob->blb_level = 2;
2004-02-02 12:02:12 +01:00
page = (blob_page*) DPM_allocate(tdbb, &window);
page->pag_flags = blp_pointers;
page->pag_type = pag_blob;
2001-05-23 15:26:42 +02:00
page->blp_lead_page = blob->blb_lead_page;
2001-12-24 03:51:06 +01:00
page->blp_length = vector->count() << SHIFTLONG;
MOVE_FASTER(vector->memPtr(), page->blp_page, page->blp_length);
vector->resize(1);
2001-12-24 03:51:06 +01:00
(*vector)[0] = window.win_page;
2001-05-23 15:26:42 +02:00
CCH_RELEASE(tdbb, &window);
}
/* The blob must be level 2. Find the appropriate pointer page (creating
it if need be, and stick the pointer in the appropriate slot. */
USHORT l = blob->blb_sequence / blob->blb_pointers;
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
if (l < vector->count()) {
window.win_page = (*vector)[l];
2001-05-23 15:26:42 +02:00
window.win_flags = 0;
2004-02-02 12:02:12 +01:00
page = (blob_page*) CCH_FETCH(tdbb, &window, LCK_write, pag_blob);
2001-05-23 15:26:42 +02:00
}
else {
2004-02-02 12:02:12 +01:00
page = (blob_page*) DPM_allocate(tdbb, &window);
page->pag_flags = blp_pointers;
page->pag_type = pag_blob;
2001-05-23 15:26:42 +02:00
page->blp_lead_page = blob->blb_lead_page;
vector->resize(l + 1);
2001-12-24 03:51:06 +01:00
(*vector)[l] = window.win_page;
2001-05-23 15:26:42 +02:00
}
CCH_precedence(tdbb, &window, page_number);
CCH_MARK(tdbb, &window);
l = blob->blb_sequence % blob->blb_pointers;
page->blp_page[l] = page_number;
page->blp_length = (l + 1) << SHIFTLONG;
CCH_RELEASE(tdbb, &window);
}
static void release_blob(blb* blob, const bool purge_flag)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* r e l e a s e _ b l o b
*
**************************************
*
* Functional description
* Release a blob and associated blocks. Among other things,
* disconnect it from the transaction. However, if purge_flag
2003-10-08 10:42:48 +02:00
* is false, then only release the associated blocks.
2001-05-23 15:26:42 +02:00
*
**************************************/
jrd_tra* transaction = blob->blb_transaction;
2001-05-23 15:26:42 +02:00
/* Disconnect blob from transaction block. */
if (purge_flag) {
for (blb** ptr = &transaction->tra_blobs; *ptr; ptr = &(*ptr)->blb_next) {
2001-05-23 15:26:42 +02:00
if (*ptr == blob) {
*ptr = blob->blb_next;
break;
}
}
}
2001-05-23 15:26:42 +02:00
if (blob->blb_pages) {
2001-12-24 03:51:06 +01:00
delete blob->blb_pages;
2001-05-23 15:26:42 +02:00
blob->blb_pages = NULL;
}
if (purge_flag)
2001-12-24 03:51:06 +01:00
delete blob;
2001-05-23 15:26:42 +02:00
}
static void slice_callback(array_slice* arg, ULONG count, DSC* descriptors)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* s l i c e _ c a l l b a c k
*
**************************************
*
* Functional description
* Perform slice assignment.
*
**************************************/
dsc* array_desc = descriptors;
dsc* slice_desc = &arg->slice_desc;
BLOB_PTR* const next =
(BLOB_PTR*) slice_desc->dsc_address + arg->slice_element_length;
2001-05-23 15:26:42 +02:00
if (next > arg->slice_end)
ERR_post(isc_out_of_bounds, 0);
2001-05-23 15:26:42 +02:00
if ((BLOB_PTR *) array_desc->dsc_address < arg->slice_base)
2001-05-23 15:26:42 +02:00
ERR_error(198); /* msg 198 array subscript computation error */
if (arg->slice_direction) {
/* Storing INTO array */
/* FROM slice_desc TO array_desc */
/* If storing beyond the high-water mark, ensure elements
* from the high-water mark to current position are zeroed
*/
// Since we are only initializing, it makes sense to throw away
// the constness of arg->slice_high_water.
const SLONG l =
(BLOB_PTR*) array_desc->dsc_address - arg->slice_high_water;
2001-05-23 15:26:42 +02:00
if (l > 0)
memset(const_cast<BLOB_PTR*>(arg->slice_high_water), 0, l);
2001-05-23 15:26:42 +02:00
/* The individual elements of a varying string array may not be aligned
correctly. If they aren't, some RISC machines may break. In those
cases, calculate the actual length and then move the length and
text manually. */
if (array_desc->dsc_dtype == dtype_varying &&
(U_IPTR) array_desc->dsc_address !=
FB_ALIGN((U_IPTR) array_desc->dsc_address,
2002-10-29 17:27:47 +01:00
(MIN(sizeof(USHORT), ALIGNMENT))))
{
/* Note: cannot remove this GET_THREAD_DATA without api change
2001-05-23 15:26:42 +02:00
to slice callback routines */
thread_db* tdbb = GET_THREAD_DATA;
2001-05-23 15:26:42 +02:00
const USHORT tmp_len = array_desc->dsc_length;
STR tmp_buffer = FB_NEW_RPT(*tdbb->tdbb_default, tmp_len) str();
const char* p;
const USHORT len = MOV_make_string(slice_desc,
2001-05-23 15:26:42 +02:00
INTL_TEXT_TYPE(*array_desc),
&p,
2002-10-29 17:27:47 +01:00
reinterpret_cast<vary*>(tmp_buffer->str_data),
tmp_len);
2001-05-23 15:26:42 +02:00
MOVE_FAST(&len, array_desc->dsc_address, sizeof(USHORT));
MOVE_FAST(p, array_desc->dsc_address + sizeof(USHORT), (int) len);
2001-12-24 03:51:06 +01:00
delete tmp_buffer;
2001-05-23 15:26:42 +02:00
}
else
2002-10-29 17:27:47 +01:00
{
2001-05-23 15:26:42 +02:00
MOV_move(slice_desc, array_desc);
2002-10-29 17:27:47 +01:00
}
const BLOB_PTR* const end =
(BLOB_PTR*) array_desc->dsc_address + array_desc->dsc_length;
if (end > arg->slice_high_water)
arg->slice_high_water = end;
2001-05-23 15:26:42 +02:00
}
else {
/* Fetching FROM array */
/* FROM array_desc TO slice_desc */
/* If the element is under the high-water mark, fetch it,
* otherwise just zero it
2001-05-23 15:26:42 +02:00
*/
if ((BLOB_PTR *) array_desc->dsc_address < arg->slice_high_water) {
2001-05-23 15:26:42 +02:00
/* If a varying string isn't aligned correctly, calculate the actual
length and then treat the string as if it had type text. */
if (array_desc->dsc_dtype == dtype_varying &&
(U_IPTR) array_desc->dsc_address !=
FB_ALIGN((U_IPTR) array_desc->dsc_address,
(MIN(sizeof(USHORT), ALIGNMENT))))
{
// temp_desc will vanish at the end of the block, but it's used
// only as a way to transfer blocks of memory.
dsc temp_desc;
2001-05-23 15:26:42 +02:00
temp_desc.dsc_dtype = dtype_text;
temp_desc.dsc_sub_type = array_desc->dsc_sub_type;
temp_desc.dsc_scale = array_desc->dsc_scale;
temp_desc.dsc_flags = array_desc->dsc_flags;
MOVE_FAST(array_desc->dsc_address, &temp_desc.dsc_length,
sizeof(USHORT));
temp_desc.dsc_address =
array_desc->dsc_address + sizeof(USHORT);
MOV_move(&temp_desc, slice_desc);
}
else
MOV_move(array_desc, slice_desc);
++arg->slice_count;
}
else {
const SLONG l = slice_desc->dsc_length;
if (l)
memset(slice_desc->dsc_address, 0, l);
}
2001-05-23 15:26:42 +02:00
}
slice_desc->dsc_address = next;
}
static blb* store_array(thread_db* tdbb, jrd_tra* transaction, bid* blob_id)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* s t o r e _ a r r a y
*
**************************************
*
* Functional description
* Actually store an array. Oh boy!
*
**************************************/
SET_TDBB(tdbb);
/* Validate array */
ArrayField* array = find_array(transaction, blob_id);
if (!array)
2001-05-23 15:26:42 +02:00
return NULL;
/* Create blob for array */
blb* blob = BLB_create2(tdbb, transaction, blob_id, 0, NULL);
2001-05-23 15:26:42 +02:00
blob->blb_flags |= BLB_stream;
/* Write out array descriptor */
BLB_put_segment(tdbb, blob,
reinterpret_cast<const UCHAR*>(&array->arr_desc),
array->arr_desc.iad_length);
2001-05-23 15:26:42 +02:00
/* Write out actual array */
2003-12-03 09:19:24 +01:00
const USHORT seg_limit = 32768;
const BLOB_PTR* p = (BLOB_PTR*) array->arr_data;
SLONG length = array->arr_effective_length;
while (length > seg_limit) {
BLB_put_segment(tdbb, blob, p, seg_limit);
length -= seg_limit;
p += seg_limit;
}
2001-05-23 15:26:42 +02:00
if (length)
BLB_put_segment(tdbb, blob, p, (USHORT) length);
BLB_close(tdbb, blob);
return blob;
}