diff --git a/builds/win32/msvc12/engine.vcxproj b/builds/win32/msvc12/engine.vcxproj
index 992af5e8fd8..a285a9e4e34 100644
--- a/builds/win32/msvc12/engine.vcxproj
+++ b/builds/win32/msvc12/engine.vcxproj
@@ -91,6 +91,7 @@
+
@@ -276,6 +277,7 @@
+
@@ -558,4 +560,4 @@
-
\ No newline at end of file
+
diff --git a/builds/win32/msvc12/engine.vcxproj.filters b/builds/win32/msvc12/engine.vcxproj.filters
index 0491f1a60e4..1926c2b3561 100644
--- a/builds/win32/msvc12/engine.vcxproj.filters
+++ b/builds/win32/msvc12/engine.vcxproj.filters
@@ -288,6 +288,9 @@
JRD files
+
+ JRD files
+
JRD files
@@ -824,6 +827,9 @@
Header files
+
+ Header files
+
Header files
@@ -1088,4 +1094,4 @@
Resource files
-
\ No newline at end of file
+
diff --git a/builds/win32/msvc14/engine.vcxproj b/builds/win32/msvc14/engine.vcxproj
index 9ab9dc33b7e..9364d2cf1a2 100644
--- a/builds/win32/msvc14/engine.vcxproj
+++ b/builds/win32/msvc14/engine.vcxproj
@@ -91,6 +91,7 @@
+
@@ -277,6 +278,7 @@
+
@@ -560,4 +562,4 @@
-
\ No newline at end of file
+
diff --git a/builds/win32/msvc14/engine.vcxproj.filters b/builds/win32/msvc14/engine.vcxproj.filters
index ad92c9a4d95..d412b8e7ac4 100644
--- a/builds/win32/msvc14/engine.vcxproj.filters
+++ b/builds/win32/msvc14/engine.vcxproj.filters
@@ -288,6 +288,9 @@
JRD files
+
+ JRD files
+
JRD files
@@ -827,6 +830,9 @@
Header files
+
+ Header files
+
Header files
@@ -1094,4 +1100,4 @@
Resource files
-
\ No newline at end of file
+
diff --git a/builds/win32/msvc15/engine.vcxproj b/builds/win32/msvc15/engine.vcxproj
index 274670ee27d..afb873e390f 100644
--- a/builds/win32/msvc15/engine.vcxproj
+++ b/builds/win32/msvc15/engine.vcxproj
@@ -91,6 +91,7 @@
+
@@ -277,6 +278,7 @@
+
@@ -561,4 +563,4 @@
-
\ No newline at end of file
+
diff --git a/builds/win32/msvc15/engine.vcxproj.filters b/builds/win32/msvc15/engine.vcxproj.filters
index ad92c9a4d95..d412b8e7ac4 100644
--- a/builds/win32/msvc15/engine.vcxproj.filters
+++ b/builds/win32/msvc15/engine.vcxproj.filters
@@ -288,6 +288,9 @@
JRD files
+
+ JRD files
+
JRD files
@@ -827,6 +830,9 @@
Header files
+
+ Header files
+
Header files
@@ -1094,4 +1100,4 @@
Resource files
-
\ No newline at end of file
+
diff --git a/src/jrd/KeywordsTable.cpp b/src/jrd/KeywordsTable.cpp
new file mode 100644
index 00000000000..397fddde72e
--- /dev/null
+++ b/src/jrd/KeywordsTable.cpp
@@ -0,0 +1,101 @@
+/*
+ * 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 Adriano dos Santos Fernandes
+ * for the Firebird Open Source RDBMS project.
+ *
+ * Copyright (c) 2021 Adriano dos Santos Fernandes
+ * and all contributors signed below.
+ *
+ * All Rights Reserved.
+ * Contributor(s): ______________________________________.
+ */
+
+#include "../jrd/KeywordsTable.h"
+#include "../jrd/ini.h"
+#include "../jrd/ids.h"
+#include "../common/keywords.h"
+
+using namespace Jrd;
+using namespace Firebird;
+
+
+RecordBuffer* KeywordsTable::getRecords(thread_db* tdbb, jrd_rel* relation)
+{
+ fb_assert(relation);
+ fb_assert(relation->rel_id == rel_keywords);
+
+ auto recordBuffer = getData(relation);
+ if (recordBuffer)
+ return recordBuffer;
+
+ recordBuffer = allocBuffer(tdbb, *tdbb->getDefaultPool(), relation->rel_id);
+
+ const auto record = recordBuffer->getTempRecord();
+
+ for (const auto* token = keywordGetTokens(); token->tok_string; ++token)
+ {
+ if (isalpha(token->tok_string[0]))
+ {
+ record->nullify();
+
+ putField(tdbb, record,
+ DumpField(f_keyword_name, VALUE_STRING, strlen(token->tok_string), token->tok_string));
+
+ const bool reserved = !token->nonReserved;
+ putField(tdbb, record, DumpField(f_keyword_reserved, VALUE_BOOLEAN, 1, &reserved));
+
+ recordBuffer->store(record);
+ }
+ }
+
+ return recordBuffer;
+}
+
+
+//--------------------------------------
+
+
+void KeywordsTableScan::close(thread_db* tdbb) const
+{
+ const auto request = tdbb->getRequest();
+ const auto impure = request->getImpure(impureOffset);
+
+ delete impure->table;
+ impure->table = nullptr;
+
+ VirtualTableScan::close(tdbb);
+}
+
+const Format* KeywordsTableScan::getFormat(thread_db* tdbb, jrd_rel* relation) const
+{
+ const auto records = getRecords(tdbb, relation);
+ return records->getFormat();
+}
+
+bool KeywordsTableScan::retrieveRecord(thread_db* tdbb, jrd_rel* relation,
+ FB_UINT64 position, Record* record) const
+{
+ const auto records = getRecords(tdbb, relation);
+ return records->fetch(position, record);
+}
+
+RecordBuffer* KeywordsTableScan::getRecords(thread_db* tdbb, jrd_rel* relation) const
+{
+ const auto request = tdbb->getRequest();
+ const auto impure = request->getImpure(impureOffset);
+
+ if (!impure->table)
+ impure->table = FB_NEW_POOL(*tdbb->getDefaultPool()) KeywordsTable(*tdbb->getDefaultPool());
+
+ return impure->table->getRecords(tdbb, relation);
+}
diff --git a/src/jrd/KeywordsTable.h b/src/jrd/KeywordsTable.h
new file mode 100644
index 00000000000..06ab0e094c3
--- /dev/null
+++ b/src/jrd/KeywordsTable.h
@@ -0,0 +1,79 @@
+/*
+ * 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 Adriano dos Santos Fernandes
+ * for the Firebird Open Source RDBMS project.
+ *
+ * Copyright (c) 2021 Adriano dos Santos Fernandes
+ * and all contributors signed below.
+ *
+ * All Rights Reserved.
+ * Contributor(s): ______________________________________.
+ */
+
+#ifndef JRD_KEYWORDS_TABLE_H
+#define JRD_KEYWORDS_TABLE_H
+
+#include "firebird.h"
+#include "../common/classes/fb_string.h"
+#include "../jrd/Monitoring.h"
+#include "../jrd/recsrc/RecordSource.h"
+
+
+namespace Jrd
+{
+
+class KeywordsTable : public SnapshotData
+{
+public:
+ KeywordsTable(MemoryPool& pool)
+ : SnapshotData(pool)
+ {
+ }
+
+public:
+ RecordBuffer* getRecords(thread_db* tdbb, jrd_rel* relation);
+};
+
+
+class KeywordsTableScan : public VirtualTableScan
+{
+public:
+ KeywordsTableScan(CompilerScratch* csb, const Firebird::string& alias,
+ StreamType stream, jrd_rel* relation)
+ : VirtualTableScan(csb, alias, stream, relation)
+ {
+ impureOffset = csb->allocImpure();
+ }
+
+ void close(thread_db* tdbb) const override;
+
+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;
+
+private:
+ struct Impure
+ {
+ KeywordsTable* table;
+ };
+
+ RecordBuffer* getRecords(thread_db* tdbb, jrd_rel* relation) const;
+
+ ULONG impureOffset;
+};
+
+} // namespace Jrd
+
+#endif // JRD_KEYWORDS_TABLE_H
diff --git a/src/jrd/fields.h b/src/jrd/fields.h
index 64dd66311cb..6ffdaf9d6a2 100644
--- a/src/jrd/fields.h
+++ b/src/jrd/fields.h
@@ -220,3 +220,6 @@
FIELD(fld_cfg_is_set , nam_cfg_is_set , dtype_boolean , 1 , 0 , NULL , false)
FIELD(fld_repl_mode , nam_repl_mode , dtype_short , sizeof(SSHORT) , 0 , NULL , true)
+
+ FIELD(fld_keyword_name , nam_keyword_name , dtype_varying , METADATA_IDENTIFIER_CHAR_LEN, dsc_text_type_ascii , NULL , false)
+ FIELD(fld_keyword_reserved, nam_keyword_reserved, dtype_boolean, 1 , 0 , NULL , false)
diff --git a/src/jrd/names.h b/src/jrd/names.h
index 60ef6585aca..ece88e406d1 100644
--- a/src/jrd/names.h
+++ b/src/jrd/names.h
@@ -453,3 +453,7 @@ NAME("RDB$CONFIG_IS_SET", nam_cfg_is_set)
NAME("RDB$CONFIG_SOURCE", nam_cfg_source)
NAME("MON$SESSION_TIMEZONE", nam_mon_session_tz)
+
+NAME("RDB$KEYWORDS", nam_keywords)
+NAME("RDB$KEYWORD_NAME", nam_keyword_name)
+NAME("RDB$KEYWORD_RESERVED", nam_keyword_reserved)
diff --git a/src/jrd/opt.cpp b/src/jrd/opt.cpp
index cdf7e81e100..de803055a74 100644
--- a/src/jrd/opt.cpp
+++ b/src/jrd/opt.cpp
@@ -75,6 +75,7 @@
#include "../jrd/par_proto.h"
#include "../yvalve/gds_proto.h"
#include "../jrd/DataTypeUtil.h"
+#include "../jrd/KeywordsTable.h"
#include "../jrd/RecordSourceNodes.h"
#include "../jrd/VirtualTable.h"
#include "../jrd/Monitoring.h"
@@ -2316,6 +2317,10 @@ static RecordSource* gen_retrieval(thread_db* tdbb,
rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) ConfigTableScan(csb, alias, stream, relation);
break;
+ case rel_keywords:
+ rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) KeywordsTableScan(csb, alias, stream, relation);
+ break;
+
default:
rsb = FB_NEW_POOL(*tdbb->getDefaultPool()) MonitoringTableScan(csb, alias, stream, relation);
break;
diff --git a/src/jrd/relations.h b/src/jrd/relations.h
index dc677f41354..2e00e9c4885 100644
--- a/src/jrd/relations.h
+++ b/src/jrd/relations.h
@@ -735,3 +735,9 @@ RELATION(nam_config, rel_config, ODS_13_0, rel_virtual)
FIELD(f_cfg_is_set, nam_cfg_is_set, fld_cfg_is_set, 0, ODS_13_0)
FIELD(f_cfg_source, nam_cfg_source, fld_file_name2, 0, ODS_13_0)
END_RELATION
+
+// Relation 54 (RDB$KEYWORDS)
+RELATION(nam_keywords, rel_keywords, ODS_13_1, rel_virtual)
+ FIELD(f_keyword_name, nam_keyword_name, fld_keyword_name, 0, ODS_13_1)
+ FIELD(f_keyword_reserved, nam_keyword_reserved, fld_keyword_reserved, 0, ODS_13_1)
+END_RELATION