Skip to content

Commit

Permalink
Feature #6910 - Add way to retrieve statement BLR with Statement::get…
Browse files Browse the repository at this point in the history
…Info and ISQL's SET EXEC_PATH_DISPLAY BLR.
  • Loading branch information
asfernandes committed Jul 29, 2021
1 parent 7ce963a commit 55704ef
Show file tree
Hide file tree
Showing 14 changed files with 219 additions and 42 deletions.
23 changes: 18 additions & 5 deletions doc/README.isql_enhancements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -163,13 +163,13 @@ Isql enhancements in Firebird v3.

Author: Vladyslav Khorsun <hvlad at users sourcefoege net>

When set to ON, isql keeps text of following successful SET TRANSACTION statement and
new DML transactions is started using the same SQL text (instead of defaul CONCURRENCY
When set to ON, isql keeps text of following successful SET TRANSACTION statement and
new DML transactions is started using the same SQL text (instead of defaul CONCURRENCY
WAIT mode).
When set to OFF, isql start new DML transaction as usual.
Name KEEP_TRAN_PARAMS could be cut down to the KEEP_TRAN.

In Firebird 3 KEEP_TRAN_PARAMS value is OFF by default, preserving backward compatibility
In Firebird 3 KEEP_TRAN_PARAMS value is OFF by default, preserving backward compatibility
with old behaviour.
In Firebird 4 KEEP_TRAN_PARAMS is ON by default to make isql behaviour more logical.

Expand All @@ -191,7 +191,7 @@ SET TRANSACTION

SQL>commit;

-- start new transaction, check KEEP_TRAN value and actual transaction's
-- start new transaction, check KEEP_TRAN value and actual transaction's
-- parameters
SQL>SET TRANSACTION READ COMMITTED WAIT;
SQL>SET;
Expand Down Expand Up @@ -237,4 +237,17 @@ SNAPSHOT
SQL> SET;
...
Keep transaction params: OFF
SQL>
SQL>



Isql enhancements in Firebird v5.
---------------------------------

10) SET EXEC_PATH_DISPLAY BLR/OFF

Retrieves the execution path of a DML statement formatted as BLR text.

Warning: this feature is very tied to engine internals and its usage is discouraged
if you do not understand very well how these internals are subject to change between
versions.
2 changes: 2 additions & 0 deletions src/dsql/DsqlCompilerScratch.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class DsqlCompilerScratch : public BlrDebugWriter
transaction(aTransaction),
statement(aStatement),
flags(0),
prepareFlags(0),
nestingLevel(0),
ports(p),
relation(NULL),
Expand Down Expand Up @@ -267,6 +268,7 @@ class DsqlCompilerScratch : public BlrDebugWriter

public:
unsigned flags; // flags
unsigned prepareFlags; // prepare flags (IStatement::PREPARE*)
unsigned nestingLevel; // begin...end nesting level
Firebird::Array<dsql_msg*> ports; // Port messages
dsql_rel* relation; // relation created by this request (for DDL)
Expand Down
2 changes: 2 additions & 0 deletions src/dsql/PackageNodes.epp
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ DdlNode* CreateAlterPackageNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)

itemScratch->clientDialect = dsqlScratch->clientDialect;
itemScratch->flags |= DsqlCompilerScratch::FLAG_DDL;
itemScratch->prepareFlags = dsqlScratch->prepareFlags;
itemScratch->package = name;

switch ((*items)[i].type)
Expand Down Expand Up @@ -624,6 +625,7 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)

itemScratch->clientDialect = dsqlScratch->clientDialect;
itemScratch->flags |= DsqlCompilerScratch::FLAG_DDL;
itemScratch->prepareFlags = dsqlScratch->prepareFlags;
itemScratch->package = name;

