Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion builds/posix/make.shared.variables
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ AllObjects += $(Profiler_Objects)

# Engine
Engine_Objects:= $(call dirObjects,jrd) $(call dirObjects,dsql) $(call dirObjects,jrd/extds) \
$(call dirObjects,jrd/optimizer) $(call dirObjects,jrd/recsrc) $(call dirObjects,jrd/replication) $(call dirObjects,jrd/trace) \
$(call dirObjects,jrd/optimizer) $(call dirObjects,jrd/recsrc) $(call dirObjects,jrd/replication) \
$(call dirObjects,jrd/sys-packages) $(call dirObjects,jrd/trace) \
$(call makeObjects,lock,lock.cpp)

Engine_Test_Objects:= $(call dirObjects,jrd/tests)
Expand Down
4 changes: 3 additions & 1 deletion builds/win32/msvc15/engine_static.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@
<ClCompile Include="..\..\..\src\jrd\sqz.cpp" />
<ClCompile Include="..\..\..\src\jrd\Statement.cpp" />
<ClCompile Include="..\..\..\src\jrd\svc.cpp" />
<ClCompile Include="..\..\..\src\jrd\sys-packages\SqlPackage.cpp" />
<ClCompile Include="..\..\..\src\jrd\SysFunction.cpp" />
<ClCompile Include="..\..\..\src\jrd\SystemPackages.cpp" />
<ClCompile Include="..\..\..\src\jrd\TempSpace.cpp" />
Expand Down Expand Up @@ -345,6 +346,7 @@
<ClInclude Include="..\..\..\src\jrd\status.h" />
<ClInclude Include="..\..\..\src\jrd\svc.h" />
<ClInclude Include="..\..\..\src\jrd\svc_undoc.h" />
<ClInclude Include="..\..\..\src\jrd\sys-packages\SqlPackage.h" />
<ClInclude Include="..\..\..\src\jrd\SysFunction.h" />
<ClInclude Include="..\..\..\src\jrd\SystemPackages.h" />
<ClInclude Include="..\..\..\src\jrd\TempSpace.h" />
Expand Down Expand Up @@ -513,4 +515,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>
6 changes: 6 additions & 0 deletions builds/win32/msvc15/engine_static.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,9 @@
<ClCompile Include="..\..\..\src\jrd\svc.cpp">
<Filter>JRD files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\jrd\sys-packages\SqlPackage.cpp">
<Filter>JRD files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\jrd\SysFunction.cpp">
<Filter>JRD files</Filter>
</ClCompile>
Expand Down Expand Up @@ -1073,6 +1076,9 @@
<ClInclude Include="..\..\..\src\dsql\DsqlBatch.h">
<Filter>Header files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\jrd\sys-packages\SqlPackage.h">
<Filter>Header files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\jrd\SystemPackages.h">
<Filter>Header files</Filter>
</ClInclude>
Expand Down
29 changes: 29 additions & 0 deletions doc/README.isql_enhancements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -324,3 +324,32 @@ RDB$RELATIONS | 59| | | |

-- turn per-table stats off, using shortened name
SQL> SET PER_TAB OFF;



Isql enhancements in Firebird v6.
---------------------------------

EXPLAIN statement.

Author: Adriano dos Santos Fernandes

A new ISQL statement was created to easily show a query plan without execute it.

Note: If SET STATS is ON, stats are still shown.

Examples:

SQL> explain select * from employees where id = ?;

SQL> set term !;
SQL>
SQL> explain
CON> execute block
CON> as
CON> declare id integer;
CON> begin
CON> select id from employees where id = ? into id;
CON> end!
SQL>
SQL> set term ;!
46 changes: 46 additions & 0 deletions doc/sql.extensions/README.sql_package.md
Original file line number Diff line number Diff line change
@@ -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
10 changes: 8 additions & 2 deletions src/common/classes/objects_array.h
Original file line number Diff line number Diff line change
Expand Up @@ -306,10 +306,16 @@ namespace Firebird
return iterator(this, getCount());
}

iterator back()
T& front()
{
fb_assert(getCount() > 0);
return iterator(this, getCount() - 1);
return *begin();
}

T& back()
{
fb_assert(getCount() > 0);
return *iterator(this, getCount() - 1);
}

const_iterator begin() const
Expand Down
6 changes: 3 additions & 3 deletions src/common/unicode_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1708,16 +1708,16 @@ UnicodeUtil::Utf16Collation* UnicodeUtil::Utf16Collation::create(
++secondKeyDataIt;
}

