8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 16:43:03 +01:00

Added RDB$TIME_ZONES virtual table.

This commit is contained in:
Adriano dos Santos Fernandes 2018-05-14 12:56:18 -03:00
parent ebe39c4b6a
commit 942d7f49f5
12 changed files with 338 additions and 143 deletions

View File

@ -223,6 +223,16 @@ select localtimestamp
# TODO: CURRENT_TIME and CURRENT_TIMESTAMP changes
## Virtual tables
### `RDB$TIME_ZONES` table
This virtual table lists time zones supported in the engine.
Columns:
- `RDB$TIME_ZONE_ID` type `INTEGER`
- `RDB$TIME_ZONE_NAME` type `CHAR(63)`
# Appendix: time zone regions

View File

@ -46,13 +46,14 @@ using namespace Firebird;
//-------------------------------------
struct TimeZoneDesc
namespace
{
const char* asciiName;
const UChar* icuName;
};
//-------------------------------------
struct TimeZoneDesc
{
const char* asciiName;
const UChar* icuName;
};
} // namespace
#include "./TimeZones.h"
@ -68,170 +69,175 @@ static void skipSpaces(const char*& p, const char* end);
//-------------------------------------
struct TimeZoneStartup
namespace
{
TimeZoneStartup(MemoryPool& pool)
: systemTimeZone(TimeZoneUtil::GMT_ZONE),
nameIdMap(pool)
struct TimeZoneStartup
{
TimeZoneStartup(MemoryPool& pool)
: systemTimeZone(TimeZoneUtil::GMT_ZONE),
nameIdMap(pool)
{
#if defined DEV_BUILD && defined TZ_UPDATE
tzUpdate();
tzUpdate();
#endif
for (USHORT i = 0; i < FB_NELEM(TIME_ZONE_LIST); ++i)
{
string s(TIME_ZONE_LIST[i].asciiName);
s.upper();
nameIdMap.put(s, i);
}
for (USHORT i = 0; i < FB_NELEM(TIME_ZONE_LIST); ++i)
{
string s(TIME_ZONE_LIST[i].asciiName);
s.upper();
nameIdMap.put(s, i);
}
UErrorCode icuErrorCode = U_ZERO_ERROR;
UErrorCode icuErrorCode = U_ZERO_ERROR;
Jrd::UnicodeUtil::ConversionICU& icuLib = Jrd::UnicodeUtil::getConversionICU();
UCalendar* icuCalendar = icuLib.ucalOpen(NULL, -1, NULL, UCAL_GREGORIAN, &icuErrorCode);
Jrd::UnicodeUtil::ConversionICU& icuLib = Jrd::UnicodeUtil::getConversionICU();
UCalendar* icuCalendar = icuLib.ucalOpen(NULL, -1, NULL, UCAL_GREGORIAN, &icuErrorCode);
if (!icuCalendar)
{
gds__log("ICU's ucal_open error opening the default callendar.");
return;
}
if (!icuCalendar)
{
gds__log("ICU's ucal_open error opening the default callendar.");
return;
}
UChar buffer[TimeZoneUtil::MAX_SIZE];
bool found = false;
UChar buffer[TimeZoneUtil::MAX_SIZE];
bool found = false;
int32_t len = icuLib.ucalGetTimeZoneID(icuCalendar, buffer, FB_NELEM(buffer), &icuErrorCode);
int32_t len = icuLib.ucalGetTimeZoneID(icuCalendar, buffer, FB_NELEM(buffer), &icuErrorCode);
if (!U_FAILURE(icuErrorCode))
{
bool error;
string bufferStrUnicode(reinterpret_cast<const char*>(buffer), len * sizeof(USHORT));
string bufferStrAscii(IntlUtil::convertUtf16ToAscii(bufferStrUnicode, &error));
found = getId(bufferStrAscii, systemTimeZone);
}
else
icuErrorCode = U_ZERO_ERROR;
if (!U_FAILURE(icuErrorCode))
{
bool error;
string bufferStrUnicode(reinterpret_cast<const char*>(buffer), len * sizeof(USHORT));
string bufferStrAscii(IntlUtil::convertUtf16ToAscii(bufferStrUnicode, &error));
found = getId(bufferStrAscii, systemTimeZone);
}
else
icuErrorCode = U_ZERO_ERROR;
if (found)
{
icuLib.ucalClose(icuCalendar);
return;
}
gds__log("ICU error retrieving the system time zone: %d. Fallbacking to displacement.", int(icuErrorCode));
int32_t displacement = (icuLib.ucalGet(icuCalendar, UCAL_ZONE_OFFSET, &icuErrorCode) +
icuLib.ucalGet(icuCalendar, UCAL_DST_OFFSET, &icuErrorCode)) / U_MILLIS_PER_MINUTE;
if (found)
{
icuLib.ucalClose(icuCalendar);
return;
if (!U_FAILURE(icuErrorCode))
{
int sign = displacement < 0 ? -1 : 1;
unsigned tzh = (unsigned) abs(int(displacement / 60));
unsigned tzm = (unsigned) abs(int(displacement % 60));
systemTimeZone = makeFromOffset(sign, tzh, tzm);
}
else
gds__log("Cannot retrieve the system time zone: %d.", int(icuErrorCode));
}
gds__log("ICU error retrieving the system time zone: %d. Fallbacking to displacement.", int(icuErrorCode));
int32_t displacement = (icuLib.ucalGet(icuCalendar, UCAL_ZONE_OFFSET, &icuErrorCode) +
icuLib.ucalGet(icuCalendar, UCAL_DST_OFFSET, &icuErrorCode)) / U_MILLIS_PER_MINUTE;
icuLib.ucalClose(icuCalendar);
if (!U_FAILURE(icuErrorCode))
bool getId(string name, USHORT& id)
{
int sign = displacement < 0 ? -1 : 1;
unsigned tzh = (unsigned) abs(int(displacement / 60));
unsigned tzm = (unsigned) abs(int(displacement % 60));
systemTimeZone = makeFromOffset(sign, tzh, tzm);
}
else
gds__log("Cannot retrieve the system time zone: %d.", int(icuErrorCode));
}
USHORT index;
name.upper();
bool getId(string name, USHORT& id)
{
USHORT index;
name.upper();
if (nameIdMap.get(name, index))
{
id = MAX_USHORT - index;
return true;
if (nameIdMap.get(name, index))
{
id = MAX_USHORT - index;
return true;
}
else
return false;
}
else
return false;
}
#if defined DEV_BUILD && defined TZ_UPDATE
void tzUpdate()
{
SortedObjectsArray<string> currentZones, icuZones;
for (unsigned i = 0; i < FB_NELEM(TIME_ZONE_LIST); ++i)
currentZones.push(TIME_ZONE_LIST[i].asciiName);
Jrd::UnicodeUtil::ConversionICU& icuLib = Jrd::UnicodeUtil::getConversionICU();
UErrorCode icuErrorCode = U_ZERO_ERROR;
UEnumeration* uenum = icuLib.ucalOpenTimeZones(&icuErrorCode);
int32_t length;
while (const UChar* str = icuLib.uenumUnext(uenum, &length, &icuErrorCode))
void tzUpdate()
{
char buffer[256];
SortedObjectsArray<string> currentZones, icuZones;
for (int i = 0; i <= length; ++i)
buffer[i] = (char) str[i];
for (unsigned i = 0; i < FB_NELEM(TIME_ZONE_LIST); ++i)
currentZones.push(TIME_ZONE_LIST[i].asciiName);
icuZones.push(buffer);
Jrd::UnicodeUtil::ConversionICU& icuLib = Jrd::UnicodeUtil::getConversionICU();
UErrorCode icuErrorCode = U_ZERO_ERROR;
UEnumeration* uenum = icuLib.ucalOpenTimeZones(&icuErrorCode);
int32_t length;
while (const UChar* str = icuLib.uenumUnext(uenum, &length, &icuErrorCode))
{
char buffer[256];
for (int i = 0; i <= length; ++i)
buffer[i] = (char) str[i];
icuZones.push(buffer);
}
icuLib.uenumClose(uenum);
for (auto const& zone : currentZones)
{
FB_SIZE_T pos;
if (icuZones.find(zone, pos))
icuZones.remove(pos);
else
printf("--> %s does not exist in ICU.\n", zone.c_str());
}
ObjectsArray<string> newZones;
for (int i = 0; i < FB_NELEM(TIME_ZONE_LIST); ++i)
newZones.push(TIME_ZONE_LIST[i].asciiName);
for (auto const& zone : icuZones)
newZones.push(zone);
printf("// The content of this file is generated with help of macro TZ_UPDATE.\n\n");
int index = 0;
for (auto const& zone : newZones)
{
printf("static const UChar TZSTR_%d[] = {", index);
for (int i = 0; i < zone.length(); ++i)
printf("'%c', ", zone[i]);
printf("'\\0'};\n");
++index;
}
printf("\n");
printf("// Do not change order of items in this array! The index corresponds to a TimeZone ID, which must be fixed!\n");
printf("static const TimeZoneDesc TIME_ZONE_LIST[] = {");
index = 0;
for (auto const& zone : newZones)
{
printf("%s\n\t{\"%s\", TZSTR_%d}", (index == 0 ? "" : ","), zone.c_str(), index);
++index;
}
printf("\n");
printf("};\n\n");
}
icuLib.uenumClose(uenum);
for (auto const& zone : currentZones)
{
FB_SIZE_T pos;
if (icuZones.find(zone, pos))
icuZones.remove(pos);
else
printf("--> %s does not exist in ICU.\n", zone.c_str());
}
ObjectsArray<string> newZones;
for (int i = 0; i < FB_NELEM(TIME_ZONE_LIST); ++i)
newZones.push(TIME_ZONE_LIST[i].asciiName);
for (auto const& zone : icuZones)
newZones.push(zone);
printf("// The content of this file is generated with help of macro TZ_UPDATE.\n\n");
int index = 0;
for (auto const& zone : newZones)
{
printf("static const UChar TZSTR_%d[] = {", index);
for (int i = 0; i < zone.length(); ++i)
printf("'%c', ", zone[i]);
printf("'\\0'};\n");
++index;
}
printf("\n");
printf("// Do not change order of items in this array! The index corresponds to a TimeZone ID, which must be fixed!\n");
printf("static const TimeZoneDesc TIME_ZONE_LIST[] = {");
index = 0;
for (auto const& zone : newZones)
{
printf("%s\n\t{\"%s\", TZSTR_%d}", (index == 0 ? "" : ","), zone.c_str(), index);
++index;
}
printf("\n");
printf("};\n\n");
}
#endif // defined DEV_BUILD && defined TZ_UPDATE
USHORT systemTimeZone;
USHORT systemTimeZone;
private:
GenericMap<Pair<Left<string, USHORT> > > nameIdMap;
};
private:
GenericMap<Pair<Left<string, USHORT> > > nameIdMap;
};
} // namespace
//-------------------------------------
static InitInstance<TimeZoneStartup> timeZoneStartup;
@ -243,6 +249,12 @@ USHORT TimeZoneUtil::getSystemTimeZone()
return timeZoneStartup().systemTimeZone;
}
void TimeZoneUtil::iterateRegions(std::function<void (USHORT, const char*)> func)
{
for (USHORT i = 0; i < FB_NELEM(TIME_ZONE_LIST); ++i)
func(MAX_USHORT - i, TIME_ZONE_LIST[i].asciiName);
}
// Parses a time zone, offset- or region-based.
USHORT TimeZoneUtil::parse(const char* str, unsigned strLen)
{

View File

@ -27,6 +27,7 @@
#ifndef COMMON_TIME_ZONE_UTIL_H
#define COMMON_TIME_ZONE_UTIL_H
#include <functional>
#include "../common/classes/fb_string.h"
#include "../common/cvt.h"
@ -58,6 +59,8 @@ public:
public:
static USHORT getSystemTimeZone();
static void iterateRegions(std::function<void (USHORT, const char*)> func);
static USHORT parse(const char* str, unsigned strLen);
static unsigned format(char* buffer, size_t bufferSize, USHORT timeZone);

View File

@ -698,3 +698,9 @@
const USHORT f_mon_tab_rec_stat_id = 3;
// Relation 50 (RDB$TIME_ZONES)
const USHORT f_tz_id = 0;
const USHORT f_tz_name = 1;

73
src/jrd/TimeZone.cpp Normal file
View File

@ -0,0 +1,73 @@
/*
* 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 Alex Peshkov
* for the Firebird Open Source RDBMS project.
*
* Copyright (c) 2018 Adriano dos Santos Fernandes <adrianosf@gmail.com>
* and all contributors signed below.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*/
#include "firebird.h"
#include "../jrd/TimeZone.h"
#include "../jrd/Record.h"
#include "../jrd/ini.h"
#include "../jrd/tra.h"
#include "gen/ids.h"
using namespace Jrd;
using namespace Firebird;
TimeZoneSnapshot::TimeZoneSnapshot(thread_db* tdbb, MemoryPool& pool)
: SnapshotData(pool)
{
RecordBuffer* buffer = allocBuffer(tdbb, pool, rel_time_zones);
Record* record = buffer->getTempRecord();
record->nullify();
TimeZoneUtil::iterateRegions(
[=]
(USHORT id, const char* name)
{
SINT64 idValue = id;
putField(tdbb, record, DumpField(f_tz_id, VALUE_INTEGER, sizeof(idValue), &idValue));
putField(tdbb, record, DumpField(f_tz_name, VALUE_STRING, static_cast<USHORT>(strlen(name)), name));
buffer->store(record);
}
);
}
TimeZonesTableScan::TimeZonesTableScan(CompilerScratch* csb, const Firebird::string& alias,
StreamType stream, jrd_rel* relation)
: VirtualTableScan(csb, alias, stream, relation)
{
}
const Format* TimeZonesTableScan::getFormat(thread_db* tdbb, jrd_rel* relation) const
{
return tdbb->getTransaction()->getTimeZoneSnapshot(tdbb)->getData(relation)->getFormat();
}
bool TimeZonesTableScan::retrieveRecord(thread_db* tdbb, jrd_rel* relation,
FB_UINT64 position, Record* record) const
{
return tdbb->getTransaction()->getTimeZoneSnapshot(tdbb)->getData(relation)->fetch(position, record);
}

58
src/jrd/TimeZone.h Normal file
View File

@ -0,0 +1,58 @@
/*
* 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 Alex Peshkov
* for the Firebird Open Source RDBMS project.
*
* Copyright (c) 2018 Adriano dos Santos Fernandes <adrianosf@gmail.com>
* and all contributors signed below.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*/
#ifndef JRD_TIME_ZONE_H
#define JRD_TIME_ZONE_H
#include "firebird.h"
#include "../common/classes/fb_string.h"
#include "../jrd/Monitoring.h"
#include "../jrd/recsrc/RecordSource.h"
namespace Jrd {
class thread_db;
class jrd_tra;
class RecordBuffer;
class TimeZoneSnapshot : public SnapshotData
{
public:
TimeZoneSnapshot(thread_db* tdbb, MemoryPool& pool);
};
class TimeZonesTableScan: public VirtualTableScan
{
public:
TimeZonesTableScan(CompilerScratch* csb, const Firebird::string& alias, StreamType stream, jrd_rel* relation);
protected:
const Format* getFormat(thread_db* tdbb, jrd_rel* relation) const override;
bool retrieveRecord(thread_db* tdbb, jrd_rel* relation, FB_UINT64 position, Record* record) const override;
};
} // namespace
#endif // JRD_TIME_ZONE_H

View File

@ -200,3 +200,6 @@
FIELD(fld_idle_timer , nam_idle_timer , dtype_timestamp, TIMESTAMP_SIZE , 0 , NULL , true)
FIELD(fld_stmt_timeout , nam_stmt_timeout , dtype_long , sizeof(SLONG) , 0 , NULL , false)
FIELD(fld_stmt_timer , nam_stmt_timer , dtype_timestamp, TIMESTAMP_SIZE , 0 , NULL , true)
FIELD(fld_tz_id , nam_tz_id , dtype_long , sizeof(SLONG) , 0 , NULL , true)
FIELD(fld_tz_name , nam_tz_name , dtype_text , MAX_SQL_IDENTIFIER_LEN , dsc_text_type_metadata , NULL , true)

View File

@ -417,3 +417,7 @@ NAME("MON$STATEMENT_TIMER", nam_stmt_timer)
NAME("MON$WIRE_COMPRESSED", nam_wire_compressed)
NAME("MON$WIRE_ENCRYPTED", nam_wire_encrypted)
NAME("RDB$TIME_ZONES", nam_time_zones)
NAME("RDB$TIME_ZONE_ID", nam_tz_id)
NAME("RDB$TIME_ZONE_NAME", nam_tz_name)

View File

@ -78,6 +78,7 @@
#include "../jrd/RecordSourceNodes.h"
#include "../jrd/VirtualTable.h"
#include "../jrd/Monitoring.h"
#include "../jrd/TimeZone.h"
#include "../jrd/UserManagement.h"
#include "../common/classes/array.h"
#include "../common/classes/objects_array.h"
@ -2265,6 +2266,10 @@ static RecordSource* gen_retrieval(thread_db* tdbb,
rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) DbCreatorsScan(csb, alias, stream, relation);
break;
case rel_time_zones:
rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) TimeZonesTableScan(csb, alias, stream, relation);
break;
default:
rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) MonitoringTableScan(csb, alias, stream, relation);
break;