switch ((*arrays[i])[j].type)
Expand Down
8 changes: 6 additions & 2 deletions src/dsql/StmtNodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1725,6 +1725,7 @@ DeclareSubFuncNode* DeclareSubFuncNode::dsqlPass(DsqlCompilerScratch* dsqlScratc
DsqlCompilerScratch::FLAG_FUNCTION |
DsqlCompilerScratch::FLAG_SUB_ROUTINE |
(dsqlScratch->flags & DsqlCompilerScratch::FLAG_DDL);
blockScratch->prepareFlags = dsqlScratch->prepareFlags;

dsqlBlock = dsqlBlock->dsqlPass(blockScratch);

Expand Down Expand Up @@ -2064,8 +2065,11 @@ DeclareSubProcNode* DeclareSubProcNode::dsqlPass(DsqlCompilerScratch* dsqlScratc
blockScratch = FB_NEW_POOL(pool) DsqlCompilerScratch(pool,
dsqlScratch->getAttachment(), dsqlScratch->getTransaction(), statement, dsqlScratch);
blockScratch->clientDialect = dsqlScratch->clientDialect;
blockScratch->flags |= DsqlCompilerScratch::FLAG_PROCEDURE | DsqlCompilerScratch::FLAG_SUB_ROUTINE;
blockScratch->flags |= dsqlScratch->flags & DsqlCompilerScratch::FLAG_DDL;
blockScratch->flags |=
DsqlCompilerScratch::FLAG_PROCEDURE |
DsqlCompilerScratch::FLAG_SUB_ROUTINE |
(dsqlScratch->flags & DsqlCompilerScratch::FLAG_DDL);
blockScratch->prepareFlags = dsqlScratch->prepareFlags;

dsqlBlock = dsqlBlock->dsqlPass(blockScratch);

Expand Down
77 changes: 66 additions & 11 deletions src/dsql/dsql.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ using namespace Firebird;

static ULONG get_request_info(thread_db*, dsql_req*, ULONG, UCHAR*);
static dsql_dbb* init(Jrd::thread_db*, Jrd::Attachment*);
static dsql_req* prepareRequest(thread_db*, dsql_dbb*, jrd_tra*, ULONG, const TEXT*, USHORT, bool);
static dsql_req* prepareStatement(thread_db*, dsql_dbb*, jrd_tra*, ULONG, const TEXT*, USHORT, bool);
static dsql_req* prepareRequest(thread_db*, dsql_dbb*, jrd_tra*, ULONG, const TEXT*, USHORT, unsigned, bool);
static dsql_req* prepareStatement(thread_db*, dsql_dbb*, jrd_tra*, ULONG, const TEXT*, USHORT, unsigned, bool);
static UCHAR* put_item(UCHAR, const USHORT, const UCHAR*, UCHAR*, const UCHAR* const);
static void release_statement(DsqlCompiledStatement* statement);
static void sql_info(thread_db*, dsql_req*, ULONG, const UCHAR*, ULONG, UCHAR*);
Expand Down Expand Up @@ -288,7 +288,7 @@ bool DsqlDmlRequest::fetch(thread_db* tdbb, UCHAR* msgBuffer)
UCHAR* dsqlMsgBuffer = req_msg_buffers[message->msg_buffer_number];
if (!firstRowFetched && needRestarts())
{
// Note: tra_handle can't be changed by executeReceiveWithRestarts below
// Note: tra_handle can't be changed by executeReceiveWithRestarts below
// and outMetadata and outMsg in not used there, so passing NULL's is safe.
jrd_tra* tra = req_transaction;

Expand Down Expand Up @@ -392,7 +392,7 @@ void DSQL_free_statement(thread_db* tdbb, dsql_req* request, USHORT option)
**/
dsql_req* DSQL_prepare(thread_db* tdbb,
Attachment* attachment, jrd_tra* transaction,
ULONG length, const TEXT* string, USHORT dialect,
ULONG length, const TEXT* string, USHORT dialect, unsigned prepareFlags,
Array<UCHAR>* items, Array<UCHAR>* buffer,
bool isInternalRequest)
{
Expand All @@ -406,7 +406,7 @@ dsql_req* DSQL_prepare(thread_db* tdbb,
// Allocate a new request block and then prepare the request.

request = prepareRequest(tdbb, database, transaction, length, string, dialect,
isInternalRequest);
prepareFlags, isInternalRequest);

// Can not prepare a CREATE DATABASE/SCHEMA statement

Expand Down Expand Up @@ -557,7 +557,7 @@ void DSQL_execute_immediate(thread_db* tdbb, Jrd::Attachment* attachment, jrd_tr
try
{
request = prepareRequest(tdbb, database, *tra_handle, length, string, dialect,
isInternalRequest);
0, isInternalRequest);

const DsqlCompiledStatement* statement = request->getStatement();

Expand Down Expand Up @@ -659,7 +659,8 @@ void DsqlDmlRequest::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, boo
scratch->getBlrData().getCount(), scratch->getBlrData().begin(),
statement->getSqlText(),
scratch->getDebugData().getCount(), scratch->getDebugData().begin(),
(scratch->flags & DsqlCompilerScratch::FLAG_INTERNAL_REQUEST));
(scratch->flags & DsqlCompilerScratch::FLAG_INTERNAL_REQUEST),
(scratch->prepareFlags & IStatement::PREPARE_KEEP_EXEC_PATH));
}
catch (const Exception&)
{
Expand Down Expand Up @@ -940,7 +941,7 @@ void DsqlDmlRequest::executeReceiveWithRestarts(thread_db* tdbb, jrd_tra** traHa
"\tQuery:\n%s\n", numTries, req_request->getStatement()->sqlText->c_str() );
}

// When restart we must execute query
// When restart we must execute query
exec = true;
}
}
Expand Down Expand Up @@ -1477,17 +1478,17 @@ static void checkD(IStatus* st)
// Prepare a request for execution. Return SQL status code.
// Note: caller is responsible for pool handling.
static dsql_req* prepareRequest(thread_db* tdbb, dsql_dbb* database, jrd_tra* transaction,
ULONG textLength, const TEXT* text, USHORT clientDialect, bool isInternalRequest)
ULONG textLength, const TEXT* text, USHORT clientDialect, unsigned prepareFlags, bool isInternalRequest)
{
return prepareStatement(tdbb, database, transaction, textLength, text, clientDialect,
isInternalRequest);
prepareFlags, isInternalRequest);
}


