2015-02-19 15:15:00 +01:00
|
|
|
/*
|
|
|
|
* The contents of this file are subject to the Initial
|
|
|
|
* Developer's 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.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed AS IS,
|
|
|
|
* 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 Dmitry Yemanov
|
|
|
|
* for the Firebird Open Source RDBMS project.
|
|
|
|
*
|
|
|
|
* Copyright (c) 2015 Dmitry Yemanov <dimitr@firebirdsql.org>
|
|
|
|
* and all contributors signed below.
|
|
|
|
*
|
|
|
|
* All Rights Reserved.
|
|
|
|
* Contributor(s): ______________________________________.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef JRD_RECORD_H
|
|
|
|
#define JRD_RECORD_H
|
|
|
|
|
|
|
|
#include "../common/classes/array.h"
|
|
|
|
#include "../jrd/pag.h"
|
|
|
|
#include "../jrd/val.h"
|
|
|
|
|
|
|
|
namespace Jrd
|
|
|
|
{
|
|
|
|
// Record flags
|
|
|
|
const UCHAR REC_fake_nulls = 1; // all fields simulate being NULLs
|
|
|
|
const UCHAR REC_gc_active = 2; // relation garbage collect record block in use
|
|
|
|
const UCHAR REC_undo_active = 4; // record block in use for undo purposes
|
|
|
|
|
|
|
|
class Record
|
|
|
|
{
|
|
|
|
template <UCHAR FLAG> friend class AutoTempRecord;
|
|
|
|
|
|
|
|
public:
|
|
|
|
Record(MemoryPool& p, const Format* format, UCHAR flags = 0)
|
|
|
|
: m_precedence(p), m_data(p)
|
|
|
|
{
|
2015-02-19 20:50:22 +01:00
|
|
|
m_data.resize(format->fmt_length);
|
|
|
|
m_format = format;
|
|
|
|
m_flags = flags;
|
2015-02-19 15:15:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Record(MemoryPool& p, const Record* other)
|
2015-02-19 20:50:22 +01:00
|
|
|
: m_precedence(p), m_data(p, other->m_data),
|
|
|
|
m_format(other->m_format), m_flags(other->m_flags)
|
|
|
|
{}
|
2015-02-19 15:15:00 +01:00
|
|
|
|
|
|
|
void reset(const Format* format = NULL, UCHAR flags = 0)
|
|
|
|
{
|
|
|
|
if (format && format != m_format)
|
|
|
|
{
|
|
|
|
m_data.resize(format->fmt_length);
|
|
|
|
m_format = format;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_flags = flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
void setNull(USHORT id)
|
|
|
|
{
|
|
|
|
fb_assert(!(m_flags & REC_fake_nulls));
|
2015-02-19 20:50:22 +01:00
|
|
|
getData()[id >> 3] |= (1 << (id & 7));
|
2015-02-19 15:15:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void clearNull(USHORT id)
|
|
|
|
{
|
|
|
|
fb_assert(!(m_flags & REC_fake_nulls));
|
2015-02-19 20:50:22 +01:00
|
|
|
getData()[id >> 3] &= ~(1 << (id & 7));
|
2015-02-19 15:15:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool isNull(USHORT id) const
|
|
|
|
{
|
|
|
|
if (m_flags & REC_fake_nulls)
|
|
|
|
return true;
|
|
|
|
|
2015-02-19 20:50:22 +01:00
|
|
|
return ((getData()[id >> 3] & (1 << (id & 7))) != 0);
|
2015-02-19 15:15:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void nullify()
|
|
|
|
{
|
|
|
|
// Zero the record buffer and initialize all fields to NULLs
|
|
|
|
const size_t null_bytes = (m_format->fmt_count + 7) >> 3;
|
2015-02-19 20:50:22 +01:00
|
|
|
memset(getData(), 0xFF, null_bytes);
|
|
|
|
memset(getData() + null_bytes, 0, getLength() - null_bytes);
|
2015-02-19 15:15:00 +01:00
|
|
|
|
|
|
|
// Record has real NULLs now, so clear the "fake-nulls" flag
|
|
|
|
m_flags &= ~REC_fake_nulls;
|
|
|
|
}
|
|
|
|
|
|
|
|
PageStack& getPrecedence()
|
|
|
|
{
|
|
|
|
return m_precedence;
|
|
|
|
}
|
|
|
|
|
|
|
|
void pushPrecedence(const PageNumber& page)
|
|
|
|
{
|
|
|
|
m_precedence.push(page);
|
|
|
|
}
|
|
|
|
|
|
|
|
void copyFrom(const Record* other)
|
|
|
|
{
|
|
|
|
m_format = other->m_format;
|
|
|
|
m_flags = other->m_flags;
|
|
|
|
|
|
|
|
copyDataFrom(other);
|
|
|
|
}
|
|
|
|
|
|
|
|
void copyDataFrom(const Record* other, bool assertLength = false)
|
|
|
|
{
|
|
|
|
if (assertLength)
|
|
|
|
fb_assert(getLength() == other->getLength());
|
|
|
|
|
|
|
|
m_data.assign(other->m_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
void copyDataFrom(const UCHAR* data)
|
|
|
|
{
|
2015-02-19 20:50:22 +01:00
|
|
|
memcpy(getData(), data, getLength());
|
2015-02-19 15:15:00 +01:00
|
|
|
}
|
|
|
|
|
2015-03-13 16:26:22 +01:00
|
|
|
void copyDataTo(UCHAR* data) const
|
2015-02-19 15:15:00 +01:00
|
|
|
{
|
2015-02-19 20:50:22 +01:00
|
|
|
memcpy(data, getData(), getLength());
|
2015-02-19 15:15:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const Format* getFormat() const
|
|
|
|
{
|
|
|
|
return m_format;
|
|
|
|
}
|
|
|
|
|
|
|
|
void fakeNulls()
|
|
|
|
{
|
|
|
|
m_flags |= REC_fake_nulls;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isNull() const
|
|
|
|
{
|
|
|
|
return (m_flags & REC_fake_nulls);
|
|
|
|
}
|
|
|
|
|
|
|
|
ULONG getLength() const
|
|
|
|
{
|
|
|
|
return m_format->fmt_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
UCHAR* getData()
|
|
|
|
{
|
|
|
|
return m_data.begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
const UCHAR* getData() const
|
|
|
|
{
|
|
|
|
return m_data.begin();
|
|
|
|
}
|
|
|
|
|
2015-02-19 16:53:42 +01:00
|
|
|
bool testFlags(UCHAR mask) const
|
2015-02-19 15:15:00 +01:00
|
|
|
{
|
2015-02-20 16:31:07 +01:00
|
|
|
return ((m_flags & mask) != 0);
|
2015-02-19 15:15:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
PageStack m_precedence; // stack of higher precedence pages/transactions
|
|
|
|
Firebird::Array<UCHAR> m_data; // space for record data
|
|
|
|
const Format* m_format; // what the data looks like
|
|
|
|
UCHAR m_flags; // misc record flags
|
|
|
|
};
|
|
|
|
|
|
|
|
// Wrapper for reusable temporary records
|
|
|
|
|
|
|
|
template <UCHAR FLAG>
|
|
|
|
class AutoTempRecord
|
|
|
|
{
|
|
|
|
public:
|
2015-03-13 16:26:22 +01:00
|
|
|
explicit AutoTempRecord(Record* record = NULL)
|
2015-02-19 15:15:00 +01:00
|
|
|
: m_record(record)
|
|
|
|
{
|
|
|
|
// validate record and its flag
|
|
|
|
fb_assert(!record || (record->m_flags & FLAG));
|
|
|
|
}
|
|
|
|
|
|
|
|
~AutoTempRecord()
|
|
|
|
{
|
|
|
|
release();
|
|
|
|
}
|
|
|
|
|
|
|
|
Record* operator=(Record* record)
|
|
|
|
{
|
2016-04-04 09:15:30 +02:00
|
|
|
// class object can be initialized just once
|
2015-02-19 15:15:00 +01:00
|
|
|
fb_assert(!m_record);
|
2016-04-04 09:15:30 +02:00
|
|
|
// validate record and its flag
|
|
|
|
fb_assert(!record || (record->m_flags & FLAG));
|
2015-02-19 15:15:00 +01:00
|
|
|
|
|
|
|
m_record = record;
|
|
|
|
return m_record;
|
|
|
|
}
|
|
|
|
|
|
|
|
void release()
|
|
|
|
{
|
|
|
|
if (m_record)
|
|
|
|
{
|
|
|
|
m_record->m_flags &= ~FLAG;
|
|
|
|
m_record = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
operator Record*()
|
|
|
|
{
|
|
|
|
return m_record;
|
|
|
|
}
|
|
|
|
|
|
|
|
Record* operator->()
|
|
|
|
{
|
|
|
|
return m_record;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Record* m_record;
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef AutoTempRecord<REC_gc_active> AutoGCRecord;
|
|
|
|
typedef AutoTempRecord<REC_undo_active> AutoUndoRecord;
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
#endif // JRD_RECORD_H
|