mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-29 06:43:03 +01:00
316 lines
8.3 KiB
C++
316 lines
8.3 KiB
C++
/*
|
|
* PROGRAM: JRD Access Method
|
|
* MODULE: RecordNumber.h
|
|
* DESCRIPTION: Handler class for table record number
|
|
*
|
|
* 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 Nickolay Samofatov
|
|
* for the Firebird Open Source RDBMS project.
|
|
*
|
|
* Copyright (c) 2004 Nickolay Samofatov <nickolay@broadviewsoftware.com>
|
|
* and all contributors signed below.
|
|
*
|
|
* All Rights Reserved.
|
|
* Contributor(s): ______________________________________.
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
#ifndef JRD_RECORDNUMBER_H
|
|
#define JRD_RECORDNUMBER_H
|
|
|
|
#include "../common/gdsassert.h"
|
|
|
|
const SINT64 BOF_NUMBER = QUADCONST(-1);
|
|
|
|
// This class is to be used everywhere you may need to handle record numbers. We
|
|
// deliberately not define implicit conversions to and from integer to allow
|
|
// compiler check errors on our migration path from 32-bit to 64-bit record
|
|
// numbers.
|
|
class RecordNumber
|
|
{
|
|
public:
|
|
// Packed record number represents common layout of RDB$DB_KEY and BLOB ID.
|
|
// To ensure binary compatibility with old (pre-ODS11) databases it differs
|
|
// for big- and little-endian machines.
|
|
class Packed
|
|
{
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wunused-private-field"
|
|
#endif
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
private:
|
|
UCHAR bid_number_up; // Upper byte of 40-bit record number
|
|
UCHAR bid_reserved_for_relation; // Reserved for future
|
|
public:
|
|
USHORT bid_relation_id; // Relation id (or null)
|
|
private:
|
|
ULONG bid_number; // Lower bytes of 40-bit record number
|
|
// or 32-bit temporary ID of blob or array
|
|
#else
|
|
public:
|
|
USHORT bid_relation_id; /* Relation id (or null) */
|
|
|
|
private:
|
|
UCHAR bid_reserved_for_relation; /* Reserved for future expansion of relation space. */
|
|
UCHAR bid_number_up; // Upper byte of 40-bit record number
|
|
ULONG bid_number; // Lower bytes of 40-bit record number
|
|
// or 32-bit temporary ID of blob or array
|
|
#endif
|
|
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic pop
|
|
#endif
|
|
|
|
public:
|
|
ULONG& bid_temp_id()
|
|
{
|
|
return bid_number;
|
|
}
|
|
|
|
ULONG bid_temp_id() const
|
|
{
|
|
return bid_number;
|
|
}
|
|
|
|
// Handle encoding of record number for RDB$DB_KEY and BLOB ID structure.
|
|
// BLOB ID is stored in database thus we do encode large record numbers
|
|
// in a manner which preserves backward compatibility with older ODS.
|
|
// The same applies to bid_decode routine below.
|
|
inline void bid_encode(SINT64 value)
|
|
{
|
|
// Use explicit casts to suppress 64-bit truncation warnings
|
|
// Store lower 32 bits of number
|
|
bid_number = static_cast<ULONG>(value);
|
|
// Store high 8 bits of number
|
|
bid_number_up = static_cast<UCHAR>(value >> 32);
|
|
}
|
|
|
|
inline SINT64 bid_decode() const
|
|
{
|
|
return bid_number + (static_cast<FB_UINT64>(bid_number_up) << 32);
|
|
}
|
|
};
|
|
|
|
// Default constructor.
|
|
inline RecordNumber() : value(0), valid(false) {}
|
|
|
|
// Copy constructor
|
|
inline RecordNumber(const RecordNumber& from) : value(from.value), valid(from.valid) {}
|
|
|
|
// Explicit constructor from 64-bit record number value
|
|
inline explicit RecordNumber(SINT64 number) : value(number), valid(true) {}
|
|
|
|
// Assignment operator
|
|
inline RecordNumber& operator =(const RecordNumber& from)
|
|
{
|
|
value = from.value;
|
|
valid = from.valid;
|
|
return *this;
|
|
}
|
|
|
|
inline bool operator ==(const RecordNumber& other) const
|
|
{
|
|
return value == other.value;
|
|
}
|
|
|
|
inline bool operator !=(const RecordNumber& other) const
|
|
{
|
|
return value != other.value;
|
|
}
|
|
|
|
inline bool operator > (const RecordNumber& other) const
|
|
{
|
|
return value > other.value;
|
|
}
|
|
|
|
inline bool operator < (const RecordNumber& other) const
|
|
{
|
|
return value < other.value;
|
|
}
|
|
|
|
inline bool operator >= (const RecordNumber& other) const
|
|
{
|
|
return value >= other.value;
|
|
}
|
|
|
|
inline bool operator <= (const RecordNumber& other) const
|
|
{
|
|
return value <= other.value;
|
|
}
|
|
|
|
inline void decrement() { value--; }
|
|
|
|
inline void increment() { value++; }
|
|
|
|
inline SINT64 getValue() const { return value; }
|
|
|
|
inline void setValue(SINT64 avalue) { value = avalue; }
|
|
|
|
bool isBof() const { return value == BOF_NUMBER; }
|
|
|
|
bool isValid() const { return valid; }
|
|
|
|
inline void decompose(USHORT records_per_page, // ~400 (8k page)
|
|
USHORT data_pages_per_pointer_page, // ~2000 (8k page)
|
|
USHORT& line,
|
|
USHORT& slot,
|
|
ULONG& pp_sequence) const
|
|
{
|
|
// Use explicit casts to suppress 64-bit truncation warnings
|
|
line = static_cast<USHORT>(value % records_per_page);
|
|
const ULONG sequence = static_cast<ULONG>(value / records_per_page);
|
|
slot = sequence % data_pages_per_pointer_page;
|
|
pp_sequence = sequence / data_pages_per_pointer_page;
|
|
}
|
|
|
|
inline void compose(USHORT records_per_page, // ~400 (8k page)
|
|
USHORT data_pages_per_pointer_page, // ~2000 (8k page)
|
|
USHORT line,
|
|
USHORT slot,
|
|
ULONG pp_sequence)
|
|
{
|
|
value = (((SINT64) pp_sequence) * data_pages_per_pointer_page + slot) * records_per_page + line;
|
|
}
|
|
|
|
// Handle encoding of record number for RDB$DB_KEY and BLOB ID structure.
|
|
inline void bid_encode(Packed* recno) const
|
|
{
|
|
recno->bid_encode(value);
|
|
}
|
|
|
|
inline void bid_decode(const Packed* recno)
|
|
{
|
|
value = recno->bid_decode();
|
|
}
|
|
|
|
inline void setValid(bool to_value)
|
|
{
|
|
valid = to_value;
|
|
}
|
|
|
|
private:
|
|
// Use signed value because negative values are widely used as flags in the
|
|
// engine. Since page number is the signed 32-bit integer and it is also may
|
|
// be stored in this structure we want sign extension to take place.
|
|
SINT64 value;
|
|
bool valid;
|
|
};
|
|
|
|
namespace Jrd
|
|
{
|
|
/* Blob id. A blob has two states -- temporary and permanent. In each
|
|
case, the blob id is 8 bytes (2 longwords) long. In the case of a
|
|
temporary blob, the first word is NULL and the second word points to
|
|
an internal blob block. In the case of a permanent blob, the first
|
|
word contains the relation id of the blob and the second the record
|
|
number of the first segment-clump. The two types of blobs can be
|
|
reliably distinguished by a zero or non-zero relation id. */
|
|
|
|
// This structure must occupy 8 bytes
|
|
struct bid
|
|
{
|
|
// This is how bid structure represented in public API.
|
|
// Must be present to enforce alignment rules when structure is declared on stack
|
|
struct bid_quad_struct
|
|
{
|
|
ULONG bid_quad_high;
|
|
ULONG bid_quad_low;
|
|
};
|
|
union // anonymous union
|
|
{
|
|
// Internal decomposition of the structure
|
|
RecordNumber::Packed bid_internal;
|
|
bid_quad_struct bid_quad;
|
|
};
|
|
|
|
ULONG& bid_temp_id()
|
|
{
|
|
// Make sure that compiler packed structure like we wanted
|
|
fb_assert(sizeof(*this) == 8);
|
|
|
|
return bid_internal.bid_temp_id();
|
|
}
|
|
|
|
ULONG bid_temp_id() const
|
|
{
|
|
// Make sure that compiler packed structure like we wanted
|
|
fb_assert(sizeof(*this) == 8);
|
|
|
|
return bid_internal.bid_temp_id();
|
|
}
|
|
|
|
bool isEmpty() const
|
|
{
|
|
// Make sure that compiler packed structure like we wanted
|
|
fb_assert(sizeof(*this) == 8);
|
|
|
|
return bid_quad.bid_quad_high == 0 && bid_quad.bid_quad_low == 0;
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
// Make sure that compiler packed structure like we wanted
|
|
fb_assert(sizeof(*this) == 8);
|
|
|
|
bid_quad.bid_quad_high = 0;
|
|
bid_quad.bid_quad_low = 0;
|
|
}
|
|
|
|
void set_temporary(ULONG temp_id)
|
|
{
|
|
// Make sure that compiler packed structure like we wanted
|
|
fb_assert(sizeof(*this) == 8);
|
|
|
|
clear();
|
|
bid_temp_id() = temp_id;
|
|
}
|
|
|
|
void set_permanent(USHORT relation_id, RecordNumber num)
|
|
{
|
|
// Make sure that compiler packed structure like we wanted
|
|
fb_assert(sizeof(*this) == 8);
|
|
|
|
clear();
|
|
bid_internal.bid_relation_id = relation_id;
|
|
num.bid_encode(&bid_internal);
|
|
}
|
|
|
|
RecordNumber get_permanent_number() const
|
|
{
|
|
// Make sure that compiler packed structure like we wanted
|
|
fb_assert(sizeof(*this) == 8);
|
|
|
|
RecordNumber temp;
|
|
temp.bid_decode(&bid_internal);
|
|
return temp;
|
|
}
|
|
|
|
bool operator == (const bid& other) const
|
|
{
|
|
// Make sure that compiler packed structure like we wanted
|
|
fb_assert(sizeof(*this) == 8);
|
|
|
|
return bid_quad.bid_quad_high == other.bid_quad.bid_quad_high &&
|
|
bid_quad.bid_quad_low == other.bid_quad.bid_quad_low;
|
|
}
|
|
};
|
|
|
|
} // namespace Jrd
|
|
|
|
|
|
#endif // JRD_RECORDNUMBER_H
|