// Prepare a statement for execution. Return SQL status code.
// Note: caller is responsible for pool handling.
static dsql_req* prepareStatement(thread_db* tdbb, dsql_dbb* database, jrd_tra* transaction,
ULONG textLength, const TEXT* text, USHORT clientDialect, bool isInternalRequest)
ULONG textLength, const TEXT* text, USHORT clientDialect, unsigned prepareFlags, bool isInternalRequest)
{
Database* const dbb = tdbb->getDatabase();

Expand Down Expand Up @@ -1547,6 +1548,7 @@ static dsql_req* prepareStatement(thread_db* tdbb, dsql_dbb* database, jrd_tra*

DsqlCompilerScratch* scratch = FB_NEW_POOL(*scratchPool) DsqlCompilerScratch(*scratchPool, database,
transaction, statement);
scratch->prepareFlags = prepareFlags;
scratch->clientDialect = clientDialect;

if (isInternalRequest)
Expand Down Expand Up @@ -2223,6 +2225,59 @@ static void sql_info(thread_db* tdbb,
}
break;

case isc_info_sql_exec_path_blr_bytes:
case isc_info_sql_exec_path_blr_text:
{
HalfStaticArray<UCHAR, 128> path;

if (request->req_request && request->req_request->getStatement())
{
const auto& blr = request->req_request->getStatement()->blr;

if (blr.hasData())
{
if (item == isc_info_sql_exec_path_blr_bytes)
path.push(blr.begin(), blr.getCount());
else if (item == isc_info_sql_exec_path_blr_text)
{
fb_print_blr(blr.begin(), (ULONG) blr.getCount(),
[](void* arg, SSHORT offset, const char* line)
{
auto& localPath = *static_cast<decltype(path)*>(arg);
auto lineLen = strlen(line);
char offsetStr[10];
auto offsetLen = sprintf(offsetStr, "%5d", (int) offset);

localPath.push(reinterpret_cast<const UCHAR*>(offsetStr), offsetLen);
localPath.push(' ');
localPath.push(reinterpret_cast<const UCHAR*>(line), lineLen);
localPath.push('\n');
},
&path, 0);
}
}
}

if (path.hasData())
{
// 1-byte item + 2-byte length + isc_info_end/isc_info_truncated == 4
const ULONG bufferLength = end_info - info - 4;
const ULONG maxLength = MIN(bufferLength, MAX_USHORT);

if (path.getCount() > maxLength)
{
*info = isc_info_truncated;
info = NULL;
}
else
info = put_item(item, path.getCount(), path.begin(), info, end_info);
}

if (!info)
return;
}
break;

case isc_info_sql_num_variables:
case isc_info_sql_describe_vars:
if (messageFound)
Expand Down
2 changes: 1 addition & 1 deletion src/dsql/dsql_proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Jrd::DsqlCursor* DSQL_open(Jrd::thread_db*, Jrd::jrd_tra**, Jrd::dsql_req*,
Firebird::IMessageMetadata*, const UCHAR*,
Firebird::IMessageMetadata*, ULONG);
Jrd::dsql_req* DSQL_prepare(Jrd::thread_db*, Jrd::Attachment*, Jrd::jrd_tra*, ULONG, const TEXT*,
USHORT, Firebird::Array<UCHAR>*, Firebird::Array<UCHAR>*, bool);
USHORT, unsigned, Firebird::Array<UCHAR>*, Firebird::Array<UCHAR>*, bool);
void DSQL_sql_info(Jrd::thread_db*, Jrd::dsql_req*,
ULONG, const UCHAR*, ULONG, UCHAR*);

Expand Down
5 changes: 5 additions & 0 deletions src/include/firebird/FirebirdInterface.idl
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,11 @@ interface Statement : ReferenceCounted
PREPARE_PREFETCH_METADATA | PREPARE_PREFETCH_LEGACY_PLAN | PREPARE_PREFETCH_DETAILED_PLAN |
PREPARE_PREFETCH_AFFECTED_RECORDS;

// Keep the execution path information to be retrieved with getInfo and isc_info_sql_exec_path_*
// Warning: this feature is very tied to engine internals and its usage is discouraged if you do
// not understand very well how these internals are subject to change between versions.
const uint PREPARE_KEEP_EXEC_PATH = 0x80;

// Statement flags.
const uint FLAG_HAS_CURSOR = 0x01;
const uint FLAG_REPEAT_EXECUTE = 0x02;
Expand Down
1 change: 1 addition & 0 deletions src/include/firebird/IdlFbInterfaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -1718,6 +1718,7 @@ namespace Firebird
static const unsigned PREPARE_PREFETCH_FLAGS = 0x40;
static const unsigned PREPARE_PREFETCH_METADATA = IStatement::PREPARE_PREFETCH_TYPE | IStatement::PREPARE_PREFETCH_FLAGS | IStatement::PREPARE_PREFETCH_INPUT_PARAMETERS | IStatement::PREPARE_PREFETCH_OUTPUT_PARAMETERS;
static const unsigned PREPARE_PREFETCH_ALL = IStatement::PREPARE_PREFETCH_METADATA | IStatement::PREPARE_PREFETCH_LEGACY_PLAN | IStatement::PREPARE_PREFETCH_DETAILED_PLAN | IStatement::PREPARE_PREFETCH_AFFECTED_RECORDS;
static const unsigned PREPARE_KEEP_EXEC_PATH = 0x80;
static const unsigned FLAG_HAS_CURSOR = 0x1;
static const unsigned FLAG_REPEAT_EXECUTE = 0x2;
static const unsigned CURSOR_TYPE_SCROLLABLE = 0x1;
Expand Down
2 changes: 2 additions & 0 deletions src/include/firebird/impl/inf_pub.h
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,8 @@ enum info_db_provider
#define isc_info_sql_stmt_timeout_user 28
#define isc_info_sql_stmt_timeout_run 29
#define isc_info_sql_stmt_blob_align 30
#define isc_info_sql_exec_path_blr_bytes 31
#define isc_info_sql_exec_path_blr_text 32

/*********************************/
/* SQL information return values */
Expand Down
1 change: 1 addition & 0 deletions src/include/gen/Firebird.pas
Original file line number Diff line number Diff line change
Expand Up @@ -1457,6 +1457,7 @@ IStatement = class(IReferenceCounted)
const PREPARE_PREFETCH_FLAGS = Cardinal($40);
const PREPARE_PREFETCH_METADATA = Cardinal(IStatement.PREPARE_PREFETCH_TYPE or IStatement.PREPARE_PREFETCH_FLAGS or IStatement.PREPARE_PREFETCH_INPUT_PARAMETERS or IStatement.PREPARE_PREFETCH_OUTPUT_PARAMETERS);
const PREPARE_PREFETCH_ALL = Cardinal(IStatement.PREPARE_PREFETCH_METADATA or IStatement.PREPARE_PREFETCH_LEGACY_PLAN or IStatement.PREPARE_PREFETCH_DETAILED_PLAN or IStatement.PREPARE_PREFETCH_AFFECTED_RECORDS);
const PREPARE_KEEP_EXEC_PATH = Cardinal($80);
const FLAG_HAS_CURSOR = Cardinal($1);
const FLAG_REPEAT_EXECUTE = Cardinal($2);
const CURSOR_TYPE_SCROLLABLE = Cardinal($1);
Expand Down
Loading

0 comments on commit 55704ef

Please sign in to comment.