mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 20:03:02 +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
|
||||||
Engine_Objects:= $(call dirObjects,jrd) $(call dirObjects,dsql) $(call dirObjects,jrd/extds) \
|
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)
|
$(call makeObjects,lock,lock.cpp)
|
||||||
|
|
||||||
Engine_Test_Objects:= $(call dirObjects,jrd/tests)
|
Engine_Test_Objects:= $(call dirObjects,jrd/tests)
|
||||||
|
@ -161,6 +161,7 @@
|
|||||||
<ClCompile Include="..\..\..\src\jrd\sqz.cpp" />
|
<ClCompile Include="..\..\..\src\jrd\sqz.cpp" />
|
||||||
<ClCompile Include="..\..\..\src\jrd\Statement.cpp" />
|
<ClCompile Include="..\..\..\src\jrd\Statement.cpp" />
|
||||||
<ClCompile Include="..\..\..\src\jrd\svc.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\SysFunction.cpp" />
|
||||||
<ClCompile Include="..\..\..\src\jrd\SystemPackages.cpp" />
|
<ClCompile Include="..\..\..\src\jrd\SystemPackages.cpp" />
|
||||||
<ClCompile Include="..\..\..\src\jrd\TempSpace.cpp" />
|
<ClCompile Include="..\..\..\src\jrd\TempSpace.cpp" />
|
||||||
@ -345,6 +346,7 @@
|
|||||||
<ClInclude Include="..\..\..\src\jrd\status.h" />
|
<ClInclude Include="..\..\..\src\jrd\status.h" />
|
||||||
<ClInclude Include="..\..\..\src\jrd\svc.h" />
|
<ClInclude Include="..\..\..\src\jrd\svc.h" />
|
||||||
<ClInclude Include="..\..\..\src\jrd\svc_undoc.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\SysFunction.h" />
|
||||||
<ClInclude Include="..\..\..\src\jrd\SystemPackages.h" />
|
<ClInclude Include="..\..\..\src\jrd\SystemPackages.h" />
|
||||||
<ClInclude Include="..\..\..\src\jrd\TempSpace.h" />
|
<ClInclude Include="..\..\..\src\jrd\TempSpace.h" />
|
||||||
|
@ -378,6 +378,9 @@
|
|||||||
<ClCompile Include="..\..\..\src\jrd\svc.cpp">
|
<ClCompile Include="..\..\..\src\jrd\svc.cpp">
|
||||||
<Filter>JRD files</Filter>
|
<Filter>JRD files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\..\src\jrd\sys-packages\SqlPackage.cpp">
|
||||||
|
<Filter>JRD files</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\..\src\jrd\SysFunction.cpp">
|
<ClCompile Include="..\..\..\src\jrd\SysFunction.cpp">
|
||||||
<Filter>JRD files</Filter>
|
<Filter>JRD files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@ -1073,6 +1076,9 @@
|
|||||||
<ClInclude Include="..\..\..\src\dsql\DsqlBatch.h">
|
<ClInclude Include="..\..\..\src\dsql\DsqlBatch.h">
|
||||||
<Filter>Header files</Filter>
|
<Filter>Header files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\..\src\jrd\sys-packages\SqlPackage.h">
|
||||||
|
<Filter>Header files</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\..\src\jrd\SystemPackages.h">
|
<ClInclude Include="..\..\..\src\jrd\SystemPackages.h">
|
||||||
<Filter>Header files</Filter>
|
<Filter>Header files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -324,3 +324,32 @@ RDB$RELATIONS | 59| | | |
|
|||||||
|
|
||||||
-- turn per-table stats off, using shortened name
|
-- turn per-table stats off, using shortened name
|
||||||
SQL> SET PER_TAB OFF;
|
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());
|
return iterator(this, getCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator back()
|
T& front()
|
||||||
{
|
{
|
||||||
fb_assert(getCount() > 0);
|
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
|
const_iterator begin() const
|
||||||
|
@ -1708,16 +1708,16 @@ UnicodeUtil::Utf16Collation* UnicodeUtil::Utf16Collation::create(
|
|||||||
++secondKeyDataIt;
|
++secondKeyDataIt;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned backSize = commonKeys.back()->getCount();
|
unsigned backSize = commonKeys.back().getCount();
|
||||||
|
|
||||||
if (common > backSize)
|
if (common > backSize)
|
||||||
commonKeys.back()->append(secondKeyIt->begin() + backSize, common - backSize);
|
commonKeys.back().append(secondKeyIt->begin() + backSize, common - backSize);
|
||||||
else if (common < backSize)
|
else if (common < backSize)
|
||||||
{
|
{
|
||||||
if (common == 0)
|
if (common == 0)
|
||||||
commonKeys.push(*secondKeyIt);
|
commonKeys.push(*secondKeyIt);
|
||||||
else
|
else
|
||||||
commonKeys.back()->resize(common);
|
commonKeys.back().resize(common);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (++secondKeyIt != keySet.end())
|
if (++secondKeyIt != keySet.end())
|
||||||
|
@ -2505,7 +2505,7 @@ column_constraint_def($addColumnClause)
|
|||||||
: constraint_name_opt column_constraint($addColumnClause)
|
: constraint_name_opt column_constraint($addColumnClause)
|
||||||
{
|
{
|
||||||
if ($1)
|
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, 202, NO_PUBLICATIONS, "There is no publications in this database")
|
||||||
FB_IMPL_MSG_SYMBOL(ISQL, 203, MSG_PUBLICATIONS, "Publications:")
|
FB_IMPL_MSG_SYMBOL(ISQL, 203, MSG_PUBLICATIONS, "Publications:")
|
||||||
FB_IMPL_MSG_SYMBOL(ISQL, 204, MSG_PROCEDURES, "Procedures:")
|
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 bool get_numeric(const UCHAR*, USHORT, SSHORT*, SINT64*);
|
||||||
static void print_set(const char* str, bool v);
|
static void print_set(const char* str, bool v);
|
||||||
static processing_state print_sets();
|
static processing_state print_sets();
|
||||||
|
static processing_state explain(const TEXT*);
|
||||||
static processing_state help(const TEXT*);
|
static processing_state help(const TEXT*);
|
||||||
static bool isyesno(const TEXT*);
|
static bool isyesno(const TEXT*);
|
||||||
static processing_state newdb(TEXT*, const TEXT*, const TEXT*, int, const TEXT*, bool);
|
static processing_state newdb(TEXT*, const TEXT*, const TEXT*, int, const TEXT*, bool);
|
||||||
@ -587,6 +588,7 @@ public:
|
|||||||
KeepTranParams = true;
|
KeepTranParams = true;
|
||||||
TranParams->assign(DEFAULT_DML_TRANS_SQL);
|
TranParams->assign(DEFAULT_DML_TRANS_SQL);
|
||||||
PerTableStats = false;
|
PerTableStats = false;
|
||||||
|
ExplainCommand = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ColList global_Cols;
|
ColList global_Cols;
|
||||||
@ -611,6 +613,7 @@ public:
|
|||||||
SCHAR ISQL_charset[MAXCHARSET_SIZE];
|
SCHAR ISQL_charset[MAXCHARSET_SIZE];
|
||||||
bool KeepTranParams;
|
bool KeepTranParams;
|
||||||
bool PerTableStats;
|
bool PerTableStats;
|
||||||
|
bool ExplainCommand;
|
||||||
};
|
};
|
||||||
|
|
||||||
static SetValues setValues;
|
static SetValues setValues;
|
||||||
@ -5009,7 +5012,7 @@ static processing_state frontend(const TEXT* statement)
|
|||||||
{
|
{
|
||||||
show, add, copy,
|
show, add, copy,
|
||||||
blobview, output, shell, set, create, drop, connect,
|
blobview, output, shell, set, create, drop, connect,
|
||||||
edit, input, quit, exit, help,
|
edit, input, quit, exit, explain, help,
|
||||||
#ifdef DEV_BUILD
|
#ifdef DEV_BUILD
|
||||||
passthrough,
|
passthrough,
|
||||||
#endif
|
#endif
|
||||||
@ -5039,6 +5042,7 @@ static processing_state frontend(const TEXT* statement)
|
|||||||
{FrontOptions::input, "INPUT", 2},
|
{FrontOptions::input, "INPUT", 2},
|
||||||
{FrontOptions::quit, "QUIT", 0},
|
{FrontOptions::quit, "QUIT", 0},
|
||||||
{FrontOptions::exit, "EXIT", 0},
|
{FrontOptions::exit, "EXIT", 0},
|
||||||
|
{FrontOptions::explain, "EXPLAIN", 0},
|
||||||
{FrontOptions::help, "?", 0},
|
{FrontOptions::help, "?", 0},
|
||||||
{FrontOptions::help, "HELP", 0}
|
{FrontOptions::help, "HELP", 0}
|
||||||
#ifdef DEV_BUILD
|
#ifdef DEV_BUILD
|
||||||
@ -5236,6 +5240,10 @@ static processing_state frontend(const TEXT* statement)
|
|||||||
ret = EXIT;
|
ret = EXIT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case FrontOptions::explain:
|
||||||
|
ret = explain(cmd + 7);
|
||||||
|
break;
|
||||||
|
|
||||||
case FrontOptions::help:
|
case FrontOptions::help:
|
||||||
ret = help(parms[1]);
|
ret = help(parms[1]);
|
||||||
break;
|
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)
|
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_BLOBED, // BLOBVIEW <blobid> -- view BLOB in text editor
|
||||||
HLP_EDIT, // EDIT [<filename>] -- edit SQL script file and execute
|
HLP_EDIT, // EDIT [<filename>] -- edit SQL script file and execute
|
||||||
HLP_EDIT2, // EDIT -- edit current command buffer 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_HELP, // HELP -- display this menu
|
||||||
HLP_INPUT, // INput <filename> -- take input from the named SQL file
|
HLP_INPUT, // INput <filename> -- take input from the named SQL file
|
||||||
HLP_OUTPUT, // OUTput [<filename>] -- write output to named 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)
|
statement_type != isc_info_sql_stmt_set_generator)
|
||||||
{
|
{
|
||||||
process_plan();
|
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])
|
if (setValues.ExecPathDisplay[0])
|
||||||
|
@ -498,80 +498,31 @@ void ProfilerManager::prepareRecSource(thread_db* tdbb, Request* request, const
|
|||||||
|
|
||||||
fb_assert(profileStatement->definedCursors.exist(recordSource->getCursorId()));
|
fb_assert(profileStatement->definedCursors.exist(recordSource->getCursorId()));
|
||||||
|
|
||||||
struct PlanItem : PermanentStorage
|
PlanEntry rootEntry;
|
||||||
{
|
recordSource->getPlan(tdbb, rootEntry, 0, true);
|
||||||
explicit PlanItem(MemoryPool& p)
|
|
||||||
: PermanentStorage(p)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
const AccessPath* recordSource = nullptr;
|
Array<NonPooledPair<const PlanEntry*, const PlanEntry*>> flatPlan;
|
||||||
const AccessPath* parentRecordSource = nullptr;
|
rootEntry.asFlatList(flatPlan);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NonPooledMap<ULONG, ULONG> idSequenceMap;
|
NonPooledMap<ULONG, ULONG> idSequenceMap;
|
||||||
auto sequencePtr = profileStatement->cursorNextSequence.getOrPut(recordSource->getCursorId());
|
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 cursorId = planEntry->accessPath->getCursorId();
|
||||||
const auto recSourceId = planItem.recordSource->getRecSourceId();
|
const auto recSourceId = planEntry->accessPath->getRecSourceId();
|
||||||
idSequenceMap.put(recSourceId, ++*sequencePtr);
|
idSequenceMap.put(recSourceId, ++*sequencePtr);
|
||||||
|
|
||||||
ULONG parentSequence = 0;
|
ULONG parentSequence = 0;
|
||||||
|
|
||||||
if (planItem.parentRecordSource)
|
if (parentPlanEntry)
|
||||||
parentSequence = *idSequenceMap.get(planItem.parentRecordSource->getRecSourceId());
|
parentSequence = *idSequenceMap.get(parentPlanEntry->accessPath->getRecSourceId());
|
||||||
|
|
||||||
|
string accessPath;
|
||||||
|
planEntry->getDescriptionAsString(accessPath);
|
||||||
|
|
||||||
currentSession->pluginSession->defineRecordSource(profileStatement->id, cursorId,
|
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);
|
profileStatement->recSourceSequence.put(recSourceId, *sequencePtr);
|
||||||
}
|
}
|
||||||
|
@ -239,51 +239,33 @@ struct bid
|
|||||||
|
|
||||||
ULONG& bid_temp_id()
|
ULONG& bid_temp_id()
|
||||||
{
|
{
|
||||||
// Make sure that compiler packed structure like we wanted
|
|
||||||
fb_assert(sizeof(*this) == 8);
|
|
||||||
|
|
||||||
return bid_internal.bid_temp_id();
|
return bid_internal.bid_temp_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
ULONG bid_temp_id() const
|
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();
|
return bid_internal.bid_temp_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isEmpty() const
|
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;
|
return bid_quad.bid_quad_high == 0 && bid_quad.bid_quad_low == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear()
|
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_high = 0;
|
||||||
bid_quad.bid_quad_low = 0;
|
bid_quad.bid_quad_low = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_temporary(ULONG temp_id)
|
void set_temporary(ULONG temp_id)
|
||||||
{
|
{
|
||||||
// Make sure that compiler packed structure like we wanted
|
|
||||||
fb_assert(sizeof(*this) == 8);
|
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
bid_temp_id() = temp_id;
|
bid_temp_id() = temp_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_permanent(USHORT relation_id, RecordNumber num)
|
void set_permanent(USHORT relation_id, RecordNumber num)
|
||||||
{
|
{
|
||||||
// Make sure that compiler packed structure like we wanted
|
|
||||||
fb_assert(sizeof(*this) == 8);
|
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
bid_internal.bid_relation_id = relation_id;
|
bid_internal.bid_relation_id = relation_id;
|
||||||
num.bid_encode(&bid_internal);
|
num.bid_encode(&bid_internal);
|
||||||
@ -291,19 +273,18 @@ struct bid
|
|||||||
|
|
||||||
RecordNumber get_permanent_number() const
|
RecordNumber get_permanent_number() const
|
||||||
{
|
{
|
||||||
// Make sure that compiler packed structure like we wanted
|
|
||||||
fb_assert(sizeof(*this) == 8);
|
|
||||||
|
|
||||||
RecordNumber temp;
|
RecordNumber temp;
|
||||||
temp.bid_decode(&bid_internal);
|
temp.bid_decode(&bid_internal);
|
||||||
return temp;
|
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
|
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 &&
|
return bid_quad.bid_quad_high == other.bid_quad.bid_quad_high &&
|
||||||
bid_quad.bid_quad_low == other.bid_quad.bid_quad_low;
|
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
|
} // namespace Jrd
|
||||||
|
|
||||||
|
|
||||||
|
@ -275,6 +275,11 @@ public:
|
|||||||
bool isVirtual() const;
|
bool isVirtual() const;
|
||||||
bool isView() const;
|
bool isView() const;
|
||||||
|
|
||||||
|
ObjectType getObjectType() const
|
||||||
|
{
|
||||||
|
return isView() ? obj_view : obj_relation;
|
||||||
|
}
|
||||||
|
|
||||||
bool isReplicating(thread_db* tdbb);
|
bool isReplicating(thread_db* tdbb);
|
||||||
|
|
||||||
// global temporary relations attributes
|
// global temporary relations attributes
|
||||||
|
@ -730,6 +730,15 @@ string Statement::getPlan(thread_db* tdbb, bool detailed) const
|
|||||||
return plan;
|
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.
|
// Check that we have enough rights to access all resources this list of triggers touches.
|
||||||
void Statement::verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation,
|
void Statement::verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation,
|
||||||
TrigVector* triggers, MetaName userName)
|
TrigVector* triggers, MetaName userName)
|
||||||
|
@ -28,6 +28,8 @@
|
|||||||
|
|
||||||
namespace Jrd {
|
namespace Jrd {
|
||||||
|
|
||||||
|
class PlanEntry;
|
||||||
|
|
||||||
// Compiled statement.
|
// Compiled statement.
|
||||||
class Statement : public pool_alloc<type_req>
|
class Statement : public pool_alloc<type_req>
|
||||||
{
|
{
|
||||||
@ -78,6 +80,7 @@ public:
|
|||||||
void release(thread_db* tdbb);
|
void release(thread_db* tdbb);
|
||||||
|
|
||||||
Firebird::string getPlan(thread_db* tdbb, bool detailed) const;
|
Firebird::string getPlan(thread_db* tdbb, bool detailed) const;
|
||||||
|
void getPlan(thread_db* tdbb, PlanEntry& planEntry) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation, TrigVector* triggers,
|
static void verifyTriggerAccess(thread_db* tdbb, jrd_rel* ownerRelation, TrigVector* triggers,
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "../jrd/BlobUtil.h"
|
#include "../jrd/BlobUtil.h"
|
||||||
#include "../jrd/TimeZone.h"
|
#include "../jrd/TimeZone.h"
|
||||||
#include "../jrd/ProfilerManager.h"
|
#include "../jrd/ProfilerManager.h"
|
||||||
|
#include "../jrd/sys-packages/SqlPackage.h"
|
||||||
|
|
||||||
using namespace Firebird;
|
using namespace Firebird;
|
||||||
using namespace Jrd;
|
using namespace Jrd;
|
||||||
@ -40,6 +41,7 @@ namespace
|
|||||||
list->add(TimeZonePackage(pool));
|
list->add(TimeZonePackage(pool));
|
||||||
list->add(ProfilerPackage(pool));
|
list->add(ProfilerPackage(pool));
|
||||||
list->add(BlobUtilPackage(pool));
|
list->add(BlobUtilPackage(pool));
|
||||||
|
list->add(SqlPackage(pool));
|
||||||
}
|
}
|
||||||
|
|
||||||
static InitInstance<SystemPackagesInit> INSTANCE;
|
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_12_0 = ENCODE_ODS(ODS_VERSION12, 0);
|
||||||
const USHORT ODS_13_0 = ENCODE_ODS(ODS_VERSION13, 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_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;
|
const USHORT ODS_FIREBIRD_FLAG = 0x8000;
|
||||||
|
|
||||||
|
@ -1970,7 +1970,7 @@ unsigned Optimizer::distributeEqualities(BoolExprNodeStack& orgStack, unsigned b
|
|||||||
ValueExprNodeStack& s = classes.add();
|
ValueExprNodeStack& s = classes.add();
|
||||||
s.push(node1);
|
s.push(node1);
|
||||||
s.push(node2);
|
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);
|
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)
|
planEntry.className = "AggregatedStream";
|
||||||
{
|
|
||||||
plan += printIndent(++level) + "Aggregate";
|
planEntry.description.add() = "Aggregate";
|
||||||
printOptInfo(plan);
|
printOptInfo(planEntry.description);
|
||||||
}
|
|
||||||
|
|
||||||
if (recurse)
|
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
|
bool AggregatedStream::internalGetRecord(thread_db* tdbb) const
|
||||||
|
@ -122,23 +122,8 @@ bool BitmapTableScan::internalGetRecord(thread_db* tdbb) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BitmapTableScan::getChildren(Array<const RecordSource*>& children) const
|
void BitmapTableScan::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||||
{
|
{
|
||||||
}
|
|
||||||
|
|
||||||
void BitmapTableScan::print(thread_db* tdbb, string& plan,
|
|
||||||
bool detailed, unsigned level, bool recurse) const
|
|
||||||
{
|
|
||||||
if (detailed)
|
|
||||||
{
|
|
||||||
plan += printIndent(++level) + "Table " +
|
|
||||||
printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Access By ID";
|
|
||||||
|
|
||||||
printOptInfo(plan);
|
|
||||||
printInversion(tdbb, m_inversion, plan, true, level);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!level)
|
if (!level)
|
||||||
plan += "(";
|
plan += "(";
|
||||||
|
|
||||||
@ -149,5 +134,20 @@ void BitmapTableScan::print(thread_db* tdbb, string& plan,
|
|||||||
|
|
||||||
if (!level)
|
if (!level)
|
||||||
plan += ")";
|
plan += ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BitmapTableScan::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||||
|
{
|
||||||
|
planEntry.className = "BitmapTableScan";
|
||||||
|
|
||||||
|
planEntry.description.add() = "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Access By ID";
|
||||||
|
printOptInfo(planEntry.description);
|
||||||
|
|
||||||
|
printInversion(tdbb, m_inversion, planEntry.description, true);
|
||||||
|
|
||||||
|
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);
|
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)
|
planEntry.className = "BufferedStream";
|
||||||
{
|
|
||||||
string extras;
|
string extras;
|
||||||
extras.printf(" (record length: %" ULONGFORMAT")", m_format->fmt_length);
|
extras.printf(" (record length: %" ULONGFORMAT")", m_format->fmt_length);
|
||||||
|
|
||||||
plan += printIndent(++level) + "Record Buffer" + extras;
|
planEntry.description.add() = "Record Buffer" + extras;
|
||||||
printOptInfo(plan);
|
printOptInfo(planEntry.description);
|
||||||
}
|
|
||||||
|
planEntry.recordLength = m_format->fmt_length;
|
||||||
|
|
||||||
if (recurse)
|
if (recurse)
|
||||||
m_next->print(tdbb, plan, detailed, level, recurse);
|
m_next->getPlan(tdbb, planEntry.children.add(), ++level, recurse);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferedStream::markRecursive()
|
void BufferedStream::markRecursive()
|
||||||
|
@ -114,38 +114,33 @@ WriteLockResult ConditionalStream::lockRecord(thread_db* tdbb, bool skipLocked)
|
|||||||
return impure->irsb_next->lockRecord(tdbb, 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConditionalStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
|
||||||
{
|
|
||||||
if (detailed)
|
|
||||||
{
|
|
||||||
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)
|
if (!level)
|
||||||
plan += "(";
|
plan += "(";
|
||||||
|
|
||||||
m_first->print(tdbb, plan, false, level + 1, recurse);
|
m_first->getLegacyPlan(tdbb, plan, level + 1);
|
||||||
|
|
||||||
plan += ", ";
|
plan += ", ";
|
||||||
|
|
||||||
m_second->print(tdbb, plan, false, level + 1, recurse);
|
m_second->getLegacyPlan(tdbb, plan, level + 1);
|
||||||
|
|
||||||
if (!level)
|
if (!level)
|
||||||
plan += ")";
|
plan += ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConditionalStream::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||||
|
{
|
||||||
|
planEntry.className = "ConditionalStream";
|
||||||
|
|
||||||
|
planEntry.description.add() = "Condition";
|
||||||
|
printOptInfo(planEntry.description);
|
||||||
|
|
||||||
|
if (recurse)
|
||||||
|
{
|
||||||
|
++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())
|
if (m_rse->isInvariant())
|
||||||
plan += " (invariant)";
|
planEntry.description.back() += " (invariant)";
|
||||||
}
|
}
|
||||||
else if (m_cursorName.hasData())
|
else if (m_cursorName.hasData())
|
||||||
{
|
{
|
||||||
plan += "\nCursor \"" + string(m_cursorName) + "\"";
|
planEntry.description.add() = "Cursor \"" + string(m_cursorName) + "\"";
|
||||||
|
|
||||||
if (m_rse->isScrollable())
|
if (m_rse->isScrollable())
|
||||||
plan += " (scrollable)";
|
planEntry.description.back() += " (scrollable)";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
plan += "\nSelect Expression";
|
planEntry.description.add() = "Select Expression";
|
||||||
|
|
||||||
if (m_line || m_column)
|
if (m_line || m_column)
|
||||||
{
|
{
|
||||||
string pos;
|
string pos;
|
||||||
pos.printf(" (line %u, column %u)", m_line, m_column);
|
pos.printf(" (line %u, column %u)", m_line, m_column);
|
||||||
plan += pos;
|
planEntry.description.back() += pos;
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (m_line || m_column)
|
|
||||||
{
|
|
||||||
string pos;
|
|
||||||
pos.printf("\n-- line %u, column %u", m_line, m_column);
|
|
||||||
plan += pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
plan += "\nPLAN ";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recurse)
|
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 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
|
void printPlan(thread_db* tdbb, Firebird::string& plan, bool detailed) const
|
||||||
{
|
{
|
||||||
print(tdbb, plan, detailed, 0, true);
|
if (detailed)
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
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 open(thread_db* tdbb) const = 0;
|
||||||
virtual void close(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:
|
protected:
|
||||||
const RecordSource* const m_root;
|
const RecordSource* const m_root;
|
||||||
const RseNode* const m_rse;
|
const RseNode* const m_rse;
|
||||||
|
@ -115,21 +115,8 @@ WriteLockResult ExternalTableScan::lockRecord(thread_db* tdbb, bool skipLocked)
|
|||||||
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
|
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
|
||||||
{
|
{
|
||||||
}
|
|
||||||
|
|
||||||
void ExternalTableScan::print(thread_db* tdbb, string& plan,
|
|
||||||
bool detailed, 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)
|
if (!level)
|
||||||
plan += "(";
|
plan += "(";
|
||||||
|
|
||||||
@ -137,5 +124,18 @@ void ExternalTableScan::print(thread_db* tdbb, string& plan,
|
|||||||
|
|
||||||
if (!level)
|
if (!level)
|
||||||
plan += ")";
|
plan += ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExternalTableScan::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||||
|
{
|
||||||
|
planEntry.className = "ExternalTableScan";
|
||||||
|
|
||||||
|
planEntry.description.add() = "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan";
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
@ -116,25 +116,24 @@ WriteLockResult FilteredStream::lockRecord(thread_db* tdbb, bool skipLocked) con
|
|||||||
return m_next->lockRecord(tdbb, skipLocked);
|
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)
|
planEntry.className = "FilteredStream";
|
||||||
{
|
|
||||||
plan += printIndent(++level) + "Filter";
|
planEntry.description.add() = "Filter";
|
||||||
|
|
||||||
if (m_invariant)
|
if (m_invariant)
|
||||||
plan += " (preliminary)";
|
planEntry.description.back() += " (preliminary)";
|
||||||
|
|
||||||
printOptInfo(plan);
|
printOptInfo(planEntry.description);
|
||||||
}
|
|
||||||
|
|
||||||
if (recurse)
|
if (recurse)
|
||||||
m_next->print(tdbb, plan, detailed, level, recurse);
|
m_next->getPlan(tdbb, planEntry.children.add(), ++level, recurse);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilteredStream::markRecursive()
|
void FilteredStream::markRecursive()
|
||||||
|
@ -120,21 +120,23 @@ WriteLockResult FirstRowsStream::lockRecord(thread_db* tdbb, bool skipLocked) co
|
|||||||
return m_next->lockRecord(tdbb, skipLocked);
|
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)
|
planEntry.className = "FirstRowsStream";
|
||||||
{
|
|
||||||
plan += printIndent(++level) + "First N Records";
|
planEntry.description.add() = "First N Records";
|
||||||
printOptInfo(plan);
|
printOptInfo(planEntry.description);
|
||||||
}
|
|
||||||
|
|
||||||
if (recurse)
|
if (recurse)
|
||||||
m_next->print(tdbb, plan, detailed, level, recurse);
|
{
|
||||||
|
++level;
|
||||||
|
m_next->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FirstRowsStream::markRecursive()
|
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));
|
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);
|
level++;
|
||||||
children.add(m_arg2);
|
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)
|
planEntry.className = "FullOuterJoin";
|
||||||
{
|
|
||||||
plan += printIndent(++level) + "Full Outer Join";
|
planEntry.description.add() = "Full Outer Join";
|
||||||
|
printOptInfo(planEntry.description);
|
||||||
|
|
||||||
if (recurse)
|
if (recurse)
|
||||||
{
|
{
|
||||||
m_arg1->print(tdbb, plan, true, level, recurse);
|
++level;
|
||||||
m_arg2->print(tdbb, plan, true, level, recurse);
|
m_arg1->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||||
}
|
m_arg2->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
level++;
|
|
||||||
plan += "JOIN (";
|
|
||||||
m_arg1->print(tdbb, plan, false, level, recurse);
|
|
||||||
plan += ", ";
|
|
||||||
m_arg2->print(tdbb, plan, false, level, recurse);
|
|
||||||
plan += ")";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,14 +162,21 @@ bool FullTableScan::internalGetRecord(thread_db* tdbb) const
|
|||||||
return false;
|
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;
|
auto lowerBounds = 0, upperBounds = 0;
|
||||||
for (const auto range : m_dbkeyRanges)
|
for (const auto range : m_dbkeyRanges)
|
||||||
{
|
{
|
||||||
@ -188,18 +195,12 @@ void FullTableScan::print(thread_db* tdbb, string& plan, bool detailed, unsigned
|
|||||||
else if (upperBounds)
|
else if (upperBounds)
|
||||||
bounds += " (upper bound)";
|
bounds += " (upper bound)";
|
||||||
|
|
||||||
plan += printIndent(++level) + "Table " +
|
planEntry.description.add() = "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan" + bounds;
|
||||||
printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan" + bounds;
|
printOptInfo(planEntry.description);
|
||||||
printOptInfo(plan);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!level)
|
|
||||||
plan += "(";
|
|
||||||
|
|
||||||
plan += printName(tdbb, m_alias, false) + " NATURAL";
|
planEntry.objectType = m_relation->getObjectType();
|
||||||
|
planEntry.objectName = m_relation->rel_name;
|
||||||
|
|
||||||
if (!level)
|
if (m_alias.hasData() && m_relation->rel_name != m_alias)
|
||||||
plan += ")";
|
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));
|
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);
|
|
||||||
|
|
||||||
for (FB_SIZE_T i = 0; i < m_args.getCount(); i++)
|
|
||||||
children.add(m_args[i].source);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HashJoin::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
|
||||||
{
|
|
||||||
if (detailed)
|
|
||||||
{
|
|
||||||
plan += printIndent(++level) + "Hash Join (inner)";
|
|
||||||
printOptInfo(plan);
|
|
||||||
|
|
||||||
if (recurse)
|
|
||||||
{
|
|
||||||
m_leader.source->print(tdbb, plan, true, 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++;
|
level++;
|
||||||
plan += "HASH (";
|
plan += "HASH (";
|
||||||
m_leader.source->print(tdbb, plan, false, level, recurse);
|
m_leader.source->getLegacyPlan(tdbb, plan, level);
|
||||||
plan += ", ";
|
plan += ", ";
|
||||||
for (FB_SIZE_T i = 0; i < m_args.getCount(); i++)
|
for (FB_SIZE_T i = 0; i < m_args.getCount(); i++)
|
||||||
{
|
{
|
||||||
if (i)
|
if (i)
|
||||||
plan += ", ";
|
plan += ", ";
|
||||||
|
|
||||||
m_args[i].source->print(tdbb, plan, false, level, recurse);
|
m_args[i].source->getLegacyPlan(tdbb, plan, level);
|
||||||
}
|
}
|
||||||
plan += ")";
|
plan += ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
void HashJoin::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||||
|
{
|
||||||
|
planEntry.className = "HashJoin";
|
||||||
|
|
||||||
|
planEntry.description.add() = "Hash Join (inner)";
|
||||||
|
printOptInfo(planEntry.description);
|
||||||
|
|
||||||
|
if (recurse)
|
||||||
|
{
|
||||||
|
++level;
|
||||||
|
|
||||||
|
m_leader.source->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||||
|
|
||||||
|
for (const auto& arg : m_args)
|
||||||
|
arg.source->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,25 +352,8 @@ bool IndexTableScan::internalGetRecord(thread_db* tdbb) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IndexTableScan::getChildren(Array<const RecordSource*>& children) const
|
void IndexTableScan::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) const
|
||||||
{
|
{
|
||||||
}
|
|
||||||
|
|
||||||
void IndexTableScan::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
|
||||||
{
|
|
||||||
if (detailed)
|
|
||||||
{
|
|
||||||
plan += printIndent(++level) + "Table " +
|
|
||||||
printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Access By ID";
|
|
||||||
|
|
||||||
printOptInfo(plan);
|
|
||||||
printInversion(tdbb, m_index, plan, true, level, true);
|
|
||||||
|
|
||||||
if (m_inversion)
|
|
||||||
printInversion(tdbb, m_inversion, plan, true, ++level);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!level)
|
if (!level)
|
||||||
plan += "(";
|
plan += "(";
|
||||||
|
|
||||||
@ -389,7 +372,25 @@ void IndexTableScan::print(thread_db* tdbb, string& plan, bool detailed, unsigne
|
|||||||
|
|
||||||
if (!level)
|
if (!level)
|
||||||
plan += ")";
|
plan += ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IndexTableScan::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||||
|
{
|
||||||
|
planEntry.className = "IndexTableScan";
|
||||||
|
|
||||||
|
planEntry.description.add() = "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Access By ID";
|
||||||
|
printOptInfo(planEntry.description);
|
||||||
|
|
||||||
|
printInversion(tdbb, m_index, planEntry.description, true, true);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (m_inversion)
|
||||||
|
printInversion(tdbb, m_inversion, planEntry.description, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
int IndexTableScan::compareKeys(const index_desc* idx,
|
int IndexTableScan::compareKeys(const index_desc* idx,
|
||||||
|
@ -71,10 +71,6 @@ void LocalTableStream::close(thread_db* tdbb) const
|
|||||||
impure->irsb_flags &= ~irsb_open;
|
impure->irsb_flags &= ~irsb_open;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalTableStream::getChildren(Array<const RecordSource*>& children) const
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LocalTableStream::internalGetRecord(thread_db* tdbb) const
|
bool LocalTableStream::internalGetRecord(thread_db* tdbb) const
|
||||||
{
|
{
|
||||||
JRD_reschedule(tdbb);
|
JRD_reschedule(tdbb);
|
||||||
@ -113,17 +109,10 @@ WriteLockResult LocalTableStream::lockRecord(thread_db* tdbb, bool skipLocked) c
|
|||||||
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
|
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.
|
//// TODO: Use Local Table name/alias.
|
||||||
|
|
||||||
if (detailed)
|
|
||||||
{
|
|
||||||
plan += printIndent(++level) + "Local Table Full Scan";
|
|
||||||
printOptInfo(plan);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!level)
|
if (!level)
|
||||||
plan += "(";
|
plan += "(";
|
||||||
|
|
||||||
@ -132,5 +121,14 @@ void LocalTableStream::print(thread_db* tdbb, string& plan, bool detailed, unsig
|
|||||||
|
|
||||||
if (!level)
|
if (!level)
|
||||||
plan += ")";
|
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);
|
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)
|
planEntry.className = "LockedStream";
|
||||||
{
|
|
||||||
plan += printIndent(++level) + "Write Lock";
|
planEntry.description.add() = "Write Lock";
|
||||||
printOptInfo(plan);
|
printOptInfo(planEntry.description);
|
||||||
}
|
|
||||||
|
|
||||||
if (recurse)
|
if (recurse)
|
||||||
m_next->print(tdbb, plan, detailed, level, recurse);
|
{
|
||||||
|
++level;
|
||||||
|
m_next->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LockedStream::markRecursive()
|
void LockedStream::markRecursive()
|
||||||
|
@ -345,27 +345,8 @@ WriteLockResult MergeJoin::lockRecord(thread_db* /*tdbb*/, bool /*skipLocked*/)
|
|||||||
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
|
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
|
||||||
{
|
{
|
||||||
for (FB_SIZE_T i = 0; i < m_args.getCount(); i++)
|
|
||||||
children.add(m_args[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MergeJoin::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
|
||||||
{
|
|
||||||
if (detailed)
|
|
||||||
{
|
|
||||||
plan += printIndent(++level) + "Merge Join (inner)";
|
|
||||||
printOptInfo(plan);
|
|
||||||
|
|
||||||
if (recurse)
|
|
||||||
{
|
|
||||||
for (FB_SIZE_T i = 0; i < m_args.getCount(); i++)
|
|
||||||
m_args[i]->print(tdbb, plan, true, level, recurse);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
level++;
|
level++;
|
||||||
plan += "MERGE (";
|
plan += "MERGE (";
|
||||||
for (FB_SIZE_T i = 0; i < m_args.getCount(); i++)
|
for (FB_SIZE_T i = 0; i < m_args.getCount(); i++)
|
||||||
@ -373,9 +354,24 @@ void MergeJoin::print(thread_db* tdbb, string& plan, bool detailed, unsigned lev
|
|||||||
if (i)
|
if (i)
|
||||||
plan += ", ";
|
plan += ", ";
|
||||||
|
|
||||||
m_args[i]->print(tdbb, plan, false, level, recurse);
|
m_args[i]->getLegacyPlan(tdbb, plan, level);
|
||||||
}
|
}
|
||||||
plan += ")";
|
plan += ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
void MergeJoin::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||||
|
{
|
||||||
|
planEntry.className = "MergeJoin";
|
||||||
|
|
||||||
|
planEntry.description.add() = "Merge Join (inner)";
|
||||||
|
printOptInfo(planEntry.description);
|
||||||
|
|
||||||
|
if (recurse)
|
||||||
|
{
|
||||||
|
++level;
|
||||||
|
|
||||||
|
for (const auto arg : m_args)
|
||||||
|
arg->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,51 +208,9 @@ WriteLockResult NestedLoopJoin::lockRecord(thread_db* /*tdbb*/, bool /*skipLocke
|
|||||||
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
|
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
|
||||||
}
|
}
|
||||||
|
|
||||||
void NestedLoopJoin::getChildren(Array<const RecordSource*>& children) const
|
void NestedLoopJoin::getLegacyPlan(thread_db* tdbb, string& plan, unsigned level) 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
|
|
||||||
{
|
{
|
||||||
if (m_args.hasData())
|
if (m_args.hasData())
|
||||||
{
|
|
||||||
if (detailed)
|
|
||||||
{
|
|
||||||
plan += printIndent(++level) + "Nested Loop Join ";
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
level++;
|
level++;
|
||||||
plan += "JOIN (";
|
plan += "JOIN (";
|
||||||
@ -261,10 +219,48 @@ void NestedLoopJoin::print(thread_db* tdbb, string& plan, bool detailed, unsigne
|
|||||||
if (i)
|
if (i)
|
||||||
plan += ", ";
|
plan += ", ";
|
||||||
|
|
||||||
m_args[i]->print(tdbb, plan, false, level, recurse);
|
m_args[i]->getLegacyPlan(tdbb, plan, level);
|
||||||
}
|
}
|
||||||
plan += ")";
|
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,20 +249,8 @@ WriteLockResult ProcedureScan::lockRecord(thread_db* /*tdbb*/, bool /*skipLocked
|
|||||||
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
|
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
|
||||||
{
|
{
|
||||||
}
|
|
||||||
|
|
||||||
void ProcedureScan::print(thread_db* tdbb, string& plan, bool detailed, 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)
|
if (!level)
|
||||||
plan += "(";
|
plan += "(";
|
||||||
|
|
||||||
@ -270,7 +258,21 @@ void ProcedureScan::print(thread_db* tdbb, string& plan, bool detailed, unsigned
|
|||||||
|
|
||||||
if (!level)
|
if (!level)
|
||||||
plan += ")";
|
plan += ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProcedureScan::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||||
|
{
|
||||||
|
planEntry.className = "ProcedureScan";
|
||||||
|
|
||||||
|
planEntry.description.add() = "Procedure " + printName(tdbb, m_procedure->getName().toString(), m_alias) + " Scan";
|
||||||
|
printOptInfo(planEntry.description);
|
||||||
|
|
||||||
|
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,
|
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
|
// Record source class
|
||||||
// -------------------
|
// -------------------
|
||||||
@ -98,40 +154,32 @@ string RecordSource::printName(thread_db* tdbb, const string& name, const string
|
|||||||
return result;
|
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,
|
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)
|
const bool wasEmpty = planLines.isEmpty();
|
||||||
plan += printIndent(++level);
|
auto plan = &planLines.add();
|
||||||
|
|
||||||
switch (inversion->type)
|
switch (inversion->type)
|
||||||
{
|
{
|
||||||
case InversionNode::TYPE_AND:
|
case InversionNode::TYPE_AND:
|
||||||
if (detailed)
|
if (detailed)
|
||||||
plan += "Bitmap And";
|
*plan += "Bitmap And";
|
||||||
printInversion(tdbb, inversion->node1, plan, detailed, level);
|
printInversion(tdbb, inversion->node1, planLines, detailed);
|
||||||
printInversion(tdbb, inversion->node2, plan, detailed, level);
|
printInversion(tdbb, inversion->node2, planLines, detailed);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case InversionNode::TYPE_OR:
|
case InversionNode::TYPE_OR:
|
||||||
case InversionNode::TYPE_IN:
|
case InversionNode::TYPE_IN:
|
||||||
if (detailed)
|
if (detailed)
|
||||||
plan += "Bitmap Or";
|
*plan += "Bitmap Or";
|
||||||
printInversion(tdbb, inversion->node1, plan, detailed, level);
|
printInversion(tdbb, inversion->node1, planLines, detailed);
|
||||||
printInversion(tdbb, inversion->node2, plan, detailed, level);
|
printInversion(tdbb, inversion->node2, planLines, detailed);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case InversionNode::TYPE_DBKEY:
|
case InversionNode::TYPE_DBKEY:
|
||||||
if (detailed)
|
if (detailed)
|
||||||
plan += "DBKEY";
|
*plan += "DBKEY";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case InversionNode::TYPE_INDEX:
|
case InversionNode::TYPE_INDEX:
|
||||||
@ -147,7 +195,10 @@ void RecordSource::printInversion(thread_db* tdbb, const InversionNode* inversio
|
|||||||
if (detailed)
|
if (detailed)
|
||||||
{
|
{
|
||||||
if (!navigation)
|
if (!navigation)
|
||||||
plan += "Bitmap" + printIndent(++level);
|
{
|
||||||
|
*plan += "Bitmap";
|
||||||
|
plan = &planLines.add();
|
||||||
|
}
|
||||||
|
|
||||||
const index_desc& idx = retrieval->irb_desc;
|
const index_desc& idx = retrieval->irb_desc;
|
||||||
const bool uniqueIdx = (idx.idx_flags & idx_unique);
|
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;
|
(fullscan ? " Full" : unique ? " Unique" : list ? " List" : " Range") + " Scan" + bounds;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
*plan += (wasEmpty ? "" : ", ") + printName(tdbb, indexName.c_str(), false);
|
||||||
plan += (plan.hasData() ? ", " : "") + printName(tdbb, indexName.c_str(), false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
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
|
#ifdef PRINT_OPT_INFO
|
||||||
|
fb_assert(planLines.hasData());
|
||||||
string info;
|
string info;
|
||||||
// Add 0.5 to convert double->int truncation into rounding
|
// Add 0.5 to convert double->int truncation into rounding
|
||||||
info.printf(" [rows: %" UQUADFORMAT "]", (FB_UINT64) (m_cardinality + 0.5));
|
info.printf(" [rows: %" UQUADFORMAT "]", (FB_UINT64) (m_cardinality + 0.5));
|
||||||
plan += info;
|
planLines.back() += info;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#ifndef JRD_RECORD_SOURCE_H
|
#ifndef JRD_RECORD_SOURCE_H
|
||||||
#define JRD_RECORD_SOURCE_H
|
#define JRD_RECORD_SOURCE_H
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include "../common/classes/array.h"
|
#include "../common/classes/array.h"
|
||||||
#include "../common/classes/objects_array.h"
|
#include "../common/classes/objects_array.h"
|
||||||
#include "../common/classes/NestConst.h"
|
#include "../common/classes/NestConst.h"
|
||||||
@ -50,6 +51,7 @@ namespace Jrd
|
|||||||
struct win;
|
struct win;
|
||||||
class BaseBufferedStream;
|
class BaseBufferedStream;
|
||||||
class BufferedStream;
|
class BufferedStream;
|
||||||
|
class PlanEntry;
|
||||||
|
|
||||||
enum JoinType { INNER_JOIN, OUTER_JOIN, SEMI_JOIN, ANTI_JOIN };
|
enum JoinType { INNER_JOIN, OUTER_JOIN, SEMI_JOIN, ANTI_JOIN };
|
||||||
|
|
||||||
@ -71,16 +73,56 @@ namespace Jrd
|
|||||||
return m_recSourceId;
|
return m_recSourceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void print(thread_db* tdbb, Firebird::string& plan,
|
double getCardinality() const
|
||||||
bool detailed, unsigned level, bool recurse) const = 0;
|
{
|
||||||
|
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:
|
private:
|
||||||
const ULONG m_cursorId;
|
const ULONG m_cursorId;
|
||||||
const ULONG m_recSourceId;
|
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.
|
// Abstract base class for record sources.
|
||||||
class RecordSource : public AccessPath
|
class RecordSource : public AccessPath
|
||||||
{
|
{
|
||||||
@ -106,11 +148,6 @@ namespace Jrd
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
double getCardinality() const
|
|
||||||
{
|
|
||||||
return m_cardinality;
|
|
||||||
}
|
|
||||||
|
|
||||||
void open(thread_db* tdbb) const;
|
void open(thread_db* tdbb) const;
|
||||||
|
|
||||||
bool getRecord(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,
|
static Firebird::string printName(thread_db* tdbb, const Firebird::string& name,
|
||||||
const Firebird::string& alias);
|
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,
|
static void printInversion(thread_db* tdbb, const InversionNode* inversion,
|
||||||
Firebird::string& plan, bool detailed,
|
Firebird::string& plan, bool detailed,
|
||||||
unsigned level, bool navigation = false);
|
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 saveRecord(thread_db* tdbb, record_param* rpb);
|
||||||
static void restoreRecord(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 void internalOpen(thread_db* tdbb) const = 0;
|
||||||
virtual bool internalGetRecord(thread_db* tdbb) const = 0;
|
virtual bool internalGetRecord(thread_db* tdbb) const = 0;
|
||||||
|
|
||||||
double m_cardinality = 0.0;
|
|
||||||
ULONG m_impure = 0;
|
ULONG m_impure = 0;
|
||||||
bool m_recursive = false;
|
bool m_recursive = false;
|
||||||
};
|
};
|
||||||
@ -191,12 +231,10 @@ namespace Jrd
|
|||||||
|
|
||||||
void close(thread_db* tdbb) const override;
|
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;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
void internalOpen(thread_db* tdbb) const override;
|
||||||
bool internalGetRecord(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 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;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
void internalOpen(thread_db* tdbb) const override;
|
||||||
bool internalGetRecord(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 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 setInversion(InversionNode* inversion, BoolExprNode* condition)
|
void setInversion(InversionNode* inversion, BoolExprNode* condition)
|
||||||
{
|
{
|
||||||
@ -277,6 +310,7 @@ namespace Jrd
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
void internalOpen(thread_db* tdbb) const override;
|
||||||
bool internalGetRecord(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;
|
bool refetchRecord(thread_db* tdbb) const override;
|
||||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) 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;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
void internalOpen(thread_db* tdbb) const override;
|
||||||
bool internalGetRecord(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:
|
private:
|
||||||
jrd_rel* const m_relation;
|
jrd_rel* const m_relation;
|
||||||
@ -342,12 +374,10 @@ namespace Jrd
|
|||||||
bool refetchRecord(thread_db* tdbb) const override;
|
bool refetchRecord(thread_db* tdbb) const override;
|
||||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) 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;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
void internalOpen(thread_db* tdbb) const override;
|
||||||
bool internalGetRecord(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;
|
bool refetchRecord(thread_db* tdbb) const override;
|
||||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) 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;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
void internalOpen(thread_db* tdbb) const override;
|
||||||
bool internalGetRecord(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;
|
bool refetchRecord(thread_db* tdbb) const override;
|
||||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) 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 markRecursive() override;
|
void markRecursive() override;
|
||||||
void invalidateRecords(Request* request) const override;
|
void invalidateRecords(Request* request) const override;
|
||||||
@ -423,6 +448,7 @@ namespace Jrd
|
|||||||
void nullRecords(thread_db* tdbb) const override;
|
void nullRecords(thread_db* tdbb) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
void internalOpen(thread_db* tdbb) const override;
|
||||||
bool internalGetRecord(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;
|
bool refetchRecord(thread_db* tdbb) const override;
|
||||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) 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 markRecursive() override;
|
void markRecursive() override;
|
||||||
void invalidateRecords(Request* request) const override;
|
void invalidateRecords(Request* request) const override;
|
||||||
@ -455,6 +478,7 @@ namespace Jrd
|
|||||||
void nullRecords(thread_db* tdbb) const override;
|
void nullRecords(thread_db* tdbb) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
void internalOpen(thread_db* tdbb) const override;
|
||||||
bool internalGetRecord(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;
|
bool refetchRecord(thread_db* tdbb) const override;
|
||||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) 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 markRecursive() override;
|
void markRecursive() override;
|
||||||
void invalidateRecords(Request* request) const override;
|
void invalidateRecords(Request* request) const override;
|
||||||
@ -495,6 +516,7 @@ namespace Jrd
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
void internalOpen(thread_db* tdbb) const override;
|
||||||
bool internalGetRecord(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;
|
bool refetchRecord(thread_db* tdbb) const override;
|
||||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) 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 markRecursive() override;
|
void markRecursive() override;
|
||||||
void invalidateRecords(Request* request) const override;
|
void invalidateRecords(Request* request) const override;
|
||||||
@ -535,6 +554,7 @@ namespace Jrd
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
void internalOpen(thread_db* tdbb) const override;
|
||||||
bool internalGetRecord(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;
|
bool refetchRecord(thread_db* tdbb) const override;
|
||||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) 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 markRecursive() override;
|
void markRecursive() override;
|
||||||
void invalidateRecords(Request* request) const override;
|
void invalidateRecords(Request* request) const override;
|
||||||
@ -576,6 +593,7 @@ namespace Jrd
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
void internalOpen(thread_db* tdbb) const override;
|
||||||
bool internalGetRecord(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;
|
bool refetchRecord(thread_db* tdbb) const override;
|
||||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) 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 markRecursive() override;
|
void markRecursive() override;
|
||||||
void invalidateRecords(Request* request) const override;
|
void invalidateRecords(Request* request) const override;
|
||||||
@ -720,6 +735,7 @@ namespace Jrd
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
void internalOpen(thread_db* tdbb) const override;
|
||||||
bool internalGetRecord(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);
|
const NestValueArray* group, MapNode* map, RecordSource* next);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
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;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||||
bool internalGetRecord(thread_db* tdbb) const override;
|
bool internalGetRecord(thread_db* tdbb) const override;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class WindowedStream : public RecordSource
|
class WindowedStream : public RecordSource
|
||||||
@ -971,13 +986,13 @@ namespace Jrd
|
|||||||
public:
|
public:
|
||||||
void close(thread_db* tdbb) const override;
|
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 findUsedStreams(StreamList& streams, bool expandAll = false) const override;
|
||||||
void nullRecords(thread_db* tdbb) const override;
|
void nullRecords(thread_db* tdbb) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
void internalOpen(thread_db* tdbb) const override;
|
||||||
bool internalGetRecord(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;
|
bool refetchRecord(thread_db* tdbb) const override;
|
||||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) 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 markRecursive() override;
|
void markRecursive() override;
|
||||||
void invalidateRecords(Request* request) const override;
|
void invalidateRecords(Request* request) const override;
|
||||||
@ -1025,6 +1037,7 @@ namespace Jrd
|
|||||||
void nullRecords(thread_db* tdbb) const override;
|
void nullRecords(thread_db* tdbb) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
void internalOpen(thread_db* tdbb) const override;
|
||||||
bool internalGetRecord(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;
|
bool refetchRecord(thread_db* tdbb) const override;
|
||||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) 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 markRecursive() override;
|
void markRecursive() override;
|
||||||
void invalidateRecords(Request* request) const override;
|
void invalidateRecords(Request* request) const override;
|
||||||
@ -1102,6 +1112,7 @@ namespace Jrd
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
void internalOpen(thread_db* tdbb) const override;
|
||||||
bool internalGetRecord(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;
|
bool refetchRecord(thread_db* tdbb) const override;
|
||||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) 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 markRecursive() override;
|
void markRecursive() override;
|
||||||
void invalidateRecords(Request* request) const override;
|
void invalidateRecords(Request* request) const override;
|
||||||
@ -1137,6 +1145,7 @@ namespace Jrd
|
|||||||
void nullRecords(thread_db* tdbb) const override;
|
void nullRecords(thread_db* tdbb) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
void internalOpen(thread_db* tdbb) const override;
|
||||||
bool internalGetRecord(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;
|
bool refetchRecord(thread_db* tdbb) const override;
|
||||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) 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 markRecursive() override;
|
void markRecursive() override;
|
||||||
void invalidateRecords(Request* request) const override;
|
void invalidateRecords(Request* request) const override;
|
||||||
@ -1170,6 +1176,7 @@ namespace Jrd
|
|||||||
void nullRecords(thread_db* tdbb) const override;
|
void nullRecords(thread_db* tdbb) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
void internalOpen(thread_db* tdbb) const override;
|
||||||
bool internalGetRecord(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;
|
bool refetchRecord(thread_db* tdbb) const override;
|
||||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) 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 markRecursive() override;
|
void markRecursive() override;
|
||||||
void invalidateRecords(Request* request) const override;
|
void invalidateRecords(Request* request) const override;
|
||||||
@ -1226,6 +1230,7 @@ namespace Jrd
|
|||||||
static unsigned maxCapacity();
|
static unsigned maxCapacity();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
void internalOpen(thread_db* tdbb) const override;
|
||||||
bool internalGetRecord(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;
|
bool refetchRecord(thread_db* tdbb) const override;
|
||||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) 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 markRecursive() override;
|
void markRecursive() override;
|
||||||
void invalidateRecords(Request* request) const override;
|
void invalidateRecords(Request* request) const override;
|
||||||
@ -1290,6 +1292,7 @@ namespace Jrd
|
|||||||
void nullRecords(thread_db* tdbb) const override;
|
void nullRecords(thread_db* tdbb) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
void internalOpen(thread_db* tdbb) const override;
|
||||||
bool internalGetRecord(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 close(thread_db* tdbb) const override;
|
||||||
|
|
||||||
void getChildren(Firebird::Array<const RecordSource*>& children) const override;
|
|
||||||
|
|
||||||
bool refetchRecord(thread_db* tdbb) const override;
|
bool refetchRecord(thread_db* tdbb) const override;
|
||||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
||||||
|
|
||||||
void print(thread_db* tdbb, Firebird::string& plan,
|
void getLegacyPlan(thread_db* tdbb, Firebird::string& plan, unsigned level) const override;
|
||||||
bool detailed, unsigned level, bool recurse) const override;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
void internalOpen(thread_db* tdbb) const override;
|
||||||
bool internalGetRecord(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;
|
bool refetchRecord(thread_db* tdbb) const override;
|
||||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) 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 markRecursive() override;
|
void markRecursive() override;
|
||||||
void invalidateRecords(Request* request) const override;
|
void invalidateRecords(Request* request) const override;
|
||||||
void findUsedStreams(StreamList& streams, bool expandAll = false) const override;
|
void findUsedStreams(StreamList& streams, bool expandAll = false) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
void internalOpen(thread_db* tdbb) const override;
|
||||||
bool internalGetRecord(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;
|
bool refetchRecord(thread_db* tdbb) const override;
|
||||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) 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 markRecursive() override;
|
void markRecursive() override;
|
||||||
void invalidateRecords(Request* request) const override;
|
void invalidateRecords(Request* request) const override;
|
||||||
void findUsedStreams(StreamList& streams, bool expandAll = false) const override;
|
void findUsedStreams(StreamList& streams, bool expandAll = false) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
void internalOpen(thread_db* tdbb) const override;
|
||||||
bool internalGetRecord(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;
|
bool refetchRecord(thread_db* tdbb) const override;
|
||||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) 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 markRecursive() override;
|
void markRecursive() override;
|
||||||
void invalidateRecords(Request* request) const override;
|
void invalidateRecords(Request* request) const override;
|
||||||
@ -1443,6 +1437,7 @@ namespace Jrd
|
|||||||
void nullRecords(thread_db* tdbb) const override;
|
void nullRecords(thread_db* tdbb) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const override;
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
void internalOpen(thread_db* tdbb) const override;
|
||||||
bool internalGetRecord(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));
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RecursiveStream::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
|
||||||
{
|
|
||||||
if (detailed)
|
|
||||||
{
|
|
||||||
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)
|
if (!level)
|
||||||
plan += "(";
|
plan += "(";
|
||||||
|
|
||||||
m_root->print(tdbb, plan, false, level + 1, recurse);
|
m_root->getLegacyPlan(tdbb, plan, level + 1);
|
||||||
|
|
||||||
plan += ", ";
|
plan += ", ";
|
||||||
|
|
||||||
m_inner->print(tdbb, plan, false, level + 1, recurse);
|
m_inner->getLegacyPlan(tdbb, plan, level + 1);
|
||||||
|
|
||||||
if (!level)
|
if (!level)
|
||||||
plan += ")";
|
plan += ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecursiveStream::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||||
|
{
|
||||||
|
planEntry.className = "RecursiveStream";
|
||||||
|
|
||||||
|
planEntry.description.add() = "Recursion";
|
||||||
|
printOptInfo(planEntry.description);
|
||||||
|
|
||||||
|
if (recurse)
|
||||||
|
{
|
||||||
|
++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);
|
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)
|
planEntry.className = "SingularStream";
|
||||||
{
|
|
||||||
plan += printIndent(++level) + "Singularity Check";
|
planEntry.description.add() = "Singularity Check";
|
||||||
printOptInfo(plan);
|
printOptInfo(planEntry.description);
|
||||||
}
|
|
||||||
|
|
||||||
if (recurse)
|
if (recurse)
|
||||||
m_next->print(tdbb, plan, detailed, level, recurse);
|
{
|
||||||
|
++level;
|
||||||
|
m_next->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SingularStream::markRecursive()
|
void SingularStream::markRecursive()
|
||||||
|
@ -116,21 +116,23 @@ WriteLockResult SkipRowsStream::lockRecord(thread_db* tdbb, bool skipLocked) con
|
|||||||
return m_next->lockRecord(tdbb, skipLocked);
|
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)
|
planEntry.className = "SkipRowsStream";
|
||||||
{
|
|
||||||
plan += printIndent(++level) + "Skip N Records";
|
planEntry.description.add() = "Skip N Records";
|
||||||
printOptInfo(plan);
|
printOptInfo(planEntry.description);
|
||||||
}
|
|
||||||
|
|
||||||
if (recurse)
|
if (recurse)
|
||||||
m_next->print(tdbb, plan, detailed, level, recurse);
|
{
|
||||||
|
++level;
|
||||||
|
m_next->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkipRowsStream::markRecursive()
|
void SkipRowsStream::markRecursive()
|
||||||
|
@ -121,36 +121,41 @@ WriteLockResult SortedStream::lockRecord(thread_db* tdbb, bool skipLocked) const
|
|||||||
return m_next->lockRecord(tdbb, skipLocked);
|
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,
|
void SortedStream::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||||
bool detailed, unsigned level, bool recurse) const
|
|
||||||
{
|
{
|
||||||
if (detailed)
|
planEntry.className = "SortedStream";
|
||||||
{
|
|
||||||
string extras;
|
string extras;
|
||||||
extras.printf(" (record length: %" ULONGFORMAT", key length: %" ULONGFORMAT")",
|
extras.printf(" (record length: %" ULONGFORMAT", key length: %" ULONGFORMAT")",
|
||||||
m_map->length, m_map->keyLength);
|
m_map->length, m_map->keyLength);
|
||||||
|
|
||||||
if (m_map->flags & FLAG_REFETCH)
|
auto planDescription = &planEntry.description.add();
|
||||||
plan += printIndent(++level) + "Refetch";
|
|
||||||
|
|
||||||
plan += printIndent(++level) +
|
if (m_map->flags & FLAG_REFETCH)
|
||||||
((m_map->flags & FLAG_PROJECT) ? "Unique Sort" : "Sort") + extras;
|
{
|
||||||
printOptInfo(plan);
|
*planDescription = "Refetch";
|
||||||
|
planDescription = &planEntry.description.add();
|
||||||
|
++level;
|
||||||
|
}
|
||||||
|
|
||||||
|
*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)
|
if (recurse)
|
||||||
m_next->print(tdbb, plan, true, level, recurse);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
level++;
|
++level;
|
||||||
plan += "SORT (";
|
m_next->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||||
m_next->print(tdbb, plan, false, level, recurse);
|
|
||||||
plan += ")";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,27 +162,8 @@ WriteLockResult Union::lockRecord(thread_db* tdbb, bool skipLocked) const
|
|||||||
return m_args[impure->irsb_count]->lockRecord(tdbb, skipLocked);
|
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
|
||||||
{
|
{
|
||||||
for (FB_SIZE_T i = 0; i < m_args.getCount(); i++)
|
|
||||||
children.add(m_args[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Union::print(thread_db* tdbb, string& plan, bool detailed, unsigned level, bool recurse) const
|
|
||||||
{
|
|
||||||
if (detailed)
|
|
||||||
{
|
|
||||||
plan += printIndent(++level) + (m_args.getCount() == 1 ? "Materialize" : "Union");
|
|
||||||
printOptInfo(plan);
|
|
||||||
|
|
||||||
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)
|
if (!level)
|
||||||
plan += "(";
|
plan += "(";
|
||||||
|
|
||||||
@ -191,11 +172,26 @@ void Union::print(thread_db* tdbb, string& plan, bool detailed, unsigned level,
|
|||||||
if (i)
|
if (i)
|
||||||
plan += ", ";
|
plan += ", ";
|
||||||
|
|
||||||
m_args[i]->print(tdbb, plan, false, level + 1, recurse);
|
m_args[i]->getLegacyPlan(tdbb, plan, level + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!level)
|
if (!level)
|
||||||
plan += ")";
|
plan += ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Union::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||||
|
{
|
||||||
|
planEntry.className = "Union";
|
||||||
|
|
||||||
|
planEntry.description.add() = (m_args.getCount() == 1 ? "Materialize" : "Union");
|
||||||
|
printOptInfo(planEntry.description);
|
||||||
|
|
||||||
|
if (recurse)
|
||||||
|
{
|
||||||
|
++level;
|
||||||
|
|
||||||
|
for (const auto arg : m_args)
|
||||||
|
arg->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,20 +109,8 @@ WriteLockResult VirtualTableScan::lockRecord(thread_db* /*tdbb*/, bool /*skipLoc
|
|||||||
status_exception::raise(Arg::Gds(isc_record_lock_not_supp));
|
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
|
||||||
{
|
{
|
||||||
}
|
|
||||||
|
|
||||||
void VirtualTableScan::print(thread_db* tdbb, string& plan, bool detailed, 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)
|
if (!level)
|
||||||
plan += "(";
|
plan += "(";
|
||||||
|
|
||||||
@ -130,5 +118,18 @@ void VirtualTableScan::print(thread_db* tdbb, string& plan, bool detailed, unsig
|
|||||||
|
|
||||||
if (!level)
|
if (!level)
|
||||||
plan += ")";
|
plan += ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VirtualTableScan::internalGetPlan(thread_db* tdbb, PlanEntry& planEntry, unsigned level, bool recurse) const
|
||||||
|
{
|
||||||
|
planEntry.className = "VirtualTableScan";
|
||||||
|
|
||||||
|
planEntry.description.add() = "Table " + printName(tdbb, m_relation->rel_name.c_str(), m_alias) + " Full Scan";
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
@ -52,16 +52,12 @@ namespace
|
|||||||
public:
|
public:
|
||||||
BufferedStreamWindow(CompilerScratch* csb, BufferedStream* next);
|
BufferedStreamWindow(CompilerScratch* csb, BufferedStream* next);
|
||||||
|
|
||||||
void internalOpen(thread_db* tdbb) const override;
|
|
||||||
void close(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;
|
bool refetchRecord(thread_db* tdbb) const override;
|
||||||
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) const override;
|
WriteLockResult lockRecord(thread_db* tdbb, bool skipLocked) 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 markRecursive() override;
|
void markRecursive() override;
|
||||||
void invalidateRecords(Request* request) const override;
|
void invalidateRecords(Request* request) const override;
|
||||||
@ -87,6 +83,11 @@ namespace
|
|||||||
return impure->irsb_position;
|
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:
|
public:
|
||||||
NestConst<BufferedStream> m_next;
|
NestConst<BufferedStream> m_next;
|
||||||
};
|
};
|
||||||
@ -147,21 +148,23 @@ namespace
|
|||||||
return m_next->lockRecord(tdbb, skipLocked);
|
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)
|
planEntry.className = "BufferedStreamWindow";
|
||||||
{
|
|
||||||
plan += printIndent(++level) + "Window Buffer";
|
planEntry.description.add() = "Window Buffer";
|
||||||
printOptInfo(plan);
|
printOptInfo(planEntry.description);
|
||||||
}
|
|
||||||
|
|
||||||
if (recurse)
|
if (recurse)
|
||||||
m_next->print(tdbb, plan, detailed, level, recurse);
|
{
|
||||||
|
++level;
|
||||||
|
m_next->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferedStreamWindow::markRecursive()
|
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));
|
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)
|
planEntry.className = "WindowedStream";
|
||||||
{
|
|
||||||
plan += printIndent(++level) + "Window";
|
planEntry.description.add() = "Window";
|
||||||
printOptInfo(plan);
|
printOptInfo(planEntry.description);
|
||||||
}
|
|
||||||
|
|
||||||
if (recurse)
|
if (recurse)
|
||||||
m_joinedStream->print(tdbb, plan, detailed, level, recurse);
|
{
|
||||||
|
++level;
|
||||||
|
m_joinedStream->getPlan(tdbb, planEntry.children.add(), level, recurse);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowedStream::markRecursive()
|
void WindowedStream::markRecursive()
|
||||||
@ -899,22 +904,24 @@ bool WindowedStream::WindowStream::internalGetRecord(thread_db* tdbb) const
|
|||||||
return true;
|
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)
|
planEntry.className = "WindowStream";
|
||||||
{
|
|
||||||
plan += printIndent(++level) + "Window Partition";
|
planEntry.description.add() = "Window Partition";
|
||||||
printOptInfo(plan);
|
printOptInfo(planEntry.description);
|
||||||
}
|
|
||||||
|
|
||||||
if (recurse)
|
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
|
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