2009-12-09 19:45:44 +01:00
|
|
|
/*
|
|
|
|
* 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): ______________________________________.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "firebird.h"
|
|
|
|
#include "../jrd/common.h"
|
|
|
|
#include "../jrd/jrd.h"
|
|
|
|
#include "../jrd/btr.h"
|
|
|
|
#include "../jrd/intl.h"
|
|
|
|
#include "../jrd/req.h"
|
|
|
|
#include "../jrd/cmp_proto.h"
|
|
|
|
#include "../jrd/evl_proto.h"
|
|
|
|
#include "../jrd/intl_proto.h"
|
|
|
|
#include "../jrd/met_proto.h"
|
|
|
|
#include "../jrd/mov_proto.h"
|
|
|
|
#include "../jrd/vio_proto.h"
|
|
|
|
|
|
|
|
#include "RecordSource.h"
|
|
|
|
|
|
|
|
using namespace Firebird;
|
|
|
|
using namespace Jrd;
|
|
|
|
|
|
|
|
// -----------------------------
|
|
|
|
// Data access: external sorting
|
|
|
|
// -----------------------------
|
|
|
|
|
|
|
|
SortedStream::SortedStream(CompilerScratch* csb, RecordSource* next, SortMap* map)
|
|
|
|
: m_next(next), m_map(map)
|
|
|
|
{
|
|
|
|
fb_assert(m_next && m_map);
|
|
|
|
|
|
|
|
m_impure = CMP_impure(csb, sizeof(Impure));
|
|
|
|
}
|
|
|
|
|
|
|
|
void SortedStream::open(thread_db* tdbb)
|
|
|
|
{
|
|
|
|
jrd_req* const request = tdbb->getRequest();
|
|
|
|
Impure* const impure = (Impure*) ((UCHAR*) request + m_impure);
|
|
|
|
|
|
|
|
impure->irsb_flags = irsb_open;
|
|
|
|
|
|
|
|
// get rid of the old sort areas if this request has been used already
|
2010-03-16 12:19:29 +01:00
|
|
|
delete impure->irsb_sort;
|
|
|
|
impure->irsb_sort = init(tdbb);
|
2009-12-09 19:45:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void SortedStream::close(thread_db* tdbb)
|
|
|
|
{
|
|
|
|
jrd_req* const request = tdbb->getRequest();
|
|
|
|
|
|
|
|
invalidateRecords(request);
|
|
|
|
|
|
|
|
Impure* const impure = (Impure*) ((UCHAR*) request + m_impure);
|
|
|
|
|
|
|
|
if (impure->irsb_flags & irsb_open)
|
|
|
|
{
|
|
|
|
impure->irsb_flags &= ~irsb_open;
|
|
|
|
|
2010-03-16 12:19:29 +01:00
|
|
|
delete impure->irsb_sort;
|
|
|
|
impure->irsb_sort = NULL;
|
2009-12-09 19:45:44 +01:00
|
|
|
|
|
|
|
m_next->close(tdbb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SortedStream::getRecord(thread_db* tdbb)
|
|
|
|
{
|
|
|
|
jrd_req* const request = tdbb->getRequest();
|
|
|
|
Impure* const impure = (Impure*) ((UCHAR*) request + m_impure);
|
|
|
|
|
|
|
|
if (!(impure->irsb_flags & irsb_open))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
UCHAR* const data = getData(tdbb);
|
|
|
|
|
|
|
|
if (!data)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-12-13 11:41:53 +01:00
|
|
|
mapData(tdbb, request, data);
|
2009-12-09 19:45:44 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SortedStream::refetchRecord(thread_db* tdbb)
|
|
|
|
{
|
|
|
|
return m_next->refetchRecord(tdbb);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SortedStream::lockRecord(thread_db* tdbb)
|
|
|
|
{
|
|
|
|
return m_next->lockRecord(tdbb);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SortedStream::dump(thread_db* tdbb, UCharBuffer& buffer)
|
|
|
|
{
|
|
|
|
buffer.add(isc_info_rsb_begin);
|
|
|
|
|
|
|
|
buffer.add(isc_info_rsb_type);
|
|
|
|
buffer.add(isc_info_rsb_sort);
|
|
|
|
|
|
|
|
m_next->dump(tdbb, buffer);
|
|
|
|
|
|
|
|
buffer.add(isc_info_rsb_end);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SortedStream::markRecursive()
|
|
|
|
{
|
|
|
|
m_next->markRecursive();
|
|
|
|
}
|
|
|
|
|
2009-12-12 20:00:43 +01:00
|
|
|
void SortedStream::findUsedStreams(StreamsArray& streams)
|
2009-12-09 19:45:44 +01:00
|
|
|
{
|
|
|
|
m_next->findUsedStreams(streams);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SortedStream::invalidateRecords(jrd_req* request)
|
|
|
|
{
|
|
|
|
m_next->invalidateRecords(request);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SortedStream::nullRecords(thread_db* tdbb)
|
|
|
|
{
|
|
|
|
m_next->nullRecords(tdbb);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SortedStream::saveRecords(thread_db* tdbb)
|
|
|
|
{
|
|
|
|
m_next->saveRecords(tdbb);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SortedStream::restoreRecords(thread_db* tdbb)
|
|
|
|
{
|
|
|
|
m_next->restoreRecords(tdbb);
|
|
|
|
}
|
|
|
|
|
2010-03-16 12:19:29 +01:00
|
|
|
Sort* SortedStream::init(thread_db* tdbb)
|
2009-12-09 19:45:44 +01:00
|
|
|
{
|
|
|
|
jrd_req* const request = tdbb->getRequest();
|
|
|
|
|
|
|
|
m_next->open(tdbb);
|
|
|
|
ULONG records = 0;
|
|
|
|
|
|
|
|
// Initialize for sort. If this is really a project operation,
|
|
|
|
// establish a callback routine to reject duplicate records.
|
|
|
|
|
2010-03-16 12:19:29 +01:00
|
|
|
Firebird::AutoPtr<Sort> scb(FB_NEW(request->req_sorts.getPool())
|
|
|
|
Sort(tdbb->getDatabase(), &request->req_sorts,
|
|
|
|
m_map->length, m_map->keyItems.getCount(), m_map->keyItems.getCount(),
|
|
|
|
m_map->keyItems.begin(),
|
|
|
|
((m_map->flags & FLAG_PROJECT) ? rejectDuplicate : NULL), 0));
|
2009-12-09 19:45:44 +01:00
|
|
|
|
2010-03-16 12:19:29 +01:00
|
|
|
// Pump the input stream dry while pushing records into sort. For
|
|
|
|
// each record, map all fields into the sort record. The reverse
|
|
|
|
// mapping is done in get_sort().
|
2009-12-09 19:45:44 +01:00
|
|
|
|
2010-03-16 12:19:29 +01:00
|
|
|
dsc to, temp;
|
2009-12-09 19:45:44 +01:00
|
|
|
|
2010-03-16 12:19:29 +01:00
|
|
|
while (m_next->getRecord(tdbb))
|
|
|
|
{
|
|
|
|
records++;
|
2009-12-09 19:45:44 +01:00
|
|
|
|
2010-03-16 12:19:29 +01:00
|
|
|
// "Put" a record to sort. Actually, get the address of a place
|
|
|
|
// to build a record.
|
2009-12-09 19:45:44 +01:00
|
|
|
|
2010-03-16 12:19:29 +01:00
|
|
|
UCHAR* data = NULL;
|
|
|
|
scb->put(tdbb, reinterpret_cast<ULONG**>(&data));
|
2009-12-09 19:45:44 +01:00
|
|
|
|
2010-03-16 12:19:29 +01:00
|
|
|
// Zero out the sort key. This solves a multitude of problems.
|
2009-12-09 19:45:44 +01:00
|
|
|
|
2010-03-16 12:19:29 +01:00
|
|
|
memset(data, 0, m_map->length);
|
2009-12-09 19:45:44 +01:00
|
|
|
|
2010-03-16 12:19:29 +01:00
|
|
|
// Loop thru all field (keys and hangers on) involved in the sort.
|
|
|
|
// Be careful to null field all unused bytes in the sort key.
|
2009-12-09 19:45:44 +01:00
|
|
|
|
2010-03-16 12:19:29 +01:00
|
|
|
const SortMap::Item* const end_item = m_map->items.begin() + m_map->items.getCount();
|
|
|
|
for (SortMap::Item* item = m_map->items.begin(); item < end_item; item++)
|
|
|
|
{
|
|
|
|
to = item->desc;
|
|
|
|
to.dsc_address = data + (IPTR) to.dsc_address;
|
|
|
|
bool flag = false;
|
|
|
|
dsc* from = 0;
|
2009-12-09 19:45:44 +01:00
|
|
|
|
2010-03-16 12:19:29 +01:00
|
|
|
if (item->node)
|
|
|
|
{
|
|
|
|
from = EVL_expr(tdbb, item->node);
|
|
|
|
if (request->req_flags & req_null)
|
|
|
|
flag = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
from = &temp;
|
2009-12-09 19:45:44 +01:00
|
|
|
|
2010-03-16 12:19:29 +01:00
|
|
|
record_param* const rpb = &request->req_rpb[item->stream];
|
2009-12-09 19:45:44 +01:00
|
|
|
|
2010-03-16 12:19:29 +01:00
|
|
|
if (item->fieldId < 0)
|
|
|
|
{
|
|
|
|
switch (item->fieldId)
|
2009-12-09 19:45:44 +01:00
|
|
|
{
|
2010-03-16 12:19:29 +01:00
|
|
|
case ID_TRANS:
|
|
|
|
*reinterpret_cast<SLONG*>(to.dsc_address) = rpb->rpb_transaction_nr;
|
|
|
|
break;
|
|
|
|
case ID_DBKEY:
|
|
|
|
*reinterpret_cast<SINT64*>(to.dsc_address) = rpb->rpb_number.getValue();
|
|
|
|
break;
|
|
|
|
case ID_DBKEY_VALID:
|
|
|
|
*to.dsc_address = (UCHAR) rpb->rpb_number.isValid();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fb_assert(false);
|
2009-12-09 19:45:44 +01:00
|
|
|
}
|
2010-03-16 12:19:29 +01:00
|
|
|
continue;
|
|
|
|
}
|
2009-12-09 19:45:44 +01:00
|
|
|
|
2010-03-16 12:19:29 +01:00
|
|
|
if (!EVL_field(rpb->rpb_relation, rpb->rpb_record, item->fieldId, from))
|
|
|
|
{
|
|
|
|
flag = true;
|
2009-12-09 19:45:44 +01:00
|
|
|
}
|
2010-03-16 12:19:29 +01:00
|
|
|
}
|
2009-12-09 19:45:44 +01:00
|
|
|
|
2010-03-16 12:19:29 +01:00
|
|
|
*(data + item->flagOffset) = flag ? TRUE : FALSE;
|
2009-12-09 19:45:44 +01:00
|
|
|
|
2010-03-16 12:19:29 +01:00
|
|
|
if (!flag)
|
|
|
|
{
|
|
|
|
// If moving a TEXT item into the key portion of the sort record,
|
|
|
|
// then want to sort by language dependent order.
|
2009-12-09 19:45:44 +01:00
|
|
|
|
2010-03-16 12:19:29 +01:00
|
|
|
if (IS_INTL_DATA(&item->desc) &&
|
|
|
|
(USHORT)(IPTR) item->desc.dsc_address < m_map->keyLength * sizeof(ULONG))
|
|
|
|
{
|
|
|
|
INTL_string_to_key(tdbb, INTL_INDEX_TYPE(&item->desc), from, &to,
|
|
|
|
(m_map->flags & FLAG_UNIQUE ? INTL_KEY_UNIQUE : INTL_KEY_SORT));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
MOV_move(tdbb, from, &to);
|
2009-12-09 19:45:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-16 12:19:29 +01:00
|
|
|
scb->sort(tdbb);
|
|
|
|
|
2009-12-09 19:45:44 +01:00
|
|
|
// For the sake of prudence, set all record parameter blocks to contain
|
2009-12-13 11:41:53 +01:00
|
|
|
// the most recent format. This will guarentee that all fields mapped
|
2009-12-09 19:45:44 +01:00
|
|
|
// back to records by get_sort() have homes in the target record.
|
|
|
|
|
|
|
|
if (records)
|
|
|
|
{
|
|
|
|
SSHORT stream = -1;
|
|
|
|
|
2009-12-12 21:36:56 +01:00
|
|
|
const SortMap::Item* const end_item = m_map->items.begin() + m_map->items.getCount();
|
|
|
|
for (SortMap::Item* item = m_map->items.begin(); item < end_item; item++)
|
2009-12-09 19:45:44 +01:00
|
|
|
{
|
2009-12-12 21:36:56 +01:00
|
|
|
if (item->node && item->node->nod_type != nod_field)
|
2009-12-09 19:45:44 +01:00
|
|
|
continue;
|
|
|
|
|
2009-12-12 21:36:56 +01:00
|
|
|
if (item->stream == stream)
|
2009-12-09 19:45:44 +01:00
|
|
|
continue;
|
|
|
|
|
2009-12-12 21:36:56 +01:00
|
|
|
stream = item->stream;
|
2009-12-09 19:45:44 +01:00
|
|
|
record_param* const rpb = &request->req_rpb[stream];
|
|
|
|
|
|
|
|
if (rpb->rpb_relation)
|
|
|
|
{
|
|
|
|
VIO_record(tdbb, rpb, MET_current(tdbb, rpb->rpb_relation), tdbb->getDefaultPool());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-16 12:19:29 +01:00
|
|
|
return scb.release();
|
2009-12-09 19:45:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
UCHAR* SortedStream::getData(thread_db* tdbb)
|
|
|
|
{
|
|
|
|
jrd_req* const request = tdbb->getRequest();
|
|
|
|
Impure* const impure = (Impure*) ((UCHAR*) request + m_impure);
|
|
|
|
|
|
|
|
ULONG* data = NULL;
|
2010-03-16 12:19:29 +01:00
|
|
|
impure->irsb_sort->get(tdbb, &data);
|
2009-12-09 19:45:44 +01:00
|
|
|
|
|
|
|
return reinterpret_cast<UCHAR*>(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SortedStream::mapData(thread_db* tdbb, jrd_req* request, UCHAR* data)
|
|
|
|
{
|
|
|
|
dsc from, to;
|
|
|
|
|
2009-12-12 21:36:56 +01:00
|
|
|
const SortMap::Item* const end_item = m_map->items.begin() + m_map->items.getCount();
|
2009-12-09 19:45:44 +01:00
|
|
|
|
2009-12-12 21:36:56 +01:00
|
|
|
for (SortMap::Item* item = m_map->items.begin(); item < end_item; item++)
|
2009-12-09 19:45:44 +01:00
|
|
|
{
|
2009-12-12 21:36:56 +01:00
|
|
|
const UCHAR flag = *(data + item->flagOffset);
|
|
|
|
from = item->desc;
|
2009-12-09 19:45:44 +01:00
|
|
|
from.dsc_address = data + (IPTR) from.dsc_address;
|
2009-12-12 21:36:56 +01:00
|
|
|
jrd_nod* const node = item->node;
|
2009-12-09 19:45:44 +01:00
|
|
|
|
|
|
|
if (node && node->nod_type != nod_field)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// if moving a TEXT item into the key portion of the sort record,
|
|
|
|
// then want to sort by language dependent order
|
|
|
|
|
|
|
|
// in the case below a nod_field is being converted to
|
|
|
|
// a sort key, there is a later nod_field in the item
|
|
|
|
// list that contains the data to send back
|
|
|
|
|
2009-12-12 21:36:56 +01:00
|
|
|
if (IS_INTL_DATA(&item->desc) &&
|
|
|
|
(USHORT)(IPTR) item->desc.dsc_address < m_map->keyLength * sizeof(ULONG))
|
2009-12-09 19:45:44 +01:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-12-12 21:36:56 +01:00
|
|
|
record_param* const rpb = &request->req_rpb[item->stream];
|
2009-12-09 19:45:44 +01:00
|
|
|
|
2009-12-12 21:36:56 +01:00
|
|
|
const SSHORT id = item->fieldId;
|
2009-12-09 19:45:44 +01:00
|
|
|
|
|
|
|
if (id < 0)
|
|
|
|
{
|
|
|
|
switch (id)
|
|
|
|
{
|
2009-12-12 21:36:56 +01:00
|
|
|
case ID_TRANS:
|
2009-12-09 19:45:44 +01:00
|
|
|
rpb->rpb_transaction_nr = *reinterpret_cast<SLONG*>(from.dsc_address);
|
|
|
|
break;
|
2009-12-12 21:36:56 +01:00
|
|
|
case ID_DBKEY:
|
2009-12-09 19:45:44 +01:00
|
|
|
rpb->rpb_number.setValue(*reinterpret_cast<SINT64*>(from.dsc_address));
|
|
|
|
break;
|
2009-12-12 21:36:56 +01:00
|
|
|
case ID_DBKEY_VALID:
|
2009-12-09 19:45:44 +01:00
|
|
|
rpb->rpb_number.setValid(*from.dsc_address != 0);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fb_assert(false);
|
|
|
|
}
|
2009-12-12 21:36:56 +01:00
|
|
|
|
2009-12-09 19:45:44 +01:00
|
|
|
rpb->rpb_stream_flags |= RPB_s_refetch;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
Record* const record = rpb->rpb_record;
|
|
|
|
|
|
|
|
if (record && !flag && !record->rec_format)
|
|
|
|
{
|
|
|
|
fb_assert(record->rec_fmt_bk);
|
|
|
|
record->rec_format = record->rec_fmt_bk;
|
|
|
|
}
|
|
|
|
|
|
|
|
EVL_field(NULL, record, id, &to);
|
|
|
|
|
|
|
|
if (flag)
|
|
|
|
{
|
|
|
|
SET_NULL(record, id);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
MOV_move(tdbb, &from, &to);
|
|
|
|
CLEAR_NULL(record, id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|