mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 16:03:03 +01:00
* Feature #7675 - EXPLAIN statement and RDB$SQL package. * Change ObjectsArray::back() to match STL semantics and add front() method. * Fix indentation problem. * Add OBJECT_TYPE column. * Add CARDINALITY column. * Add conversion from bid to ISC_QUAD. * Replace fb_assert by static_assert. * Change ACCESS_PATH output parameter to blob. * Improve docs.
This commit is contained in:
parent
fc2fe39d3a
commit
39b019574a
@ -87,7 +87,8 @@ AllObjects += $(Profiler_Objects)
|
||||
|
||||
# Engine
|
||||
Engine_Objects:= $(call dirObjects,jrd) $(call dirObjects,dsql) $(call dirObjects,jrd/extds) \
|
||||
$(call dirObjects,jrd/optimizer) $(call dirObjects,jrd/recsrc) $(call dirObjects,jrd/replication) $(call dirObjects,jrd/trace) \
|
||||
$(call dirObjects,jrd/optimizer) $(call dirObjects,jrd/recsrc) $(call dirObjects,jrd/replication) \
|
||||
$(call dirObjects,jrd/sys-packages) $(call dirObjects,jrd/trace) \
|
||||
$(call makeObjects,lock,lock.cpp)
|
||||
|
||||
Engine_Test_Objects:= $(call dirObjects,jrd/tests)
|
||||
|
@ -161,6 +161,7 @@
|
||||
<ClCompile Include="..\..\..\src\jrd\sqz.cpp" />
|
||||
<ClCompile Include="..\..\..\src\jrd\Statement.cpp" />
|
||||
<ClCompile Include="..\..\..\src\jrd\svc.cpp" />
|
||||
<ClCompile Include="..\..\..\src\jrd\sys-packages\SqlPackage.cpp" />
|
||||
<ClCompile Include="..\..\..\src\jrd\SysFunction.cpp" />
|
||||
<ClCompile Include="..\..\..\src\jrd\SystemPackages.cpp" />
|
||||
<ClCompile Include="..\..\..\src\jrd\TempSpace.cpp" />
|
||||
@ -345,6 +346,7 @@
|
||||
<ClInclude Include="..\..\..\src\jrd\status.h" />
|
||||
<ClInclude Include="..\..\..\src\jrd\svc.h" />
|
||||
<ClInclude Include="..\..\..\src\jrd\svc_undoc.h" />
|
||||
<ClInclude Include="..\..\..\src\jrd\sys-packages\SqlPackage.h" />
|
||||
<ClInclude Include="..\..\..\src\jrd\SysFunction.h" />
|
||||
<ClInclude Include="..\..\..\src\jrd\SystemPackages.h" />
|
||||
<ClInclude Include="..\..\..\src\jrd\TempSpace.h" />
|
||||
|
@ -378,6 +378,9 @@
|
||||
<ClCompile Include="..\..\..\src\jrd\svc.cpp">
|
||||
<Filter>JRD files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\src\jrd\sys-packages\SqlPackage.cpp">
|
||||
<Filter>JRD files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\src\jrd\SysFunction.cpp">
|
||||
<Filter>JRD files</Filter>
|
||||
</ClCompile>
|
||||
@ -1073,6 +1076,9 @@
|
||||
<ClInclude Include="..\..\..\src\dsql\DsqlBatch.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\src\jrd\sys-packages\SqlPackage.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\src\jrd\SystemPackages.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
|
@ -324,3 +324,32 @@ RDB$RELATIONS | 59| | | |
|
||||
|
||||
-- turn per-table stats off, using shortened name
|
||||
SQL> SET PER_TAB OFF;
|
||||
|
||||
|
||||
|
||||
Isql enhancements in Firebird v6.
|
||||
---------------------------------
|
||||
|
||||
EXPLAIN statement.
|
||||
|
||||
Author: Adriano dos Santos Fernandes
|
||||
|
||||
A new ISQL statement was created to easily show a query plan without execute it.
|
||||
|
||||
Note: If SET STATS is ON, stats are still shown.
|
||||
|
||||
Examples:
|
||||
|
||||
SQL> explain select * from employees where id = ?;
|
||||
|
||||
SQL> set term !;
|
||||
SQL>
|
||||
SQL> explain
|
||||
CON> execute block
|
||||
CON> as
|
||||
CON> declare id integer;
|
||||
CON> begin
|
||||
CON> select id from employees where id = ? into id;
|
||||
CON> end!
|
||||
SQL>
|
||||
SQL> set term ;!
|
||||
|
46
doc/sql.extensions/README.sql_package.md
Normal file
46
doc/sql.extensions/README.sql_package.md
Normal file
@ -0,0 +1,46 @@
|
||||
# RDB$SQL package (FB 6.0)
|
||||
|
||||
`RDB$SQL` is a package with utility routines to work with dynamic SQL.
|
||||
|
||||
## Procedure `EXPLAIN`
|
||||
|
||||
`RDB$SQL.EXPLAIN` returns tabular information of a query's plan, without execute the query.
|
||||
|
||||
Since `SQL` text generally is multi-line string and have quotes, you may use `<alternate string literal>`
|
||||
(strings prefixed by `Q`) as a way to make escape easy.
|
||||
|
||||
Input parameters:
|
||||
- `SQL` type `BLOB SUB_TYPE TEXT CHARACTER SET UTF8 NOT NULL` - query statement
|
||||
|
||||
Output parameters:
|
||||
- `PLAN_LINE` type `INTEGER NOT NULL` - plan's line order
|
||||
- `RECORD_SOURCE_ID` type `BIGINT NOT NULL` - record source id
|
||||
- `PARENT_RECORD_SOURCE_ID` type `BIGINT` - parent record source id
|
||||
- `LEVEL` type `INTEGER NOT NULL` - indentation level (may have gaps in relation to parent's level)
|
||||
- `PACKAGE_NAME` type `RDB$PACKAGE_NAME` - package name of a stored procedure
|
||||
- `OBJECT_NAME` type `RDB$RELATION_NAME` - object (table, procedure) name
|
||||
- `ALIAS` type `RDB$RELATION_NAME` - alias name
|
||||
- `RECORD_LENGTH` type `INTEGER` - record length for the record source
|
||||
- `KEY_LENGTH` type `INTEGER` - key length for the record source
|
||||
- `ACCESS_PATH` type `VARCHAR(255) CHARACTER SET UTF8 NOT NULL` - friendly plan description
|
||||
|
||||
```
|
||||
select *
|
||||
from rdb$sql.explain('select * from employees where id = ?');
|
||||
```
|
||||
|
||||
```
|
||||
select *
|
||||
from rdb$sql.explain(q'{
|
||||
select *
|
||||
from (
|
||||
select name from employees
|
||||
union all
|
||||
select name from customers
|
||||
)
|
||||
where name = ?
|
||||
}');
|
||||
```
|
||||
|
||||
# Authors
|
||||
- Adriano dos Santos Fernandes
|
@ -306,10 +306,16 @@ namespace Firebird
|
||||
return iterator(this, getCount());
|
||||
}
|
||||
|
||||
iterator back()
|
||||
T& front()
|
||||
{
|
||||
fb_assert(getCount() > 0);
|
||||
return iterator(this, getCount() - 1);
|
||||
return *begin();
|
||||
}
|
||||
|
||||
T& back()
|
||||
{
|
||||
fb_assert(getCount() > 0);
|
||||
return *iterator(this, getCount() - 1);
|
||||
}
|
||||
|
||||
const_iterator begin() const
|
||||
|
@ -1708,16 +1708,16 @@ UnicodeUtil::Utf16Collation* UnicodeUtil::Utf16Collation::create(
|
||||
++secondKeyDataIt;
|
||||
}
|
||||
|
||||
unsigned backSize = commonKeys.back()->getCount();
|
||||
unsigned backSize = commonKeys.back().getCount();
|
||||
|
||||
if (common > backSize)
|
||||
commonKeys.back()->append(secondKeyIt->begin() + backSize, common - backSize);
|
||||
commonKeys.back().append(secondKeyIt->begin() + backSize, common - backSize);
|
||||
else if (common < backSize)
|
||||
{
|
||||
if (common == 0)
|
||||
commonKeys.push(*secondKeyIt);
|
||||
else
|
||||
commonKeys.back()->resize(common);
|
||||
commonKeys.back().resize(common);
|
||||
}
|
||||
|
||||
if (++secondKeyIt != keySet.end())
|
||||
|
@ -2505,7 +2505,7 @@ column_constraint_def($addColumnClause)
|
||||
: constraint_name_opt column_constraint($addColumnClause)
|
||||
{
|
||||
if ($1)
|
||||
$addColumnClause->constraints.back()->name = *$1;
|
||||
$addColumnClause->constraints.back().name = *$1;
|
||||
}
|
||||
;
|
||||
|
||||
|
@ -201,3 +201,4 @@ FB_IMPL_MSG_SYMBOL(ISQL, 201, NO_PUBLICATION, "There is no publication @1 in thi
|
||||
FB_IMPL_MSG_SYMBOL(ISQL, 202, NO_PUBLICATIONS, "There is no publications in this database")
|
||||
FB_IMPL_MSG_SYMBOL(ISQL, 203, MSG_PUBLICATIONS, "Publications:")
|
||||
FB_IMPL_MSG_SYMBOL(ISQL, 204, MSG_PROCEDURES, "Procedures:")
|
||||
FB_IMPL_MSG_SYMBOL(ISQL, 205, HLP_EXPLAIN, "EXPLAIN -- explain a query access plan")
|
||||
|
@ -492,6 +492,7 @@ static processing_state get_statement(string&, const TEXT*);
|
||||
static bool get_numeric(const UCHAR*, USHORT, SSHORT*, SINT64*);
|
||||
static void print_set(const char* str, bool v);
|
||||
static processing_state print_sets();
|
||||
static processing_state explain(const TEXT*);
|
||||
static processing_state help(const TEXT*);
|
||||
static bool isyesno(const TEXT*);
|
||||
static processing_state newdb(TEXT*, const TEXT*, const TEXT*, int, const TEXT*, bool);
|
||||
@ -587,6 +588,7 @@ public:
|
||||
KeepTranParams = true;
|
||||
TranParams->assign(DEFAULT_DML_TRANS_SQL);
|
||||
PerTableStats = false;
|
||||
ExplainCommand = false;
|
||||
}
|
||||
|
||||
ColList global_Cols;
|
||||
@ -611,6 +613,7 @@ public:
|
||||
SCHAR ISQL_charset[MAXCHARSET_SIZE];
|
||||
bool KeepTranParams;
|
||||
bool PerTableStats;
|
||||
bool ExplainCommand;
|
||||
};
|
||||
|
||||
static SetValues setValues;
|
||||
@ -5009,7 +5012,7 @@ static processing_state frontend(const TEXT* statement)
|
||||
{
|
||||
show, add, copy,
|
||||
blobview, output, shell, set, create, drop, connect,
|
||||
edit, input, quit, exit, help,
|
||||
edit, input, quit, exit, explain, help,
|
||||
#ifdef DEV_BUILD
|
||||
passthrough,
|
||||
#endif
|
||||
@ -5039,6 +5042,7 @@ static processing_state frontend(const TEXT* statement)
|
||||
{FrontOptions::input, "INPUT", 2},
|
||||
{FrontOptions::quit, "QUIT", 0},
|
||||
{FrontOptions::exit, "EXIT", 0},
|
||||
{FrontOptions::explain, "EXPLAIN", 0},
|
||||
{FrontOptions::help, "?", 0},
|
||||
{FrontOptions::help, "HELP", 0}
|
||||
#ifdef DEV_BUILD
|
||||
@ -5236,6 +5240,10 @@ static processing_state frontend(const TEXT* statement)
|
||||
ret = EXIT;
|
||||
break;
|
||||
|
||||
case FrontOptions::explain:
|
||||
ret = explain(cmd + 7);
|
||||
break;
|
||||
|
||||
case FrontOptions::help:
|
||||
ret = help(parms[1]);
|
||||
break;
|
||||
@ -6501,6 +6509,20 @@ static processing_state print_sets()
|
||||
}
|
||||
|
||||
|
||||
static processing_state explain(const TEXT* command)
|
||||
{
|
||||
Firebird::AutoSetRestore autoExplainCommand(&setValues.ExplainCommand, true);
|
||||
Firebird::AutoSetRestore autoPlanonly(&setValues.Planonly, true);
|
||||
Firebird::AutoSetRestore autoPlan(&setValues.Plan, true);
|
||||
Firebird::AutoSetRestore autoExplainPlan(&setValues.ExplainPlan, true);
|
||||
Firebird::AutoSetRestore autoSqldaDisplay(&setValues.Sqlda_display, false);
|
||||
|
||||
process_statement(command);
|
||||
|
||||
return SKIP;
|
||||
}
|
||||
|
||||
|
||||
static processing_state help(const TEXT* what)
|
||||
{
|
||||
/**************************************
|
||||
@ -6523,6 +6545,7 @@ static processing_state help(const TEXT* what)
|
||||
HLP_BLOBED, // BLOBVIEW <blobid> -- view BLOB in text editor
|
||||
HLP_EDIT, // EDIT [<filename>] -- edit SQL script file and execute
|
||||
HLP_EDIT2, // EDIT -- edit current command buffer and execute
|
||||
HLP_EXPLAIN, // EXPLAIN -- explain a query access plan
|
||||
HLP_HELP, // HELP -- display this menu
|
||||
HLP_INPUT, // INput <filename> -- take input from the named SQL file
|
||||
HLP_OUTPUT, // OUTput [<filename>] -- write output to named file
|
||||
@ -9121,10 +9144,18 @@ static processing_state process_statement(const TEXT* str2)
|
||||
statement_type != isc_info_sql_stmt_set_generator)
|
||||
{
|
||||
process_plan();
|
||||
if (setValues.Planonly && !is_selectable)
|
||||
|
||||
if (setValues.ExplainCommand)
|
||||
{
|
||||
return ret; // do not execute
|
||||
if (setValues.Stats && (print_performance(perf_before) == ps_ERR))
|
||||
ret = ps_ERR;
|
||||
|
||||
if (setValues.PerTableStats)
|
||||
perTableStats->getStats(DB, false);
|
||||
}
|
||||
|
||||
if (setValues.Planonly && (!is_selectable || setValues.ExplainCommand))
|
||||
return ret; // do not execute
|
||||
}
|
||||
|
||||
if (setValues.ExecPathDisplay[0])
|
||||
|
@ -498,80 +498,31 @@ void ProfilerManager::prepareRecSource(thread_db* tdbb, Request* request, const
|
||||
|
||||
fb_assert(profileStatement->definedCursors.exist(recordSource->getCursorId()));
|
||||
|
||||
struct PlanItem : PermanentStorage
|
||||
{
|
||||
explicit PlanItem(MemoryPool& p)
|
||||
: PermanentStorage(p)
|
||||
{
|
||||
}
|
||||
PlanEntry rootEntry;
|
||||
recordSource->getPlan(tdbb, rootEntry, 0, true);
|
||||
|
||||
const AccessPath* recordSource = nullptr;
|
||||
const AccessPath* parentRecordSource = nullptr;
|
||||
string accessPath{getPool()};
|
||||
unsigned level = 0;
|
||||
};
|
||||
|
||||
ObjectsArray<PlanItem> planItems;
|
||||
planItems.add().recordSource = recordSource;
|
||||
|
||||
for (unsigned pos = 0; pos < planItems.getCount(); ++pos)
|
||||
{
|
||||
auto& planItem = planItems[pos];
|
||||
const auto thisRsb = planItem.recordSource;
|
||||
|
||||
string& accessPath = planItem.accessPath;
|
||||
thisRsb->print(tdbb, accessPath, true, 0, false);
|
||||
|
||||
constexpr auto INDENT_MARKER = "\n ";
|
||||
constexpr unsigned INDENT_COUNT = 4;
|
||||
|
||||
if (accessPath.find(INDENT_MARKER) == 0)
|
||||
{
|
||||
unsigned pos = 0;
|
||||
|
||||
do {
|
||||
accessPath.erase(pos + 1, 4);
|
||||
} while ((pos = accessPath.find(INDENT_MARKER, pos + 1)) != string::npos);
|
||||
}
|
||||
|
||||
if (accessPath.hasData() && accessPath[0] == '\n')
|
||||
accessPath.erase(0, 1);
|
||||
|
||||
Array<const RecordSource*> children;
|
||||
thisRsb->getChildren(children);
|
||||
|
||||
unsigned level = planItem.level;
|
||||
|
||||
if (const auto lastLinePos = accessPath.find_last_of('\n'); lastLinePos != string::npos)
|
||||
level += (accessPath.find_first_not_of(' ', lastLinePos + 1) - lastLinePos + 1) / INDENT_COUNT;
|
||||
|
||||
unsigned childPos = pos;
|
||||
|
||||
for (const auto child : children)
|
||||
{
|
||||
auto& inserted = planItems.insert(++childPos);
|
||||
inserted.recordSource = child;
|
||||
inserted.parentRecordSource = thisRsb;
|
||||
inserted.level = level + 1;
|
||||
}
|
||||
}
|
||||
Array<NonPooledPair<const PlanEntry*, const PlanEntry*>> flatPlan;
|
||||
rootEntry.asFlatList(flatPlan);
|
||||
|
||||
NonPooledMap<ULONG, ULONG> idSequenceMap;
|
||||
auto sequencePtr = profileStatement->cursorNextSequence.getOrPut(recordSource->getCursorId());
|
||||
|
||||
for (const auto& planItem : planItems)
|
||||
for (const auto& [planEntry, parentPlanEntry] : flatPlan)
|
||||
{
|
||||
const auto cursorId = planItem.recordSource->getCursorId();
|
||||
const auto recSourceId = planItem.recordSource->getRecSourceId();
|
||||
const auto cursorId = planEntry->accessPath->getCursorId();
|
||||
const auto recSourceId = planEntry->accessPath->getRecSourceId();
|
||||
idSequenceMap.put(recSourceId, ++*sequencePtr);
|
||||
|
||||
ULONG parentSequence = 0;
|
||||
|
||||
if (planItem.parentRecordSource)
|
||||
parentSequence = *idSequenceMap.get(planItem.parentRecordSource->getRecSourceId());
|
||||
if (parentPlanEntry)
|
||||
parentSequence = *idSequenceMap.get(parentPlanEntry->accessPath->getRecSourceId());
|
||||
|
||||
string accessPath;
|
||||
planEntry->getDescriptionAsString(accessPath);
|
||||
|
||||
currentSession->pluginSession->defineRecordSource(profileStatement->id, cursorId,
|
||||
*sequencePtr, planItem.level, planItem.accessPath.c_str(), parentSequence);
|
||||
*sequencePtr, planEntry->level, accessPath.c_str(), parentSequence);
|
||||
|
||||
profileStatement->recSourceSequence.put(recSourceId, *sequencePtr);
|
||||
}
|
||||
|
@ -239,51 +239,33 @@ struct bid
|
||||
|
||||
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);
|
||||
@ -291,19 +273,18 @@ struct bid
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
operator ISC_QUAD() const
|
||||
{
|
||||
return {ISC_LONG(bid_quad.bid_quad_high), bid_quad.bid_quad_low};
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@ -316,6 +297,8 @@ struct bid
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(bid) == 8); // make sure that compiler packed structure like we wanted
|
||||
|
||||
} // namespace Jrd
|
||||
|
||||
|
||||
|
@ -275,6 +275,11 @@ public:
|
||||
bool isVirtual() const;
|
||||
bool isView() const;
|
||||
|
||||
ObjectType getObjectType() const
|
||||
{
|
||||
return isView() ? obj_view : obj_relation;
|
||||
}
|
||||
|
||||
bool isReplicating(thread_db* tdbb);
|
||||
|
||||
// global temporary relations attributes
|
||||
|
@ -730,6 +730,15 @@ string Statement::getPlan(thread_db* tdbb, bool detailed) const
|
||||
return plan;
|
||||
}
|
||||
|
||||
void Statement::getPlan(thread_db* tdbb, PlanEntry& planEntry) const
|
||||
{
|
||||
planEntry.className = "Statement";
|
||||
planEntry.level = 0;
|
||||
|
||||
for (const auto select : fors)
|
||||
select->getPlan(tdbb, planEntry.children.add(), 0, true);
|
||||
}
|
||||
|
||||
// Check that we have enough rights to access all resources this list of triggers touches.
|
||||
void Statement::verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation,
|
||||
TrigVector* triggers, MetaName userName)
|
||||
|
@ -28,6 +28,8 @@
|
||||
|
||||
namespace Jrd {
|
||||
|
||||
class PlanEntry;
|
||||
|
||||
// Compiled statement.
|
||||
class Statement : public pool_alloc<type_req>
|
||||
{
|
||||
@ -78,6 +80,7 @@ public:
|
||||
void release(thread_db* tdbb);
|
||||
|
||||
Firebird::string getPlan(thread_db* tdbb, bool detailed) const;
|
||||
void getPlan(thread_db* tdbb, PlanEntry& planEntry) const;
|
||||
|
||||
private:
|
||||
static void verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation, TrigVector* triggers,
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "../jrd/BlobUtil.h"
|
||||
#include "../jrd/TimeZone.h"
|
||||
#include "../jrd/ProfilerManager.h"
|
||||
#include "../jrd/sys-packages/SqlPackage.h"
|
||||
|
||||
using namespace Firebird;
|
||||
using namespace Jrd;
|
||||
@ -40,6 +41,7 @@ namespace
|
||||
list->add(TimeZonePackage(pool));
|
||||
list->add(ProfilerPackage(pool));
|
||||
list->add(BlobUtilPackage(pool));
|
||||
list->add(SqlPackage(pool));
|
||||
}
|
||||
|
||||
static InitInstance<SystemPackagesInit> INSTANCE;
|
||||
|
@ -146,6 +146,7 @@ const USHORT ODS_11_2 = ENCODE_ODS(ODS_VERSION11, 2);
|
||||
const USHORT ODS_12_0 = ENCODE_ODS(ODS_VERSION12, 0);
|
||||
const USHORT ODS_13_0 = ENCODE_ODS(ODS_VERSION13, 0);
|
||||
const USHORT ODS_13_1 = ENCODE_ODS(ODS_VERSION13, 1);
|
||||
const USHORT ODS_13_2 = ODS_13_1; //// FIXME: Review - ODS for v6.
|
||||
|
||||
const USHORT ODS_FIREBIRD_FLAG = 0x8000;
|
||||
|
||||
|
@ -1970,7 +1970,7 @@ unsigned Optimizer::distributeEqualities(BoolExprNodeStack& orgStack, unsigned b
|
||||
ValueExprNodeStack& s = classes.add();
|
||||
s.push(node1);
|
||||
s.push(node2);
|
||||
eq_class = classes.back();
|
||||
eq_class = --classes.end();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -375,21 +375,23 @@ AggregatedStream::AggregatedStream(thread_db* tdbb, CompilerScratch* csb, Stream
|
||||
fb_assert(map);
|
||||
}
|
||||
|
||||
void AggregatedStream::getChildren(Array<const RecordSource*>& children) const
|
||||
void AggregatedStream::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
children.add(m_next);
|
||||
m_next->getLegacyPlan(tdbb, plan, level);
|
||||
}
|
||||
|
||||
void AggregatedStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
||||
void AggregatedStream::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
if (detailed)
|
||||
{
|
||||
plan += printIndent(++level) + "Aggregate";
|
||||
printOptInfo(plan);
|
||||
}
|
||||
planEntry.className = "AggregatedStream";
|
||||
|
||||
planEntry.description.add() = "Aggregate";
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
if (recurse)
|
||||
m_next->print(tdbb, plan, detailed, level, recurse);
|
||||
{
|
||||
++level;
|
||||
m_next->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||
}
|
||||
}
|
||||
|
||||
bool AggregatedStream::internalGetRecord(thread_db* tdbb) const
|
||||
|
@ -122,32 +122,32 @@ bool BitmapTableScan::internalGetRecord(thread_db* tdbb) const
|
||||
return false;
|
||||
}
|
||||
|
||||
void BitmapTableScan::getChildren(Array<const RecordSource*>& children) const
|
||||
void BitmapTableScan::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
if (!level)
|
||||
plan += "(";
|
||||
|
||||
plan += printName(tdbb, m_alias, false) + " INDEX (";
|
||||
string indices;
|
||||
printInversion(tdbb, m_inversion, indices, false, level);
|
||||
plan += indices + ")";
|
||||
|
||||
if (!level)
|
||||
plan += ")";
|
||||
}
|
||||
|
||||
void BitmapTableScan::print(thread_db* tdbb, string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const
|
||||
void BitmapTableScan::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
if (detailed)
|
||||
{
|
||||
plan += printIndent(++level) + "Table " +
|
||||
printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Access By ID";
|
||||
planEntry.className = "BitmapTableScan";
|
||||
|
||||
printOptInfo(plan);
|
||||
printInversion(tdbb, m_inversion, plan, true, level);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!level)
|
||||
plan += "(";
|
||||
planEntry.description.add() = "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Access By ID";
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
plan += printName(tdbb, m_alias, false) + " INDEX (";
|
||||
string indices;
|
||||
printInversion(tdbb, m_inversion, indices, false, level);
|
||||
plan += indices + ")";
|
||||
printInversion(tdbb, m_inversion, planEntry.description, true);
|
||||
|
||||
if (!level)
|
||||
plan += ")";
|
||||
}
|
||||
planEntry.objectType = m_relation->getObjectType();
|
||||
planEntry.objectName = m_relation->rel_name;
|
||||
|
||||
if (m_alias.hasData() && m_relation->rel_name != m_alias)
|
||||
planEntry.alias = m_alias;
|
||||
}
|
||||
|
@ -314,24 +314,25 @@ WriteLockResult BufferedStream::lockRecord(thread_db* tdbb, bool skipLocked) con
|
||||
return m_next->lockRecord(tdbb, skipLocked);
|
||||
}
|
||||
|
||||
void BufferedStream::getChildren(Array<const RecordSource*>& children) const
|
||||
void BufferedStream::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
children.add(m_next);
|
||||
m_next->getLegacyPlan(tdbb, plan, level);
|
||||
}
|
||||
|
||||
void BufferedStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
||||
void BufferedStream::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
if (detailed)
|
||||
{
|
||||
string extras;
|
||||
extras.printf(" (record length: %" ULONGFORMAT")", m_format->fmt_length);
|
||||
planEntry.className = "BufferedStream";
|
||||
|
||||
plan += printIndent(++level) + "Record Buffer" + extras;
|
||||
printOptInfo(plan);
|
||||
}
|
||||
string extras;
|
||||
extras.printf(" (record length: %" ULONGFORMAT")", m_format->fmt_length);
|
||||
|
||||
planEntry.description.add() = "Record Buffer" + extras;
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
planEntry.recordLength = m_format->fmt_length;
|
||||
|
||||
if (recurse)
|
||||
m_next->print(tdbb, plan, detailed, level, recurse);
|
||||
m_next->getPlan(tdbb, planEntry.children.add(), ++level, recurse);
|
||||
}
|
||||
|
||||
void BufferedStream::markRecursive()
|
||||
|
@ -114,38 +114,33 @@ WriteLockResult ConditionalStream::lockRecord(thread_db* tdbb, bool skipLocked)
|
||||
return impure->irsb_next->lockRecord(tdbb, skipLocked);
|
||||
}
|
||||
|
||||
void ConditionalStream::getChildren(Array<const RecordSource*>& children) const
|
||||
void ConditionalStream::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
children.add(m_first);
|
||||
children.add(m_second);
|
||||
if (!level)
|
||||
plan += "(";
|
||||
|
||||
m_first->getLegacyPlan(tdbb, plan, level + 1);
|
||||
|
||||
plan += ", ";
|
||||
|
||||
m_second->getLegacyPlan(tdbb, plan, level + 1);
|
||||
|
||||
if (!level)
|
||||
plan += ")";
|
||||
}
|
||||
|
||||
void ConditionalStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
||||
void ConditionalStream::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
if (detailed)
|
||||
planEntry.className = "ConditionalStream";
|
||||
|
||||
planEntry.description.add() = "Condition";
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
if (recurse)
|
||||
{
|
||||
plan += printIndent(++level) + "Condition";
|
||||
printOptInfo(plan);
|
||||
|
||||
if (recurse)
|
||||
{
|
||||
m_first->print(tdbb, plan, true, level, recurse);
|
||||
m_second->print(tdbb, plan, true, level, recurse);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!level)
|
||||
plan += "(";
|
||||
|
||||
m_first->print(tdbb, plan, false, level + 1, recurse);
|
||||
|
||||
plan += ", ";
|
||||
|
||||
m_second->print(tdbb, plan, false, level + 1, recurse);
|
||||
|
||||
if (!level)
|
||||
plan += ")";
|
||||
++level;
|
||||
m_first->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||
m_second->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,48 +107,42 @@ void Select::initializeInvariants(Request* request) const
|
||||
}
|
||||
}
|
||||
|
||||
void Select::print(thread_db* tdbb, Firebird::string& plan, bool detailed, unsigned level, bool recurse) const
|
||||
void Select::getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const
|
||||
{
|
||||
if (detailed)
|
||||
plan += "\nPLAN ";
|
||||
m_root->getLegacyPlan(tdbb, plan, level);
|
||||
}
|
||||
|
||||
void Select::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
planEntry.className = "Select";
|
||||
|
||||
if (m_rse->isSubQuery())
|
||||
{
|
||||
if (m_rse->isSubQuery())
|
||||
{
|
||||
plan += "\nSub-query";
|
||||
planEntry.description.add() = "Sub-query";
|
||||
|
||||
if (m_rse->isInvariant())
|
||||
plan += " (invariant)";
|
||||
}
|
||||
else if (m_cursorName.hasData())
|
||||
{
|
||||
plan += "\nCursor \"" + string(m_cursorName) + "\"";
|
||||
if (m_rse->isInvariant())
|
||||
planEntry.description.back() += " (invariant)";
|
||||
}
|
||||
else if (m_cursorName.hasData())
|
||||
{
|
||||
planEntry.description.add() = "Cursor \"" + string(m_cursorName) + "\"";
|
||||
|
||||
if (m_rse->isScrollable())
|
||||
plan += " (scrollable)";
|
||||
}
|
||||
else
|
||||
plan += "\nSelect Expression";
|
||||
|
||||
if (m_line || m_column)
|
||||
{
|
||||
string pos;
|
||||
pos.printf(" (line %u, column %u)", m_line, m_column);
|
||||
plan += pos;
|
||||
}
|
||||
if (m_rse->isScrollable())
|
||||
planEntry.description.back() += " (scrollable)";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_line || m_column)
|
||||
{
|
||||
string pos;
|
||||
pos.printf("\n-- line %u, column %u", m_line, m_column);
|
||||
plan += pos;
|
||||
}
|
||||
planEntry.description.add() = "Select Expression";
|
||||
|
||||
plan += "\nPLAN ";
|
||||
if (m_line || m_column)
|
||||
{
|
||||
string pos;
|
||||
pos.printf(" (line %u, column %u)", m_line, m_column);
|
||||
planEntry.description.back() += pos;
|
||||
}
|
||||
|
||||
if (recurse)
|
||||
m_root->print(tdbb, plan, detailed, level, true);
|
||||
m_root->getPlan(tdbb, planEntry.children.add(), level + 1, recurse);
|
||||
}
|
||||
|
||||
// ---------------------
|
||||
|
@ -68,22 +68,27 @@ namespace Jrd
|
||||
|
||||
void initializeInvariants(Request* request) const;
|
||||
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
void printPlan(thread_db* tdbb, Firebird::string& plan, bool detailed) const
|
||||
{
|
||||
print(tdbb, plan, detailed, 0, true);
|
||||
}
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override
|
||||
{
|
||||
children.add(m_root);
|
||||
if (detailed)
|
||||
{
|
||||
PlanEntry planEntry;
|
||||
getPlan(tdbb, planEntry, 0, true);
|
||||
planEntry.asString(plan);
|
||||
}
|
||||
else
|
||||
getLegacyPlan(tdbb, plan, 0);
|
||||
}
|
||||
|
||||
virtual void open(thread_db* tdbb) const = 0;
|
||||
virtual void close(thread_db* tdbb) const = 0;
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry,
|
||||
unsigned level, bool recurse) const override;
|
||||
|
||||
protected:
|
||||
const RecordSource* const m_root;
|
||||
const RseNode* const m_rse;
|
||||
|
@ -115,27 +115,27 @@ WriteLockResult ExternalTableScan::lockRecord(thread_db* tdbb, bool skipLocked)
|
||||
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
|
||||
}
|
||||
|
||||
void ExternalTableScan::getChildren(Array<const RecordSource*>& children) const
|
||||
void ExternalTableScan::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
if (!level)
|
||||
plan += "(";
|
||||
|
||||
plan += printName(tdbb, m_alias, false) + " NATURAL";
|
||||
|
||||
if (!level)
|
||||
plan += ")";
|
||||
}
|
||||
|
||||
void ExternalTableScan::print(thread_db* tdbb, string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const
|
||||
void ExternalTableScan::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
if (detailed)
|
||||
{
|
||||
plan += printIndent(++level) + "Table " +
|
||||
printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan";
|
||||
printOptInfo(plan);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!level)
|
||||
plan += "(";
|
||||
planEntry.className = "ExternalTableScan";
|
||||
|
||||
plan += printName(tdbb, m_alias, false) + " NATURAL";
|
||||
planEntry.description.add() = "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan";
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
if (!level)
|
||||
plan += ")";
|
||||
}
|
||||
planEntry.objectType = m_relation->getObjectType();
|
||||
planEntry.objectName = m_relation->rel_name;
|
||||
|
||||
if (m_alias.hasData() && m_relation->rel_name != m_alias)
|
||||
planEntry.alias = m_alias;
|
||||
}
|
||||
|
@ -116,25 +116,24 @@ WriteLockResult FilteredStream::lockRecord(thread_db* tdbb, bool skipLocked) con
|
||||
return m_next->lockRecord(tdbb, skipLocked);
|
||||
}
|
||||
|
||||
void FilteredStream::getChildren(Array<const RecordSource*>& children) const
|
||||
void FilteredStream::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
children.add(m_next);
|
||||
m_next->getLegacyPlan(tdbb, plan, level);
|
||||
}
|
||||
|
||||
void FilteredStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
||||
void FilteredStream::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
if (detailed)
|
||||
{
|
||||
plan += printIndent(++level) + "Filter";
|
||||
planEntry.className = "FilteredStream";
|
||||
|
||||
if (m_invariant)
|
||||
plan += " (preliminary)";
|
||||
planEntry.description.add() = "Filter";
|
||||
|
||||
printOptInfo(plan);
|
||||
}
|
||||
if (m_invariant)
|
||||
planEntry.description.back() += " (preliminary)";
|
||||
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
if (recurse)
|
||||
m_next->print(tdbb, plan, detailed, level, recurse);
|
||||
m_next->getPlan(tdbb, planEntry.children.add(), ++level, recurse);
|
||||
}
|
||||
|
||||
void FilteredStream::markRecursive()
|
||||
|
@ -120,21 +120,23 @@ WriteLockResult FirstRowsStream::lockRecord(thread_db* tdbb, bool skipLocked) co
|
||||
return m_next->lockRecord(tdbb, skipLocked);
|
||||
}
|
||||
|
||||
void FirstRowsStream::getChildren(Array<const RecordSource*>& children) const
|
||||
void FirstRowsStream::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
children.add(m_next);
|
||||
m_next->getLegacyPlan(tdbb, plan, level);
|
||||
}
|
||||
|
||||
void FirstRowsStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
||||
void FirstRowsStream::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
if (detailed)
|
||||
{
|
||||
plan += printIndent(++level) + "First N Records";
|
||||
printOptInfo(plan);
|
||||
}
|
||||
planEntry.className = "FirstRowsStream";
|
||||
|
||||
planEntry.description.add() = "First N Records";
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
if (recurse)
|
||||
m_next->print(tdbb, plan, detailed, level, recurse);
|
||||
{
|
||||
++level;
|
||||
m_next->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||
}
|
||||
}
|
||||
|
||||
void FirstRowsStream::markRecursive()
|
||||
|
@ -112,32 +112,28 @@ WriteLockResult FullOuterJoin::lockRecord(thread_db* tdbb, bool skipLocked) cons
|
||||
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
|
||||
}
|
||||
|
||||
void FullOuterJoin::getChildren(Array<const RecordSource*>& children) const
|
||||
void FullOuterJoin::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
children.add(m_arg1);
|
||||
children.add(m_arg2);
|
||||
level++;
|
||||
plan += "JOIN (";
|
||||
m_arg1->getLegacyPlan(tdbb, plan, level);
|
||||
plan += ", ";
|
||||
m_arg2->getLegacyPlan(tdbb, plan, level);
|
||||
plan += ")";
|
||||
}
|
||||
|
||||
void FullOuterJoin::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
||||
void FullOuterJoin::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
if (detailed)
|
||||
{
|
||||
plan += printIndent(++level) + "Full Outer Join";
|
||||
planEntry.className = "FullOuterJoin";
|
||||
|
||||
if (recurse)
|
||||
{
|
||||
m_arg1->print(tdbb, plan, true, level, recurse);
|
||||
m_arg2->print(tdbb, plan, true, level, recurse);
|
||||
}
|
||||
}
|
||||
else
|
||||
planEntry.description.add() = "Full Outer Join";
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
if (recurse)
|
||||
{
|
||||
level++;
|
||||
plan += "JOIN (";
|
||||
m_arg1->print(tdbb, plan, false, level, recurse);
|
||||
plan += ", ";
|
||||
m_arg2->print(tdbb, plan, false, level, recurse);
|
||||
plan += ")";
|
||||
++level;
|
||||
m_arg1->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||
m_arg2->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,44 +162,45 @@ bool FullTableScan::internalGetRecord(thread_db* tdbb) const
|
||||
return false;
|
||||
}
|
||||
|
||||
void FullTableScan::getChildren(Array<const RecordSource*>& children) const
|
||||
void FullTableScan::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
if (!level)
|
||||
plan += "(";
|
||||
|
||||
plan += printName(tdbb, m_alias, false) + " NATURAL";
|
||||
|
||||
if (!level)
|
||||
plan += ")";
|
||||
}
|
||||
|
||||
void FullTableScan::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
||||
void FullTableScan::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
if (detailed)
|
||||
planEntry.className = "FullTableScan";
|
||||
|
||||
auto lowerBounds = 0, upperBounds = 0;
|
||||
for (const auto range : m_dbkeyRanges)
|
||||
{
|
||||
auto lowerBounds = 0, upperBounds = 0;
|
||||
for (const auto range : m_dbkeyRanges)
|
||||
{
|
||||
if (range->lower)
|
||||
lowerBounds++;
|
||||
if (range->lower)
|
||||
lowerBounds++;
|
||||
|
||||
if (range->upper)
|
||||
upperBounds++;
|
||||
}
|
||||
|
||||
string bounds;
|
||||
if (lowerBounds && upperBounds)
|
||||
bounds += " (lower bound, upper bound)";
|
||||
else if (lowerBounds)
|
||||
bounds += " (lower bound)";
|
||||
else if (upperBounds)
|
||||
bounds += " (upper bound)";
|
||||
|
||||
plan += printIndent(++level) + "Table " +
|
||||
printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan" + bounds;
|
||||
printOptInfo(plan);
|
||||
if (range->upper)
|
||||
upperBounds++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!level)
|
||||
plan += "(";
|
||||
|
||||
plan += printName(tdbb, m_alias, false) + " NATURAL";
|
||||
string bounds;
|
||||
if (lowerBounds && upperBounds)
|
||||
bounds += " (lower bound, upper bound)";
|
||||
else if (lowerBounds)
|
||||
bounds += " (lower bound)";
|
||||
else if (upperBounds)
|
||||
bounds += " (upper bound)";
|
||||
|
||||
if (!level)
|
||||
plan += ")";
|
||||
}
|
||||
planEntry.description.add() = "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan" + bounds;
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
planEntry.objectType = m_relation->getObjectType();
|
||||
planEntry.objectName = m_relation->rel_name;
|
||||
|
||||
if (m_alias.hasData() && m_relation->rel_name != m_alias)
|
||||
planEntry.alias = m_alias;
|
||||
}
|
||||
|
@ -454,43 +454,37 @@ WriteLockResult HashJoin::lockRecord(thread_db* /*tdbb*/, bool /*skipLocked*/) c
|
||||
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
|
||||
}
|
||||
|
||||
void HashJoin::getChildren(Array<const RecordSource*>& children) const
|
||||
void HashJoin::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
children.add(m_leader.source);
|
||||
|
||||
level++;
|
||||
plan += "HASH (";
|
||||
m_leader.source->getLegacyPlan(tdbb, plan, level);
|
||||
plan += ", ";
|
||||
for (FB_SIZE_T i = 0; i < m_args.getCount(); i++)
|
||||
children.add(m_args[i].source);
|
||||
{
|
||||
if (i)
|
||||
plan += ", ";
|
||||
|
||||
m_args[i].source->getLegacyPlan(tdbb, plan, level);
|
||||
}
|
||||
plan += ")";
|
||||
}
|
||||
|
||||
void HashJoin::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
||||
void HashJoin::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
if (detailed)
|
||||
planEntry.className = "HashJoin";
|
||||
|
||||
planEntry.description.add() = "Hash Join (inner)";
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
if (recurse)
|
||||
{
|
||||
plan += printIndent(++level) + "Hash Join (inner)";
|
||||
printOptInfo(plan);
|
||||
++level;
|
||||
|
||||
if (recurse)
|
||||
{
|
||||
m_leader.source->print(tdbb, plan, true, level, recurse);
|
||||
m_leader.source->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||
|
||||
for (FB_SIZE_T i = 0; i < m_args.getCount(); i++)
|
||||
m_args[i].source->print(tdbb, plan, true, level, recurse);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
level++;
|
||||
plan += "HASH (";
|
||||
m_leader.source->print(tdbb, plan, false, level, recurse);
|
||||
plan += ", ";
|
||||
for (FB_SIZE_T i = 0; i < m_args.getCount(); i++)
|
||||
{
|
||||
if (i)
|
||||
plan += ", ";
|
||||
|
||||
m_args[i].source->print(tdbb, plan, false, level, recurse);
|
||||
}
|
||||
plan += ")";
|
||||
for (const auto& arg : m_args)
|
||||
arg.source->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -352,44 +352,45 @@ bool IndexTableScan::internalGetRecord(thread_db* tdbb) const
|
||||
return false;
|
||||
}
|
||||
|
||||
void IndexTableScan::getChildren(Array<const RecordSource*>& children) const
|
||||
void IndexTableScan::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
if (!level)
|
||||
plan += "(";
|
||||
|
||||
plan += printName(tdbb, m_alias, false) + " ORDER ";
|
||||
string index;
|
||||
printInversion(tdbb, m_index, index, false, level);
|
||||
plan += index;
|
||||
|
||||
if (m_inversion)
|
||||
{
|
||||
plan += " INDEX (";
|
||||
string indices;
|
||||
printInversion(tdbb, m_inversion, indices, false, level);
|
||||
plan += indices + ")";
|
||||
}
|
||||
|
||||
if (!level)
|
||||
plan += ")";
|
||||
}
|
||||
|
||||
void IndexTableScan::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
||||
void IndexTableScan::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
if (detailed)
|
||||
{
|
||||
plan += printIndent(++level) + "Table " +
|
||||
printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Access By ID";
|
||||
planEntry.className = "IndexTableScan";
|
||||
|
||||
printOptInfo(plan);
|
||||
printInversion(tdbb, m_index, plan, true, level, true);
|
||||
planEntry.description.add() = "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Access By ID";
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
if (m_inversion)
|
||||
printInversion(tdbb, m_inversion, plan, true, ++level);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!level)
|
||||
plan += "(";
|
||||
printInversion(tdbb, m_index, planEntry.description, true, true);
|
||||
|
||||
plan += printName(tdbb, m_alias, false) + " ORDER ";
|
||||
string index;
|
||||
printInversion(tdbb, m_index, index, false, level);
|
||||
plan += index;
|
||||
planEntry.objectType = m_relation->getObjectType();
|
||||
planEntry.objectName = m_relation->rel_name;
|
||||
|
||||
if (m_inversion)
|
||||
{
|
||||
plan += " INDEX (";
|
||||
string indices;
|
||||
printInversion(tdbb, m_inversion, indices, false, level);
|
||||
plan += indices + ")";
|
||||
}
|
||||
if (m_alias.hasData() && m_relation->rel_name != m_alias)
|
||||
planEntry.alias = m_alias;
|
||||
|
||||
if (!level)
|
||||
plan += ")";
|
||||
}
|
||||
if (m_inversion)
|
||||
printInversion(tdbb, m_inversion, planEntry.description, true);
|
||||
}
|
||||
|
||||
int IndexTableScan::compareKeys(const index_desc* idx,
|
||||
|
@ -71,10 +71,6 @@ void LocalTableStream::close(thread_db* tdbb) const
|
||||
impure->irsb_flags &= ~irsb_open;
|
||||
}
|
||||
|
||||
void LocalTableStream::getChildren(Array<const RecordSource*>& children) const
|
||||
{
|
||||
}
|
||||
|
||||
bool LocalTableStream::internalGetRecord(thread_db* tdbb) const
|
||||
{
|
||||
JRD_reschedule(tdbb);
|
||||
@ -113,24 +109,26 @@ WriteLockResult LocalTableStream::lockRecord(thread_db* tdbb, bool skipLocked) c
|
||||
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
|
||||
}
|
||||
|
||||
void LocalTableStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
||||
void LocalTableStream::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
//// TODO: Use Local Table name/alias.
|
||||
|
||||
if (detailed)
|
||||
{
|
||||
plan += printIndent(++level) + "Local Table Full Scan";
|
||||
printOptInfo(plan);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!level)
|
||||
plan += "(";
|
||||
if (!level)
|
||||
plan += "(";
|
||||
|
||||
plan += "Local_Table";
|
||||
plan += " NATURAL";
|
||||
plan += "Local_Table";
|
||||
plan += " NATURAL";
|
||||
|
||||
if (!level)
|
||||
plan += ")";
|
||||
}
|
||||
if (!level)
|
||||
plan += ")";
|
||||
}
|
||||
|
||||
void LocalTableStream::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
planEntry.className = "LocalTableStream";
|
||||
|
||||
//// TODO: Use Local Table name/alias.
|
||||
|
||||
planEntry.description.add() = "Local Table Full Scan";
|
||||
printOptInfo(planEntry.description);
|
||||
}
|
||||
|
@ -111,21 +111,23 @@ WriteLockResult LockedStream::lockRecord(thread_db* tdbb, bool skipLocked) const
|
||||
return m_next->lockRecord(tdbb, skipLocked);
|
||||
}
|
||||
|
||||
void LockedStream::getChildren(Array<const RecordSource*>& children) const
|
||||
void LockedStream::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
children.add(m_next);
|
||||
m_next->getLegacyPlan(tdbb, plan, level);
|
||||
}
|
||||
|
||||
void LockedStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
||||
void LockedStream::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
if (detailed)
|
||||
{
|
||||
plan += printIndent(++level) + "Write Lock";
|
||||
printOptInfo(plan);
|
||||
}
|
||||
planEntry.className = "LockedStream";
|
||||
|
||||
planEntry.description.add() = "Write Lock";
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
if (recurse)
|
||||
m_next->print(tdbb, plan, detailed, level, recurse);
|
||||
{
|
||||
++level;
|
||||
m_next->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||
}
|
||||
}
|
||||
|
||||
void LockedStream::markRecursive()
|
||||
|
@ -345,37 +345,33 @@ WriteLockResult MergeJoin::lockRecord(thread_db* /*tdbb*/, bool /*skipLocked*/)
|
||||
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
|
||||
}
|
||||
|
||||
void MergeJoin::getChildren(Array<const RecordSource*>& children) const
|
||||
void MergeJoin::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
level++;
|
||||
plan += "MERGE (";
|
||||
for (FB_SIZE_T i = 0; i < m_args.getCount(); i++)
|
||||
children.add(m_args[i]);
|
||||
{
|
||||
if (i)
|
||||
plan += ", ";
|
||||
|
||||
m_args[i]->getLegacyPlan(tdbb, plan, level);
|
||||
}
|
||||
plan += ")";
|
||||
}
|
||||
|
||||
void MergeJoin::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
||||
void MergeJoin::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
if (detailed)
|
||||
{
|
||||
plan += printIndent(++level) + "Merge Join (inner)";
|
||||
printOptInfo(plan);
|
||||
planEntry.className = "MergeJoin";
|
||||
|
||||
if (recurse)
|
||||
{
|
||||
for (FB_SIZE_T i = 0; i < m_args.getCount(); i++)
|
||||
m_args[i]->print(tdbb, plan, true, level, recurse);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
level++;
|
||||
plan += "MERGE (";
|
||||
for (FB_SIZE_T i = 0; i < m_args.getCount(); i++)
|
||||
{
|
||||
if (i)
|
||||
plan += ", ";
|
||||
planEntry.description.add() = "Merge Join (inner)";
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
m_args[i]->print(tdbb, plan, false, level, recurse);
|
||||
}
|
||||
plan += ")";
|
||||
if (recurse)
|
||||
{
|
||||
++level;
|
||||
|
||||
for (const auto arg : m_args)
|
||||
arg->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,63 +208,59 @@ WriteLockResult NestedLoopJoin::lockRecord(thread_db* /*tdbb*/, bool /*skipLocke
|
||||
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
|
||||
}
|
||||
|
||||
void NestedLoopJoin::getChildren(Array<const RecordSource*>& children) const
|
||||
{
|
||||
for (FB_SIZE_T i = 0; i < m_args.getCount(); i++)
|
||||
children.add(m_args[i]);
|
||||
}
|
||||
|
||||
void NestedLoopJoin::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
||||
void NestedLoopJoin::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
if (m_args.hasData())
|
||||
{
|
||||
if (detailed)
|
||||
level++;
|
||||
plan += "JOIN (";
|
||||
for (FB_SIZE_T i = 0; i < m_args.getCount(); i++)
|
||||
{
|
||||
plan += printIndent(++level) + "Nested Loop Join ";
|
||||
if (i)
|
||||
plan += ", ";
|
||||
|
||||
switch (m_joinType)
|
||||
{
|
||||
case INNER_JOIN:
|
||||
plan += "(inner)";
|
||||
break;
|
||||
|
||||
case OUTER_JOIN:
|
||||
plan += "(outer)";
|
||||
break;
|
||||
|
||||
case SEMI_JOIN:
|
||||
plan += "(semi)";
|
||||
break;
|
||||
|
||||
case ANTI_JOIN:
|
||||
plan += "(anti)";
|
||||
break;
|
||||
|
||||
default:
|
||||
fb_assert(false);
|
||||
}
|
||||
|
||||
printOptInfo(plan);
|
||||
|
||||
if (recurse)
|
||||
{
|
||||
for (FB_SIZE_T i = 0; i < m_args.getCount(); i++)
|
||||
m_args[i]->print(tdbb, plan, true, level, recurse);
|
||||
}
|
||||
m_args[i]->getLegacyPlan(tdbb, plan, level);
|
||||
}
|
||||
else
|
||||
{
|
||||
level++;
|
||||
plan += "JOIN (";
|
||||
for (FB_SIZE_T i = 0; i < m_args.getCount(); i++)
|
||||
{
|
||||
if (i)
|
||||
plan += ", ";
|
||||
plan += ")";
|
||||
}
|
||||
}
|
||||
|
||||
m_args[i]->print(tdbb, plan, false, level, recurse);
|
||||
}
|
||||
plan += ")";
|
||||
}
|
||||
void NestedLoopJoin::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
planEntry.className = "NestedLoopJoin";
|
||||
|
||||
planEntry.description.add() = "Nested Loop Join ";
|
||||
|
||||
switch (m_joinType)
|
||||
{
|
||||
case INNER_JOIN:
|
||||
planEntry.description.back() += "(inner)";
|
||||
break;
|
||||
|
||||
case OUTER_JOIN:
|
||||
planEntry.description.back() += "(outer)";
|
||||
break;
|
||||
|
||||
case SEMI_JOIN:
|
||||
planEntry.description.back() += "(semi)";
|
||||
break;
|
||||
|
||||
case ANTI_JOIN:
|
||||
planEntry.description.back() += "(anti)";
|
||||
break;
|
||||
|
||||
default:
|
||||
fb_assert(false);
|
||||
}
|
||||
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
if (recurse)
|
||||
{
|
||||
++level;
|
||||
|
||||
for (const auto arg : m_args)
|
||||
arg->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -249,28 +249,30 @@ WriteLockResult ProcedureScan::lockRecord(thread_db* /*tdbb*/, bool /*skipLocked
|
||||
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
|
||||
}
|
||||
|
||||
void ProcedureScan::getChildren(Array<const RecordSource*>& children) const
|
||||
void ProcedureScan::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
if (!level)
|
||||
plan += "(";
|
||||
|
||||
plan += printName(tdbb, m_alias, false) + " NATURAL";
|
||||
|
||||
if (!level)
|
||||
plan += ")";
|
||||
}
|
||||
|
||||
void ProcedureScan::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
||||
void ProcedureScan::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
if (detailed)
|
||||
{
|
||||
plan += printIndent(++level) + "Procedure " +
|
||||
printName(tdbb, m_procedure->getName().toString(), m_alias) + " Scan";
|
||||
printOptInfo(plan);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!level)
|
||||
plan += "(";
|
||||
planEntry.className = "ProcedureScan";
|
||||
|
||||
plan += printName(tdbb, m_alias, false) + " NATURAL";
|
||||
planEntry.description.add() = "Procedure " + printName(tdbb, m_procedure->getName().toString(), m_alias) + " Scan";
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
if (!level)
|
||||
plan += ")";
|
||||
}
|
||||
planEntry.objectType = obj_procedure;
|
||||
planEntry.packageName = m_procedure->getName().package;
|
||||
planEntry.objectName = m_procedure->getName().identifier;
|
||||
|
||||
if (m_alias.hasData() && m_procedure->getName().toString() != m_alias)
|
||||
planEntry.alias = m_alias;
|
||||
}
|
||||
|
||||
void ProcedureScan::assignParams(thread_db* tdbb,
|
||||
|
@ -54,6 +54,62 @@ AccessPath::AccessPath(CompilerScratch* csb)
|
||||
{
|
||||
}
|
||||
|
||||
void AccessPath::getPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
planEntry.accessPath = this;
|
||||
planEntry.level = level;
|
||||
|
||||
internalGetPlan(tdbb, planEntry, level, recurse);
|
||||
}
|
||||
|
||||
|
||||
// PlanEntry class
|
||||
// -------------------
|
||||
|
||||
void PlanEntry::getDescriptionAsString(string& str, bool initialIndentation) const
|
||||
{
|
||||
auto indentLevel = initialIndentation ? level : 0;
|
||||
|
||||
for (const auto& line : description)
|
||||
{
|
||||
const string indent(indentLevel * 4, ' ');
|
||||
|
||||
if (initialIndentation || indentLevel)
|
||||
str += "\n" + indent;
|
||||
|
||||
if (level)
|
||||
str += "-> ";
|
||||
|
||||
str += line;
|
||||
|
||||
++indentLevel;
|
||||
}
|
||||
}
|
||||
|
||||
void PlanEntry::asFlatList(Array<NonPooledPair<const PlanEntry*, const PlanEntry*>>& list) const
|
||||
{
|
||||
list.clear();
|
||||
list.add({this, nullptr});
|
||||
|
||||
for (unsigned pos = 0; pos < list.getCount(); ++pos)
|
||||
{
|
||||
const auto thisEntry = list[pos].first;
|
||||
unsigned childPos = pos;
|
||||
|
||||
for (const auto& child : thisEntry->children)
|
||||
list.insert(++childPos, {&child, thisEntry});
|
||||
}
|
||||
}
|
||||
|
||||
void PlanEntry::asString(string& str) const
|
||||
{
|
||||
Array<NonPooledPair<const PlanEntry*, const PlanEntry*>> list;
|
||||
asFlatList(list);
|
||||
|
||||
for (const auto& pair : list)
|
||||
pair.first->getDescriptionAsString(str, true);
|
||||
}
|
||||
|
||||
|
||||
// Record source class
|
||||
// -------------------
|
||||
@ -98,40 +154,32 @@ string RecordSource::printName(thread_db* tdbb, const string& name, const string
|
||||
return result;
|
||||
}
|
||||
|
||||
string RecordSource::printIndent(unsigned level)
|
||||
{
|
||||
fb_assert(level);
|
||||
|
||||
const string indent(level * 4, ' ');
|
||||
return string("\n" + indent + "-> ");
|
||||
}
|
||||
|
||||
void RecordSource::printInversion(thread_db* tdbb, const InversionNode* inversion,
|
||||
string& plan, bool detailed, unsigned level, bool navigation)
|
||||
ObjectsArray<string>& planLines, bool detailed, bool navigation)
|
||||
{
|
||||
if (detailed)
|
||||
plan += printIndent(++level);
|
||||
const bool wasEmpty = planLines.isEmpty();
|
||||
auto plan = &planLines.add();
|
||||
|
||||
switch (inversion->type)
|
||||
{
|
||||
case InversionNode::TYPE_AND:
|
||||
if (detailed)
|
||||
plan += "Bitmap And";
|
||||
printInversion(tdbb, inversion->node1, plan, detailed, level);
|
||||
printInversion(tdbb, inversion->node2, plan, detailed, level);
|
||||
*plan += "Bitmap And";
|
||||
printInversion(tdbb, inversion->node1, planLines, detailed);
|
||||
printInversion(tdbb, inversion->node2, planLines, detailed);
|
||||
break;
|
||||
|
||||
case InversionNode::TYPE_OR:
|
||||
case InversionNode::TYPE_IN:
|
||||
if (detailed)
|
||||
plan += "Bitmap Or";
|
||||
printInversion(tdbb, inversion->node1, plan, detailed, level);
|
||||
printInversion(tdbb, inversion->node2, plan, detailed, level);
|
||||
*plan += "Bitmap Or";
|
||||
printInversion(tdbb, inversion->node1, planLines, detailed);
|
||||
printInversion(tdbb, inversion->node2, planLines, detailed);
|
||||
break;
|
||||
|
||||
case InversionNode::TYPE_DBKEY:
|
||||
if (detailed)
|
||||
plan += "DBKEY";
|
||||
*plan += "DBKEY";
|
||||
break;
|
||||
|
||||
case InversionNode::TYPE_INDEX:
|
||||
@ -147,7 +195,10 @@ void RecordSource::printInversion(thread_db* tdbb, const InversionNode* inversio
|
||||
if (detailed)
|
||||
{
|
||||
if (!navigation)
|
||||
plan += "Bitmap" + printIndent(++level);
|
||||
{
|
||||
*plan += "Bitmap";
|
||||
plan = &planLines.add();
|
||||
}
|
||||
|
||||
const index_desc& idx = retrieval->irb_desc;
|
||||
const bool uniqueIdx = (idx.idx_flags & idx_unique);
|
||||
@ -194,13 +245,11 @@ void RecordSource::printInversion(thread_db* tdbb, const InversionNode* inversio
|
||||
}
|
||||
}
|
||||
|
||||
plan += "Index " + printName(tdbb, indexName.c_str()) +
|
||||
*plan += "Index " + printName(tdbb, indexName.c_str()) +
|
||||
(fullscan ? " Full" : unique ? " Unique" : list ? " List" : " Range") + " Scan" + bounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
plan += (plan.hasData() ? ", " : "") + printName(tdbb, indexName.c_str(), false);
|
||||
}
|
||||
*plan += (wasEmpty ? "" : ", ") + printName(tdbb, indexName.c_str(), false);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -209,13 +258,28 @@ void RecordSource::printInversion(thread_db* tdbb, const InversionNode* inversio
|
||||
}
|
||||
}
|
||||
|
||||
void RecordSource::printOptInfo(string& plan) const
|
||||
void RecordSource::printInversion(thread_db* tdbb, const InversionNode* inversion,
|
||||
string& plan, bool detailed, unsigned level, bool navigation)
|
||||
{
|
||||
ObjectsArray<string> planLines;
|
||||
printInversion(tdbb, inversion, planLines, detailed, navigation);
|
||||
|
||||
for (const auto& line : planLines)
|
||||
{
|
||||
if (detailed)
|
||||
plan += '\n' + string(++level * 4, ' ');
|
||||
plan += line;
|
||||
}
|
||||
}
|
||||
|
||||
void RecordSource::printOptInfo(ObjectsArray<string>& planLines) const
|
||||
{
|
||||
#ifdef PRINT_OPT_INFO
|
||||
fb_assert(planLines.hasData());
|
||||
string info;
|
||||
// Add 0.5 to convert double->int truncation into rounding
|
||||
info.printf(" [rows: %" UQUADFORMAT "]", (FB_UINT64) (m_cardinality + 0.5));
|
||||
plan += info;
|
||||
planLines.back() += info;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#ifndef JRD_RECORD_SOURCE_H
|
||||
#define JRD_RECORD_SOURCE_H
|
||||
|
||||
#include <optional>
|
||||
#include "../common/classes/array.h"
|
||||
#include "../common/classes/objects_array.h"
|
||||
#include "../common/classes/NestConst.h"
|
||||
@ -50,6 +51,7 @@ namespace Jrd
|
||||
struct win;
|
||||
class BaseBufferedStream;
|
||||
class BufferedStream;
|
||||
class PlanEntry;
|
||||
|
||||
enum JoinType { INNER_JOIN, OUTER_JOIN, SEMI_JOIN, ANTI_JOIN };
|
||||
|
||||
@ -71,16 +73,56 @@ namespace Jrd
|
||||
return m_recSourceId;
|
||||
}
|
||||
|
||||
virtual void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const = 0;
|
||||
double getCardinality() const
|
||||
{
|
||||
return m_cardinality;
|
||||
}
|
||||
|
||||
virtual void getChildren(Firebird::Array<const RecordSource*>& children) const = 0;
|
||||
void getPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const;
|
||||
|
||||
virtual void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const = 0;
|
||||
|
||||
protected:
|
||||
virtual void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry,
|
||||
unsigned level, bool recurse) const = 0;
|
||||
|
||||
protected:
|
||||
double m_cardinality = 0.0;
|
||||
|
||||
private:
|
||||
const ULONG m_cursorId;
|
||||
const ULONG m_recSourceId;
|
||||
};
|
||||
|
||||
class PlanEntry final : public Firebird::AutoStorage
|
||||
{
|
||||
public:
|
||||
explicit PlanEntry(MemoryPool& pool)
|
||||
: AutoStorage(pool)
|
||||
{
|
||||
}
|
||||
|
||||
PlanEntry() = default;
|
||||
|
||||
public:
|
||||
void getDescriptionAsString(Firebird::string& str, bool initialIndentation = false) const;
|
||||
void asFlatList(Firebird::Array<Firebird::NonPooledPair<const PlanEntry*, const PlanEntry*>>& list) const;
|
||||
void asString(Firebird::string& str) const;
|
||||
|
||||
public:
|
||||
Firebird::string className{getPool()};
|
||||
Firebird::ObjectsArray<Firebird::string> description{getPool()};
|
||||
Firebird::ObjectsArray<PlanEntry> children{getPool()};
|
||||
std::optional<ObjectType> objectType;
|
||||
MetaName packageName;
|
||||
MetaName objectName;
|
||||
MetaName alias;
|
||||
const AccessPath* accessPath = nullptr;
|
||||
ULONG recordLength = 0;
|
||||
ULONG keyLength = 0;
|
||||
unsigned level = 0;
|
||||
};
|
||||
|
||||
// Abstract base class for record sources.
|
||||
class RecordSource : public AccessPath
|
||||
{
|
||||
@ -106,11 +148,6 @@ namespace Jrd
|
||||
return true;
|
||||
}
|
||||
|
||||
double getCardinality() const
|
||||
{
|
||||
return m_cardinality;
|
||||
}
|
||||
|
||||
void open(thread_db* tdbb) const;
|
||||
|
||||
bool getRecord(thread_db* tdbb) const;
|
||||
@ -134,11 +171,15 @@ namespace Jrd
|
||||
static Firebird::string printName(thread_db* tdbb, const Firebird::string& name,
|
||||
const Firebird::string& alias);
|
||||
|
||||
static Firebird::string printIndent(unsigned level);
|
||||
static void printInversion(thread_db* tdbb, const InversionNode* inversion,
|
||||
Firebird::ObjectsArray<Firebird::string>& planLines, bool detailed,
|
||||
bool navigation = false);
|
||||
|
||||
static void printInversion(thread_db* tdbb, const InversionNode* inversion,
|
||||
Firebird::string& plan, bool detailed,
|
||||
unsigned level, bool navigation = false);
|
||||
void printOptInfo(Firebird::string& plan) const;
|
||||
|
||||
void printOptInfo(Firebird::ObjectsArray<Firebird::string>& plan) const;
|
||||
|
||||
static void saveRecord(thread_db* tdbb, record_param* rpb);
|
||||
static void restoreRecord(thread_db* tdbb, record_param* rpb);
|
||||
@ -146,7 +187,6 @@ namespace Jrd
|
||||
virtual void internalOpen(thread_db* tdbb) const = 0;
|
||||
virtual bool internalGetRecord(thread_db* tdbb) const = 0;
|
||||
|
||||
double m_cardinality = 0.0;
|
||||
ULONG m_impure = 0;
|
||||
bool m_recursive = false;
|
||||
};
|
||||
@ -191,12 +231,10 @@ namespace Jrd
|
||||
|
||||
void close(thread_db* tdbb) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
|
||||
@ -220,12 +258,10 @@ namespace Jrd
|
||||
|
||||
void close(thread_db* tdbb) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
|
||||
@ -264,10 +300,7 @@ namespace Jrd
|
||||
|
||||
void close(thread_db* tdbb) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
void setInversion(InversionNode* inversion, BoolExprNode* condition)
|
||||
{
|
||||
@ -277,6 +310,7 @@ namespace Jrd
|
||||
}
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
|
||||
@ -317,14 +351,12 @@ namespace Jrd
|
||||
bool refetchRecord(thread_db* tdbb) const override;
|
||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
protected:
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
|
||||
private:
|
||||
jrd_rel* const m_relation;
|
||||
@ -342,12 +374,10 @@ namespace Jrd
|
||||
bool refetchRecord(thread_db* tdbb) const override;
|
||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
|
||||
@ -378,12 +408,10 @@ namespace Jrd
|
||||
bool refetchRecord(thread_db* tdbb) const override;
|
||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
|
||||
@ -411,10 +439,7 @@ namespace Jrd
|
||||
bool refetchRecord(thread_db* tdbb) const override;
|
||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
void markRecursive() override;
|
||||
void invalidateRecords(Request* request) const override;
|
||||
@ -423,6 +448,7 @@ namespace Jrd
|
||||
void nullRecords(thread_db* tdbb) const override;
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
|
||||
@ -443,10 +469,7 @@ namespace Jrd
|
||||
bool refetchRecord(thread_db* tdbb) const override;
|
||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
void markRecursive() override;
|
||||
void invalidateRecords(Request* request) const override;
|
||||
@ -455,6 +478,7 @@ namespace Jrd
|
||||
void nullRecords(thread_db* tdbb) const override;
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
|
||||
@ -478,10 +502,7 @@ namespace Jrd
|
||||
bool refetchRecord(thread_db* tdbb) const override;
|
||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
void markRecursive() override;
|
||||
void invalidateRecords(Request* request) const override;
|
||||
@ -495,6 +516,7 @@ namespace Jrd
|
||||
}
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
|
||||
@ -518,10 +540,7 @@ namespace Jrd
|
||||
bool refetchRecord(thread_db* tdbb) const override;
|
||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
void markRecursive() override;
|
||||
void invalidateRecords(Request* request) const override;
|
||||
@ -535,6 +554,7 @@ namespace Jrd
|
||||
}
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
|
||||
@ -554,10 +574,7 @@ namespace Jrd
|
||||
bool refetchRecord(thread_db* tdbb) const override;
|
||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
void markRecursive() override;
|
||||
void invalidateRecords(Request* request) const override;
|
||||
@ -576,6 +593,7 @@ namespace Jrd
|
||||
}
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
|
||||
@ -675,10 +693,7 @@ namespace Jrd
|
||||
bool refetchRecord(thread_db* tdbb) const override;
|
||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
void markRecursive() override;
|
||||
void invalidateRecords(Request* request) const override;
|
||||
@ -720,6 +735,7 @@ namespace Jrd
|
||||
}
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
|
||||
@ -901,12 +917,11 @@ namespace Jrd
|
||||
const NestValueArray* group, MapNode* map, RecordSource* next);
|
||||
|
||||
public:
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
void print(thread_db* tdbb, Firebird::string& plan, bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
|
||||
};
|
||||
|
||||
class WindowedStream : public RecordSource
|
||||
@ -971,13 +986,13 @@ namespace Jrd
|
||||
public:
|
||||
void close(thread_db* tdbb) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan, bool detailed, unsigned level, bool recurse) const override;
|
||||
void findUsedStreams(StreamList& streams, bool expandAll = false) const override;
|
||||
void nullRecords(thread_db* tdbb) const override;
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
|
||||
@ -1013,10 +1028,7 @@ namespace Jrd
|
||||
bool refetchRecord(thread_db* tdbb) const override;
|
||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
void markRecursive() override;
|
||||
void invalidateRecords(Request* request) const override;
|
||||
@ -1025,6 +1037,7 @@ namespace Jrd
|
||||
void nullRecords(thread_db* tdbb) const override;
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
|
||||
@ -1081,10 +1094,7 @@ namespace Jrd
|
||||
bool refetchRecord(thread_db* tdbb) const override;
|
||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
void markRecursive() override;
|
||||
void invalidateRecords(Request* request) const override;
|
||||
@ -1102,6 +1112,7 @@ namespace Jrd
|
||||
}
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
|
||||
@ -1125,10 +1136,7 @@ namespace Jrd
|
||||
bool refetchRecord(thread_db* tdbb) const override;
|
||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
void markRecursive() override;
|
||||
void invalidateRecords(Request* request) const override;
|
||||
@ -1137,6 +1145,7 @@ namespace Jrd
|
||||
void nullRecords(thread_db* tdbb) const override;
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
|
||||
@ -1158,10 +1167,7 @@ namespace Jrd
|
||||
bool refetchRecord(thread_db* tdbb) const override;
|
||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
void markRecursive() override;
|
||||
void invalidateRecords(Request* request) const override;
|
||||
@ -1170,6 +1176,7 @@ namespace Jrd
|
||||
void nullRecords(thread_db* tdbb) const override;
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
|
||||
@ -1212,10 +1219,7 @@ namespace Jrd
|
||||
bool refetchRecord(thread_db* tdbb) const override;
|
||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
void markRecursive() override;
|
||||
void invalidateRecords(Request* request) const override;
|
||||
@ -1226,6 +1230,7 @@ namespace Jrd
|
||||
static unsigned maxCapacity();
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
|
||||
@ -1278,10 +1283,7 @@ namespace Jrd
|
||||
bool refetchRecord(thread_db* tdbb) const override;
|
||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
void markRecursive() override;
|
||||
void invalidateRecords(Request* request) const override;
|
||||
@ -1290,6 +1292,7 @@ namespace Jrd
|
||||
void nullRecords(thread_db* tdbb) const override;
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
|
||||
@ -1311,15 +1314,13 @@ namespace Jrd
|
||||
|
||||
void close(thread_db* tdbb) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
|
||||
bool refetchRecord(thread_db* tdbb) const override;
|
||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
|
||||
@ -1344,16 +1345,14 @@ namespace Jrd
|
||||
bool refetchRecord(thread_db* tdbb) const override;
|
||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
void markRecursive() override;
|
||||
void invalidateRecords(Request* request) const override;
|
||||
void findUsedStreams(StreamList& streams, bool expandAll = false) const override;
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
|
||||
@ -1389,16 +1388,14 @@ namespace Jrd
|
||||
bool refetchRecord(thread_db* tdbb) const override;
|
||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
void markRecursive() override;
|
||||
void invalidateRecords(Request* request) const override;
|
||||
void findUsedStreams(StreamList& streams, bool expandAll = false) const override;
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
|
||||
@ -1431,10 +1428,7 @@ namespace Jrd
|
||||
bool refetchRecord(thread_db* tdbb) const override;
|
||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
void markRecursive() override;
|
||||
void invalidateRecords(Request* request) const override;
|
||||
@ -1443,6 +1437,7 @@ namespace Jrd
|
||||
void nullRecords(thread_db* tdbb) const override;
|
||||
|
||||
protected:
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
|
||||
|
@ -238,38 +238,33 @@ WriteLockResult RecursiveStream::lockRecord(thread_db* /*tdbb*/, bool /*skipLock
|
||||
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
|
||||
}
|
||||
|
||||
void RecursiveStream::getChildren(Array<const RecordSource*>& children) const
|
||||
void RecursiveStream::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
children.add(m_root);
|
||||
children.add(m_inner);
|
||||
if (!level)
|
||||
plan += "(";
|
||||
|
||||
m_root->getLegacyPlan(tdbb, plan, level + 1);
|
||||
|
||||
plan += ", ";
|
||||
|
||||
m_inner->getLegacyPlan(tdbb, plan, level + 1);
|
||||
|
||||
if (!level)
|
||||
plan += ")";
|
||||
}
|
||||
|
||||
void RecursiveStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
||||
void RecursiveStream::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
if (detailed)
|
||||
planEntry.className = "RecursiveStream";
|
||||
|
||||
planEntry.description.add() = "Recursion";
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
if (recurse)
|
||||
{
|
||||
plan += printIndent(++level) + "Recursion";
|
||||
printOptInfo(plan);
|
||||
|
||||
if (recurse)
|
||||
{
|
||||
m_root->print(tdbb, plan, true, level, recurse);
|
||||
m_inner->print(tdbb, plan, true, level, recurse);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!level)
|
||||
plan += "(";
|
||||
|
||||
m_root->print(tdbb, plan, false, level + 1, recurse);
|
||||
|
||||
plan += ", ";
|
||||
|
||||
m_inner->print(tdbb, plan, false, level + 1, recurse);
|
||||
|
||||
if (!level)
|
||||
plan += ")";
|
||||
++level;
|
||||
m_root->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||
m_inner->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,21 +146,23 @@ WriteLockResult SingularStream::lockRecord(thread_db* tdbb, bool skipLocked) con
|
||||
return m_next->lockRecord(tdbb, skipLocked);
|
||||
}
|
||||
|
||||
void SingularStream::getChildren(Array<const RecordSource*>& children) const
|
||||
void SingularStream::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
children.add(m_next);
|
||||
m_next->getLegacyPlan(tdbb, plan, level);
|
||||
}
|
||||
|
||||
void SingularStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
||||
void SingularStream::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
if (detailed)
|
||||
{
|
||||
plan += printIndent(++level) + "Singularity Check";
|
||||
printOptInfo(plan);
|
||||
}
|
||||
planEntry.className = "SingularStream";
|
||||
|
||||
planEntry.description.add() = "Singularity Check";
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
if (recurse)
|
||||
m_next->print(tdbb, plan, detailed, level, recurse);
|
||||
{
|
||||
++level;
|
||||
m_next->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||
}
|
||||
}
|
||||
|
||||
void SingularStream::markRecursive()
|
||||
|
@ -116,21 +116,23 @@ WriteLockResult SkipRowsStream::lockRecord(thread_db* tdbb, bool skipLocked) con
|
||||
return m_next->lockRecord(tdbb, skipLocked);
|
||||
}
|
||||
|
||||
void SkipRowsStream::getChildren(Array<const RecordSource*>& children) const
|
||||
void SkipRowsStream::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
children.add(m_next);
|
||||
m_next->getLegacyPlan(tdbb, plan, level);
|
||||
}
|
||||
|
||||
void SkipRowsStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
||||
void SkipRowsStream::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
if (detailed)
|
||||
{
|
||||
plan += printIndent(++level) + "Skip N Records";
|
||||
printOptInfo(plan);
|
||||
}
|
||||
planEntry.className = "SkipRowsStream";
|
||||
|
||||
planEntry.description.add() = "Skip N Records";
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
if (recurse)
|
||||
m_next->print(tdbb, plan, detailed, level, recurse);
|
||||
{
|
||||
++level;
|
||||
m_next->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||
}
|
||||
}
|
||||
|
||||
void SkipRowsStream::markRecursive()
|
||||
|
@ -121,36 +121,41 @@ WriteLockResult SortedStream::lockRecord(thread_db* tdbb, bool skipLocked) const
|
||||
return m_next->lockRecord(tdbb, skipLocked);
|
||||
}
|
||||
|
||||
void SortedStream::getChildren(Array<const RecordSource*>& children) const
|
||||
void SortedStream::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
children.add(m_next);
|
||||
level++;
|
||||
plan += "SORT (";
|
||||
m_next->getLegacyPlan(tdbb, plan, level);
|
||||
plan += ")";
|
||||
}
|
||||
|
||||
void SortedStream::print(thread_db* tdbb, string& plan,
|
||||
bool detailed, unsigned level, bool recurse) const
|
||||
void SortedStream::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
if (detailed)
|
||||
planEntry.className = "SortedStream";
|
||||
|
||||
string extras;
|
||||
extras.printf(" (record length: %" ULONGFORMAT", key length: %" ULONGFORMAT")",
|
||||
m_map->length, m_map->keyLength);
|
||||
|
||||
auto planDescription = &planEntry.description.add();
|
||||
|
||||
if (m_map->flags & FLAG_REFETCH)
|
||||
{
|
||||
string extras;
|
||||
extras.printf(" (record length: %" ULONGFORMAT", key length: %" ULONGFORMAT")",
|
||||
m_map->length, m_map->keyLength);
|
||||
|
||||
if (m_map->flags & FLAG_REFETCH)
|
||||
plan += printIndent(++level) + "Refetch";
|
||||
|
||||
plan += printIndent(++level) +
|
||||
((m_map->flags & FLAG_PROJECT) ? "Unique Sort" : "Sort") + extras;
|
||||
printOptInfo(plan);
|
||||
|
||||
if (recurse)
|
||||
m_next->print(tdbb, plan, true, level, recurse);
|
||||
*planDescription = "Refetch";
|
||||
planDescription = &planEntry.description.add();
|
||||
++level;
|
||||
}
|
||||
else
|
||||
|
||||
*planDescription += ((m_map->flags & FLAG_PROJECT) ? "Unique Sort" : "Sort") + extras;
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
planEntry.recordLength = m_map->length;
|
||||
planEntry.keyLength = m_map->keyLength;
|
||||
|
||||
if (recurse)
|
||||
{
|
||||
level++;
|
||||
plan += "SORT (";
|
||||
m_next->print(tdbb, plan, false, level, recurse);
|
||||
plan += ")";
|
||||
++level;
|
||||
m_next->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,40 +162,36 @@ WriteLockResult Union::lockRecord(thread_db* tdbb, bool skipLocked) const
|
||||
return m_args[impure->irsb_count]->lockRecord(tdbb, skipLocked);
|
||||
}
|
||||
|
||||
void Union::getChildren(Array<const RecordSource*>& children) const
|
||||
void Union::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
if (!level)
|
||||
plan += "(";
|
||||
|
||||
for (FB_SIZE_T i = 0; i < m_args.getCount(); i++)
|
||||
children.add(m_args[i]);
|
||||
{
|
||||
if (i)
|
||||
plan += ", ";
|
||||
|
||||
m_args[i]->getLegacyPlan(tdbb, plan, level + 1);
|
||||
}
|
||||
|
||||
if (!level)
|
||||
plan += ")";
|
||||
}
|
||||
|
||||
void Union::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
||||
void Union::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
if (detailed)
|
||||
planEntry.className = "Union";
|
||||
|
||||
planEntry.description.add() = (m_args.getCount() == 1 ? "Materialize" : "Union");
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
if (recurse)
|
||||
{
|
||||
plan += printIndent(++level) + (m_args.getCount() == 1 ? "Materialize" : "Union");
|
||||
printOptInfo(plan);
|
||||
++level;
|
||||
|
||||
if (recurse)
|
||||
{
|
||||
for (FB_SIZE_T i = 0; i < m_args.getCount(); i++)
|
||||
m_args[i]->print(tdbb, plan, true, level, recurse);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!level)
|
||||
plan += "(";
|
||||
|
||||
for (FB_SIZE_T i = 0; i < m_args.getCount(); i++)
|
||||
{
|
||||
if (i)
|
||||
plan += ", ";
|
||||
|
||||
m_args[i]->print(tdbb, plan, false, level + 1, recurse);
|
||||
}
|
||||
|
||||
if (!level)
|
||||
plan += ")";
|
||||
for (const auto arg : m_args)
|
||||
arg->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,26 +109,27 @@ WriteLockResult VirtualTableScan::lockRecord(thread_db* /*tdbb*/, bool /*skipLoc
|
||||
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
|
||||
}
|
||||
|
||||
void VirtualTableScan::getChildren(Array<const RecordSource*>& children) const
|
||||
void VirtualTableScan::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
if (!level)
|
||||
plan += "(";
|
||||
|
||||
plan += printName(tdbb, m_alias, false) + " NATURAL";
|
||||
|
||||
if (!level)
|
||||
plan += ")";
|
||||
}
|
||||
|
||||
void VirtualTableScan::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
||||
void VirtualTableScan::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
if (detailed)
|
||||
{
|
||||
plan += printIndent(++level) + "Table " +
|
||||
printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan";
|
||||
printOptInfo(plan);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!level)
|
||||
plan += "(";
|
||||
planEntry.className = "VirtualTableScan";
|
||||
|
||||
plan += printName(tdbb, m_alias, false) + " NATURAL";
|
||||
planEntry.description.add() = "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan";
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
if (!level)
|
||||
plan += ")";
|
||||
}
|
||||
planEntry.objectType = m_relation->getObjectType();
|
||||
planEntry.objectName = m_relation->rel_name;
|
||||
|
||||
if (m_alias.hasData() && m_relation->rel_name != m_alias)
|
||||
planEntry.alias = m_alias;
|
||||
}
|
||||
|
@ -52,16 +52,12 @@ namespace
|
||||
public:
|
||||
BufferedStreamWindow(CompilerScratch* csb, BufferedStream* next);
|
||||
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
void close(thread_db* tdbb) const override;
|
||||
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
bool refetchRecord(thread_db* tdbb) const override;
|
||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
||||
|
||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
||||
|
||||
void print(thread_db* tdbb, Firebird::string& plan, bool detailed, unsigned level, bool recurse) const override;
|
||||
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||
|
||||
void markRecursive() override;
|
||||
void invalidateRecords(Request* request) const override;
|
||||
@ -87,6 +83,11 @@ namespace
|
||||
return impure->irsb_position;
|
||||
}
|
||||
|
||||
protected:
|
||||
void internalOpen(thread_db* tdbb) const override;
|
||||
bool internalGetRecord(thread_db* tdbb) const override;
|
||||
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||
|
||||
public:
|
||||
NestConst<BufferedStream> m_next;
|
||||
};
|
||||
@ -147,21 +148,23 @@ namespace
|
||||
return m_next->lockRecord(tdbb, skipLocked);
|
||||
}
|
||||
|
||||
void BufferedStreamWindow::getChildren(Array<const RecordSource*>& children) const
|
||||
void BufferedStreamWindow::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
children.add(m_next);
|
||||
m_next->getLegacyPlan(tdbb, plan, level);
|
||||
}
|
||||
|
||||
void BufferedStreamWindow::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
||||
void BufferedStreamWindow::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
if (detailed)
|
||||
{
|
||||
plan += printIndent(++level) + "Window Buffer";
|
||||
printOptInfo(plan);
|
||||
}
|
||||
planEntry.className = "BufferedStreamWindow";
|
||||
|
||||
planEntry.description.add() = "Window Buffer";
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
if (recurse)
|
||||
m_next->print(tdbb, plan, detailed, level, recurse);
|
||||
{
|
||||
++level;
|
||||
m_next->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||
}
|
||||
}
|
||||
|
||||
void BufferedStreamWindow::markRecursive()
|
||||
@ -404,21 +407,23 @@ WriteLockResult WindowedStream::lockRecord(thread_db* /*tdbb*/, bool /*skipLocke
|
||||
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
|
||||
}
|
||||
|
||||
void WindowedStream::getChildren(Array<const RecordSource*>& children) const
|
||||
void WindowedStream::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
children.add(m_joinedStream);
|
||||
m_joinedStream->getLegacyPlan(tdbb, plan, level);
|
||||
}
|
||||
|
||||
void WindowedStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
||||
void WindowedStream::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
if (detailed)
|
||||
{
|
||||
plan += printIndent(++level) + "Window";
|
||||
printOptInfo(plan);
|
||||
}
|
||||
planEntry.className = "WindowedStream";
|
||||
|
||||
planEntry.description.add() = "Window";
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
if (recurse)
|
||||
m_joinedStream->print(tdbb, plan, detailed, level, recurse);
|
||||
{
|
||||
++level;
|
||||
m_joinedStream->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||
}
|
||||
}
|
||||
|
||||
void WindowedStream::markRecursive()
|
||||
@ -899,22 +904,24 @@ bool WindowedStream::WindowStream::internalGetRecord(thread_db* tdbb) const
|
||||
return true;
|
||||
}
|
||||
|
||||
void WindowedStream::WindowStream::getChildren(Array<const RecordSource*>& children) const
|
||||
void WindowedStream::WindowStream::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||
{
|
||||
children.add(m_next);
|
||||
m_next->getLegacyPlan(tdbb, plan, level);
|
||||
}
|
||||
|
||||
void WindowedStream::WindowStream::print(thread_db* tdbb, string& plan, bool detailed,
|
||||
unsigned level, bool recurse) const
|
||||
|
||||
void WindowedStream::WindowStream::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||
{
|
||||
if (detailed)
|
||||
{
|
||||
plan += printIndent(++level) + "Window Partition";
|
||||
printOptInfo(plan);
|
||||
}
|
||||
planEntry.className = "WindowStream";
|
||||
|
||||
planEntry.description.add() = "Window Partition";
|
||||
printOptInfo(planEntry.description);
|
||||
|
||||
if (recurse)
|
||||
m_next->print(tdbb, plan, detailed, level, recurse);
|
||||
{
|
||||
++level;
|
||||
m_next->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||
}
|
||||
}
|
||||
|
||||
void WindowedStream::WindowStream::findUsedStreams(StreamList& streams, bool expandAll) const
|
||||
|
201
src/jrd/sys-packages/SqlPackage.cpp
Normal file
201
src/jrd/sys-packages/SqlPackage.cpp
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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) 2023 Adriano dos Santos Fernandes <adrianosf@gmail.com>
|
||||
* and all contributors signed below.
|
||||
*
|
||||
* All Rights Reserved.
|
||||
* Contributor(s): ______________________________________.
|
||||
*/
|
||||
|
||||
#include "firebird.h"
|
||||
#include "../jrd/sys-packages/SqlPackage.h"
|
||||
#include "../dsql/DsqlRequests.h"
|
||||
#include "../jrd/Statement.h"
|
||||
#include "../jrd/recsrc/RecordSource.h"
|
||||
#include "../dsql/dsql_proto.h"
|
||||
#include "../jrd/mov_proto.h"
|
||||
|
||||
using namespace Jrd;
|
||||
using namespace Firebird;
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
|
||||
IExternalResultSet* SqlPackage::explainProcedure(ThrowStatusExceptionWrapper* status,
|
||||
IExternalContext* context, const ExplainInput::Type* in, ExplainOutput::Type* out)
|
||||
{
|
||||
return FB_NEW ExplainResultSet(status, context, in, out);
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
|
||||
SqlPackage::ExplainResultSet::ExplainResultSet(ThrowStatusExceptionWrapper* status,
|
||||
IExternalContext* context, const ExplainInput::Type* in, ExplainOutput::Type* aOut)
|
||||
: out(aOut)
|
||||
{
|
||||
const auto tdbb = JRD_get_thread_data();
|
||||
const auto attachment = tdbb->getAttachment();
|
||||
const auto transaction = tdbb->getTransaction();
|
||||
|
||||
dsc sqlDesc;
|
||||
sqlDesc.makeBlob(isc_blob_text, CS_METADATA, const_cast<ISC_QUAD*>(&in->sql));
|
||||
MoveBuffer sqlBuffer;
|
||||
UCHAR* sqlAddress;
|
||||
ULONG sqlLength = MOV_make_string2(tdbb, &sqlDesc, CS_METADATA, &sqlAddress, sqlBuffer, false);
|
||||
|
||||
const auto dsqlRequest = DSQL_prepare(tdbb, attachment, transaction, sqlLength, (const char*) sqlAddress,
|
||||
SQL_DIALECT_CURRENT, 0, nullptr, nullptr, false);
|
||||
|
||||
Cleanup dsqlRequestCleanup([&]
|
||||
{
|
||||
DsqlRequest::destroy(tdbb, dsqlRequest);
|
||||
});
|
||||
|
||||
if (!dsqlRequest->getStatement())
|
||||
return;
|
||||
|
||||
PlanEntry rootEntry;
|
||||
dsqlRequest->getStatement()->getPlan(tdbb, rootEntry);
|
||||
|
||||
Array<NonPooledPair<const PlanEntry*, const PlanEntry*>> planList;
|
||||
rootEntry.asFlatList(planList);
|
||||
|
||||
unsigned planLine = 0;
|
||||
|
||||
for (const auto& [planEntry, parentPlanEntry] : planList)
|
||||
{
|
||||
if (planLine == 0)
|
||||
{
|
||||
++planLine;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& resultEntry = resultEntries.add();
|
||||
|
||||
resultEntry.planLineNull = FB_FALSE;
|
||||
resultEntry.planLine = planLine++;
|
||||
|
||||
resultEntry.recordSourceIdNull = FB_FALSE;
|
||||
resultEntry.recordSourceId = planEntry->accessPath->getRecSourceId();
|
||||
|
||||
resultEntry.parentRecordSourceIdNull = parentPlanEntry->accessPath ? FB_FALSE : FB_TRUE;
|
||||
if (parentPlanEntry->accessPath)
|
||||
resultEntry.parentRecordSourceId = parentPlanEntry->accessPath->getRecSourceId();
|
||||
|
||||
resultEntry.levelNull = FB_FALSE;
|
||||
resultEntry.level = planEntry->level;
|
||||
|
||||
resultEntry.objectTypeNull = !planEntry->objectType.has_value();
|
||||
if (planEntry->objectType.has_value())
|
||||
resultEntry.objectType = planEntry->objectType.value();
|
||||
|
||||
resultEntry.packageNameNull = planEntry->packageName.hasData() ? FB_FALSE : FB_TRUE;
|
||||
if (planEntry->packageName.hasData())
|
||||
resultEntry.packageName.set(planEntry->packageName.c_str(), planEntry->packageName.length());
|
||||
|
||||
resultEntry.objectNameNull = planEntry->objectName.hasData() ? FB_FALSE : FB_TRUE;
|
||||
if (planEntry->objectName.hasData())
|
||||
resultEntry.objectName.set(planEntry->objectName.c_str(), planEntry->objectName.length());
|
||||
|
||||
resultEntry.aliasNull = planEntry->alias.hasData() ? FB_FALSE : FB_TRUE;
|
||||
if (planEntry->alias.hasData())
|
||||
resultEntry.alias.set(planEntry->alias.c_str(), planEntry->alias.length());
|
||||
|
||||
resultEntry.cardinalityNull = planEntry->level > 0 ? FB_FALSE : FB_TRUE;
|
||||
resultEntry.cardinality = planEntry->accessPath->getCardinality();
|
||||
|
||||
resultEntry.recordLengthNull = planEntry->recordLength ? FB_FALSE : FB_TRUE;
|
||||
resultEntry.recordLength = planEntry->recordLength;
|
||||
|
||||
resultEntry.keyLengthNull = planEntry->keyLength ? FB_FALSE : FB_TRUE;
|
||||
resultEntry.keyLength = planEntry->keyLength;
|
||||
|
||||
string accessPath;
|
||||
planEntry->getDescriptionAsString(accessPath);
|
||||
|
||||
constexpr UCHAR bpb[] = {
|
||||
isc_bpb_version1,
|
||||
isc_bpb_storage, 1, isc_bpb_storage_temp
|
||||
};
|
||||
|
||||
bid blobId;
|
||||
const auto blob = blb::create2(tdbb, transaction, &blobId, sizeof(bpb), bpb);
|
||||
blob->BLB_put_data(tdbb, (const UCHAR*) accessPath.c_str(), accessPath.length());
|
||||
blob->BLB_close(tdbb);
|
||||
|
||||
resultEntry.accessPathNull = FB_FALSE;
|
||||
resultEntry.accessPath = blobId;
|
||||
}
|
||||
|
||||
resultIterator = resultEntries.begin();
|
||||
}
|
||||
|
||||
FB_BOOLEAN SqlPackage::ExplainResultSet::fetch(ThrowStatusExceptionWrapper* status)
|
||||
{
|
||||
if (resultIterator >= resultEntries.end())
|
||||
return false;
|
||||
|
||||
*out = *resultIterator++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
|
||||
SqlPackage::SqlPackage(MemoryPool& pool)
|
||||
: SystemPackage(
|
||||
pool,
|
||||
"RDB$SQL",
|
||||
ODS_13_2,
|
||||
// procedures
|
||||
{
|
||||
SystemProcedure(
|
||||
pool,
|
||||
"EXPLAIN",
|
||||
SystemProcedureFactory<ExplainInput, ExplainOutput, explainProcedure>(),
|
||||
prc_selectable,
|
||||
// input parameters
|
||||
{
|
||||
{"SQL", fld_description, false}
|
||||
},
|
||||
// output parameters
|
||||
{
|
||||
{"PLAN_LINE", fld_integer, false},
|
||||
{"RECORD_SOURCE_ID", fld_gen_val, false},
|
||||
{"PARENT_RECORD_SOURCE_ID", fld_gen_val, true},
|
||||
{"LEVEL", fld_integer, false},
|
||||
{"OBJECT_TYPE", fld_obj_type, true},
|
||||
{"PACKAGE_NAME", fld_r_name, true},
|
||||
{"OBJECT_NAME", fld_r_name, true},
|
||||
{"ALIAS", fld_r_name, true},
|
||||
{"CARDINALITY", fld_statistics, true},
|
||||
{"RECORD_LENGTH", fld_integer, true},
|
||||
{"KEY_LENGTH", fld_integer, true},
|
||||
{"ACCESS_PATH", fld_description, false}
|
||||
}
|
||||
),
|
||||
},
|
||||
// functions
|
||||
{
|
||||
}
|
||||
)
|
||||
{
|
||||
}
|
99
src/jrd/sys-packages/SqlPackage.h
Normal file
99
src/jrd/sys-packages/SqlPackage.h
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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) 2023 Adriano dos Santos Fernandes <adrianosf@gmail.com>
|
||||
* and all contributors signed below.
|
||||
*
|
||||
* All Rights Reserved.
|
||||
* Contributor(s): ______________________________________.
|
||||
*/
|
||||
|
||||
#ifndef JRD_SYS_PACKAGES_SQL_PACKAGE_H
|
||||
#define JRD_SYS_PACKAGES_SQL_PACKAGE_H
|
||||
|
||||
#include "firebird.h"
|
||||
#include "firebird/Message.h"
|
||||
#include "../common/classes/array.h"
|
||||
#include "../jrd/SystemPackages.h"
|
||||
|
||||
namespace Jrd {
|
||||
|
||||
|
||||
class SqlPackage final : public SystemPackage
|
||||
{
|
||||
public:
|
||||
SqlPackage(Firebird::MemoryPool& pool);
|
||||
|
||||
SqlPackage(const SqlPackage&) = delete;
|
||||
SqlPackage& operator=(const SqlPackage&) = delete;
|
||||
|
||||
private:
|
||||
FB_MESSAGE(ExplainInput, Firebird::ThrowStatusExceptionWrapper,
|
||||
(FB_BLOB, sql)
|
||||
);
|
||||
|
||||
FB_MESSAGE(ExplainOutput, Firebird::ThrowStatusExceptionWrapper,
|
||||
(FB_INTEGER, planLine)
|
||||
(FB_BIGINT, recordSourceId)
|
||||
(FB_BIGINT, parentRecordSourceId)
|
||||
(FB_INTEGER, level)
|
||||
(FB_SMALLINT, objectType)
|
||||
(FB_INTL_VARCHAR(METADATA_IDENTIFIER_CHAR_LEN * METADATA_BYTES_PER_CHAR, CS_METADATA), packageName)
|
||||
(FB_INTL_VARCHAR(METADATA_IDENTIFIER_CHAR_LEN * METADATA_BYTES_PER_CHAR, CS_METADATA), objectName)
|
||||
(FB_INTL_VARCHAR(METADATA_IDENTIFIER_CHAR_LEN * METADATA_BYTES_PER_CHAR, CS_METADATA), alias)
|
||||
(FB_DOUBLE, cardinality)
|
||||
(FB_INTEGER, recordLength)
|
||||
(FB_INTEGER, keyLength)
|
||||
(FB_BLOB, accessPath)
|
||||
);
|
||||
|
||||
class ExplainResultSet :
|
||||
public
|
||||
Firebird::DisposeIface<
|
||||
Firebird::IExternalResultSetImpl<
|
||||
ExplainResultSet,
|
||||
Firebird::ThrowStatusExceptionWrapper
|
||||
>
|
||||
>
|
||||
{
|
||||
public:
|
||||
ExplainResultSet(Firebird::ThrowStatusExceptionWrapper* status, Firebird::IExternalContext* context,
|
||||
const ExplainInput::Type* in, ExplainOutput::Type* out);
|
||||
|
||||
public:
|
||||
void dispose() override
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
public:
|
||||
FB_BOOLEAN fetch(Firebird::ThrowStatusExceptionWrapper* status) override;
|
||||
|
||||
private:
|
||||
ExplainOutput::Type* out;
|
||||
Firebird::Array<ExplainOutput::Type> resultEntries{*getDefaultMemoryPool()};
|
||||
Firebird::Array<ExplainOutput::Type>::const_iterator resultIterator = nullptr;
|
||||
};
|
||||
|
||||
//----------
|
||||
|
||||
static Firebird::IExternalResultSet* explainProcedure(Firebird::ThrowStatusExceptionWrapper* status,
|
||||
Firebird::IExternalContext* context, const ExplainInput::Type* in, ExplainOutput::Type* out);
|
||||
};
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif // JRD_SYS_PACKAGES_SQL_PACKAGE_H
|
Loading…
Reference in New Issue
Block a user