View File

@ -696,3 +696,9 @@ RELATION(nam_mon_tab_stats, rel_mon_tab_stats, ODS_12_0, rel_virtual)
FIELD(f_mon_tab_name, nam_mon_tab_name, fld_r_name, 0, ODS_12_0)
FIELD(f_mon_tab_rec_stat_id, nam_mon_rec_stat_id, fld_stat_id, 0, ODS_12_0)
END_RELATION
// Relation 50 (RDB$TIME_ZONES)
RELATION(nam_time_zones, rel_time_zones, ODS_13_0, rel_virtual)
FIELD(f_tz_id, nam_tz_id, fld_tz_id, 0, ODS_13_0)
FIELD(f_tz_name, nam_tz_name, fld_tz_name, 0, ODS_13_0)
END_RELATION

View File

@ -40,6 +40,7 @@
#include "../jrd/rse.h"
#include "../jrd/intl_classes.h"
#include "../common/ThreadStart.h"
#include "../jrd/TimeZone.h"
#include "../jrd/UserManagement.h"
#include "../jrd/blb_proto.h"
#include "../jrd/cch_proto.h"
@ -3495,6 +3496,7 @@ jrd_tra::~jrd_tra()
delete tra_undo_space;
delete tra_user_management;
delete tra_timezone_snapshot;
delete tra_mapping_list;
delete tra_gen_ids;
@ -3538,6 +3540,15 @@ void jrd_tra::setInterface(JTransaction* jt)
}
TimeZoneSnapshot* jrd_tra::getTimeZoneSnapshot(thread_db* tdbb)
{
if (!tra_timezone_snapshot)
tra_timezone_snapshot = FB_NEW_POOL(*tra_pool) TimeZoneSnapshot(tdbb, *tra_pool);
return tra_timezone_snapshot;
}
UserManagement* jrd_tra::getUserManagement()
{
if (!tra_user_management)

View File

@ -62,6 +62,7 @@ class ArrayField;
class Attachment;
class DeferredWork;
class DeferredJob;
class TimeZoneSnapshot;
class UserManagement;
class MappingList;
class DbCreatorsList;
@ -187,6 +188,7 @@ public:
tra_blob_space(NULL),
tra_undo_space(NULL),
tra_undo_records(*p),
tra_timezone_snapshot(NULL),
tra_user_management(NULL),
tra_sec_db_context(NULL),
tra_mapping_list(NULL),
@ -301,6 +303,7 @@ private:
TempSpace* tra_undo_space; // undo log storage
UndoRecordList tra_undo_records; // temporary records used for the undo purposes
TimeZoneSnapshot* tra_timezone_snapshot;
UserManagement* tra_user_management;
SecDbContext* tra_sec_db_context;
MappingList* tra_mapping_list;
@ -368,6 +371,7 @@ public:
void linkToAttachment(Attachment* attachment);
static void tra_abort(const char* reason);
TimeZoneSnapshot* getTimeZoneSnapshot(thread_db* tdbb);
UserManagement* getUserManagement();
SecDbContext* getSecDbContext();
SecDbContext* setSecDbContext(Firebird::IAttachment* att, Firebird::ITransaction* tra);