unsigned backSize = commonKeys.back()->getCount();
unsigned backSize = commonKeys.back().getCount();

if (common > backSize)
commonKeys.back()->append(secondKeyIt->begin() + backSize, common - backSize);
commonKeys.back().append(secondKeyIt->begin() + backSize, common - backSize);
else if (common < backSize)
{
if (common == 0)
commonKeys.push(*secondKeyIt);
else
commonKeys.back()->resize(common);
commonKeys.back().resize(common);
}

if (++secondKeyIt != keySet.end())
Expand Down
2 changes: 1 addition & 1 deletion src/dsql/parse.y
Original file line number Diff line number Diff line change
Expand Up @@ -2505,7 +2505,7 @@ column_constraint_def($addColumnClause)
: constraint_name_opt column_constraint($addColumnClause)
{
if ($1)
$addColumnClause->constraints.back()->name = *$1;
$addColumnClause->constraints.back().name = *$1;
}
;

Expand Down
1 change: 1 addition & 0 deletions src/include/firebird/impl/msg/isql.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,4 @@ FB_IMPL_MSG_SYMBOL(ISQL, 201, NO_PUBLICATION, "There is no publication @1 in thi
FB_IMPL_MSG_SYMBOL(ISQL, 202, NO_PUBLICATIONS, "There is no publications in this database")
FB_IMPL_MSG_SYMBOL(ISQL, 203, MSG_PUBLICATIONS, "Publications:")
FB_IMPL_MSG_SYMBOL(ISQL, 204, MSG_PROCEDURES, "Procedures:")
FB_IMPL_MSG_SYMBOL(ISQL, 205, HLP_EXPLAIN, "EXPLAIN -- explain a query access plan")
37 changes: 34 additions & 3 deletions src/isql/isql.epp
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@ static processing_state get_statement(string&, const TEXT*);
static bool get_numeric(const UCHAR*, USHORT, SSHORT*, SINT64*);
static void print_set(const char* str, bool v);
static processing_state print_sets();
static processing_state explain(const TEXT*);
static processing_state help(const TEXT*);
static bool isyesno(const TEXT*);
static processing_state newdb(TEXT*, const TEXT*, const TEXT*, int, const TEXT*, bool);
Expand Down Expand Up @@ -587,6 +588,7 @@ public:
KeepTranParams = true;
TranParams->assign(DEFAULT_DML_TRANS_SQL);
PerTableStats = false;
ExplainCommand = false;
}

ColList global_Cols;
Expand All @@ -611,6 +613,7 @@ public:
SCHAR ISQL_charset[MAXCHARSET_SIZE];
bool KeepTranParams;
bool PerTableStats;
bool ExplainCommand;
};

static SetValues setValues;
Expand Down Expand Up @@ -5009,7 +5012,7 @@ static processing_state frontend(const TEXT* statement)
{
show, add, copy,
blobview, output, shell, set, create, drop, connect,
edit, input, quit, exit, help,
edit, input, quit, exit, explain, help,
#ifdef DEV_BUILD
passthrough,
#endif
Expand Down Expand Up @@ -5039,6 +5042,7 @@ static processing_state frontend(const TEXT* statement)
{FrontOptions::input, "INPUT", 2},
{FrontOptions::quit, "QUIT", 0},
{FrontOptions::exit, "EXIT", 0},
{FrontOptions::explain, "EXPLAIN", 0},
{FrontOptions::help, "?", 0},
{FrontOptions::help, "HELP", 0}
#ifdef DEV_BUILD
Expand Down Expand Up @@ -5236,6 +5240,10 @@ static processing_state frontend(const TEXT* statement)
ret = EXIT;
break;

case FrontOptions::explain:
ret = explain(cmd + 7);
break;

case FrontOptions::help:
ret = help(parms[1]);
break;
Expand Down Expand Up @@ -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)
{
/**************************************
Expand All @@ -6523,6 +6545,7 @@ static processing_state help(const TEXT* what)
HLP_BLOBED, // BLOBVIEW <blobid> -- view BLOB in text editor
HLP_EDIT, // EDIT [<filename>] -- edit SQL script file and execute
HLP_EDIT2, // EDIT -- edit current command buffer and execute
HLP_EXPLAIN, // EXPLAIN -- explain a query access plan
HLP_HELP, // HELP -- display this menu
HLP_INPUT, // INput <filename> -- take input from the named SQL file
HLP_OUTPUT, // OUTput [<filename>] -- write output to named file
Expand Down Expand Up @@ -9121,10 +9144,18 @@ static processing_state process_statement(const TEXT* str2)
statement_type != isc_info_sql_stmt_set_generator)
{
process_plan();
if (setValues.Planonly && !is_selectable)

if (setValues.ExplainCommand)
{
return ret; // do not execute
if (setValues.Stats && (print_performance(perf_before) == ps_ERR))
ret = ps_ERR;

if (setValues.PerTableStats)
perTableStats->getStats(DB, false);
}

if (setValues.Planonly && (!is_selectable || setValues.ExplainCommand))
return ret; // do not execute
}

if (setValues.ExecPathDisplay[0])
Expand Down
75 changes: 13 additions & 62 deletions src/jrd/ProfilerManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -498,80 +498,31 @@ void ProfilerManager::prepareRecSource(thread_db* tdbb, Request* request, const

fb_assert(profileStatement->definedCursors.exist(recordSource->getCursorId()));

struct PlanItem : PermanentStorage
{
explicit PlanItem(MemoryPool& p)
: PermanentStorage(p)
{
}

const AccessPath* recordSource = nullptr;
const AccessPath* parentRecordSource = nullptr;
string accessPath{getPool()};
unsigned level = 0;
};

ObjectsArray<PlanItem> planItems;
planItems.add().recordSource = recordSource;
PlanEntry rootEntry;
recordSource->getPlan(tdbb, rootEntry, 0, true);

for (unsigned pos = 0; pos < planItems.getCount(); ++pos)
{
auto& planItem = planItems[pos];
const auto thisRsb = planItem.recordSource;

string& accessPath = planItem.accessPath;
thisRsb->print(tdbb, accessPath, true, 0, false);

constexpr auto INDENT_MARKER = "\n ";
constexpr unsigned INDENT_COUNT = 4;

if (accessPath.find(INDENT_MARKER) == 0)
{
unsigned pos = 0;

do {
accessPath.erase(pos + 1, 4);
} while ((pos = accessPath.find(INDENT_MARKER, pos + 1)) != string::npos);
}

if (accessPath.hasData() && accessPath[0] == '\n')
accessPath.erase(0, 1);

Array<const RecordSource*> children;
thisRsb->getChildren(children);

unsigned level = planItem.level;

if (const auto lastLinePos = accessPath.find_last_of('\n'); lastLinePos != string::npos)
level += (accessPath.find_first_not_of(' ', lastLinePos + 1) - lastLinePos + 1) / INDENT_COUNT;

unsigned childPos = pos;

for (const auto child : children)
{
auto& inserted = planItems.insert(++childPos);
inserted.recordSource = child;
inserted.parentRecordSource = thisRsb;
inserted.level = level + 1;
}
}
Array<NonPooledPair<const PlanEntry*, const PlanEntry*>> flatPlan;
rootEntry.asFlatList(flatPlan);

NonPooledMap<ULONG, ULONG> idSequenceMap;
auto sequencePtr = profileStatement->cursorNextSequence.getOrPut(recordSource->getCursorId());

for (const auto& planItem : planItems)
for (const auto& [planEntry, parentPlanEntry] : flatPlan)
{
const auto cursorId = planItem.recordSource->getCursorId();
const auto recSourceId = planItem.recordSource->getRecSourceId();
const auto cursorId = planEntry->accessPath->getCursorId();
const auto recSourceId = planEntry->accessPath->getRecSourceId();
idSequenceMap.put(recSourceId, ++*sequencePtr);

ULONG parentSequence = 0;

if (planItem.parentRecordSource)
parentSequence = *idSequenceMap.get(planItem.parentRecordSource->getRecSourceId());
if (parentPlanEntry)
parentSequence = *idSequenceMap.get(parentPlanEntry->accessPath->getRecSourceId());

string accessPath;
planEntry->getDescriptionAsString(accessPath);

currentSession->pluginSession->defineRecordSource(profileStatement->id, cursorId,
*sequencePtr, planItem.level, planItem.accessPath.c_str(), parentSequence);
*sequencePtr, planEntry->level, accessPath.c_str(), parentSequence);

profileStatement->recSourceSequence.put(recSourceId, *sequencePtr);
}
Expand Down
Loading