8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 20:43:02 +01:00

Merge pull request #7218 from FirebirdSQL/work/isql-perTableStats

Work/isql per table stats
This commit is contained in:
Vlad Khorsun 2022-07-01 10:18:45 +03:00 committed by GitHub
commit 885ff64f93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 430 additions and 2 deletions

View File

@ -161,7 +161,7 @@ Isql enhancements in Firebird v3.
9) SET KEEP_TRAN_PARAMS option.
Author: Vladyslav Khorsun <hvlad at users sourcefoege net>
Author: Vladyslav Khorsun <hvlad at users sourceforge 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
@ -253,3 +253,74 @@ It requires server v4.0.1 or greater to work.
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.
Isql enhancements in Firebird v5.
---------------------------------
11) SET PER_TABLE_STATS option.
Author: Vladyslav Khorsun <hvlad at users sourceforge net>
When set to ON show per-table run-time statistics after query execution.
It is set to OFF by default. Also, it is independent of SET STATS option.
The name PER_TABLE_STATS could be shortened up to PER_TAB. Tables in output
are sorted by its relation id's.
Example:
-- check current value
SQL> SET;
...
Print per-table stats: OFF
...
-- turn per-table stats on
SQL> SET PER_TABLE_STATS ON;
SQL>
SQL> SELECT COUNT(*) FROM RDB$RELATIONS JOIN RDB$RELATION_FIELDS USING (RDB$RELATION_NAME);
COUNT
=====================
534
Per table statistics:
--------------------------------+---------+---------+---------+---------+---------+---------+---------+---------+
Table name | Natural | Index | Insert | Update | Delete | Backout | Purge | Expunge |
--------------------------------+---------+---------+---------+---------+---------+---------+---------+---------+
RDB$INDICES | | 3| | | | | | |
RDB$RELATION_FIELDS | | 534| | | | | | |
RDB$RELATIONS | 59| | | | | | | |
RDB$SECURITY_CLASSES | | 3| | | | | | |
--------------------------------+---------+---------+---------+---------+---------+---------+---------+---------+
Note, here are present some system tables that was not listed in query - it is
because engine reads some metadata when preparing the query.
-- turn common stats on
SQL> SET STATS ON;
SQL> SELECT COUNT(*) FROM RDB$RELATIONS JOIN RDB$RELATION_FIELDS USING (RDB$RELATION_NAME);
COUNT
=====================
534
Current memory = 3828960
Delta memory = 208
Max memory = 3858576
Elapsed time = 0.001 sec
Buffers = 256
Reads = 0
Writes = 0
Fetches = 715
Per table statistics:
--------------------------------+---------+---------+---------+---------+---------+---------+---------+---------+
Table name | Natural | Index | Insert | Update | Delete | Backout | Purge | Expunge |
--------------------------------+---------+---------+---------+---------+---------+---------+---------+---------+
RDB$RELATION_FIELDS | | 534| | | | | | |
RDB$RELATIONS | 59| | | | | | | |
--------------------------------+---------+---------+---------+---------+---------+---------+---------+---------+
-- turn per-table stats off, using shortened name
SQL> SET PER_TAB OFF;

View File

@ -121,6 +121,7 @@ enum literal_string_type
#include "../isql/PtrSentry.h"
#include "../common/classes/UserBlob.h"
#include "../common/classes/MsgPrint.h"
#include "../common/classes/array.h"
using Firebird::PathName;
using Firebird::TempFile;
@ -584,6 +585,7 @@ public:
ISQL_charset[0] = 0;
KeepTranParams = true;
TranParams->assign(DEFAULT_DML_TRANS_SQL);
PerTableStats = false;
}
ColList global_Cols;
@ -607,6 +609,7 @@ public:
unsigned int StmtTimeout;
SCHAR ISQL_charset[MAXCHARSET_SIZE];
bool KeepTranParams;
bool PerTableStats;
};
static SetValues setValues;
@ -646,6 +649,74 @@ static const SCHAR* alpha_months[] =
"DEC"
};
class PerTableStats
{
public:
PerTableStats(Firebird::MemoryPool& pool) :
m_initialized(false),
m_stat(pool),
m_relNames(pool)
{}
void getStats(Firebird::IAttachment* att, bool initial);
void reset();
private:
void printStats(Firebird::IAttachment* att);
unsigned loadRelNames(Firebird::IAttachment* att);
struct StatItem
{
StatItem() :
relId(0),
statId(0),
value(0)
{}
StatItem(USHORT aRelId, UCHAR aStatID) :
relId(aRelId),
statId(aStatID),
value(0)
{}
USHORT relId;
UCHAR statId;
SINT64 value;
static bool greaterThan(const StatItem& i1, const StatItem& i2)
{
if (i1.relId < i2.relId)
return false;
if (i1.relId > i2.relId)
return true;
if (i1.statId <= i2.statId)
return false;
return true;
}
};
using StatArray = Firebird::SortedArray<
StatItem,
Firebird::EmptyStorage<StatItem>,
StatItem,
Firebird::DefaultKeyValue<StatItem>,
StatItem>;
bool m_initialized; // initial (before) stats loaded OK
StatArray m_stat;
Firebird::GenericMap<Firebird::Right<USHORT, Firebird::string>> m_relNames;
static const int ITEM_COUNT = 8;
static const unsigned char m_items[ITEM_COUNT + 1];
static const char* m_itemNames[ITEM_COUNT];
};
static Firebird::GlobalPtr<PerTableStats> perTableStats;
static UCHAR predefined_blob_subtype_bpb[] =
{
isc_bpb_version1,
@ -5320,7 +5391,7 @@ static processing_state frontend_set(const char* cmd, const char* const* parms,
exec_path_display,
sql, warning, sqlCont, heading, bail,
bulk_insert, maxrows, stmtTimeout,
keepTranParams,
keepTranParams, perTableStats,
wrong
};
SetOptions(const optionsMap* inmap, size_t insize, int wrongval)
@ -5361,6 +5432,7 @@ static processing_state frontend_set(const char* cmd, const char* const* parms,
{SetOptions::stmtTimeout, "LOCAL_TIMEOUT", 0},
{SetOptions::sqlCont, "DECFLOAT", 0},
{SetOptions::keepTranParams, "KEEP_TRAN_PARAMS", 9},
{SetOptions::perTableStats, "PER_TABLE_STATS", 7}
};
// Display current set options
@ -5596,6 +5668,10 @@ static processing_state frontend_set(const char* cmd, const char* const* parms,
break;
case SetOptions::perTableStats:
ret = do_set_command(parms[2], &setValues.PerTableStats);
break;
default:
//{
// TEXT msg_string[MSG_LENGTH];
@ -6386,6 +6462,7 @@ static processing_state print_sets()
**************************************/
print_set("Print statistics:", setValues.Stats);
print_set("Print per-table stats:", setValues.PerTableStats);
print_set("Echo commands:", setValues.Echo);
print_set("List format:", setValues.List);
print_set("Show Row Count:", setValues.Docount);
@ -6838,6 +6915,8 @@ static processing_state newdb(TEXT* dbname,
DB->cancelOperation(fbStatus, fb_cancel_disable);
} // scope
perTableStats->reset();
ISQL_get_version(false);
if (*local_sql_role)
@ -8989,6 +9068,9 @@ static processing_state process_statement(const TEXT* str2)
}
}
if (setValues.PerTableStats)
perTableStats->getStats(DB, true);
// Prepare the dynamic query stored in string.
// But put this on the DDL transaction to get maximum visibility of
// metadata.
@ -9132,6 +9214,10 @@ static processing_state process_statement(const TEXT* str2)
if (setValues.Stats && (print_performance(perf_before) == ps_ERR))
ret = ps_ERR;
if (setValues.PerTableStats)
perTableStats->getStats(DB, false);
return ret;
}
@ -9147,6 +9233,10 @@ static processing_state process_statement(const TEXT* str2)
if (setValues.Stats && (print_performance(perf_before) == ps_ERR))
ret = ps_ERR;
if (setValues.PerTableStats)
perTableStats->getStats(DB, false);
return ret;
}
@ -9190,6 +9280,10 @@ static processing_state process_statement(const TEXT* str2)
if (setValues.Stats && (print_performance(perf_before) == ps_ERR))
ret = ps_ERR;
if (setValues.PerTableStats)
perTableStats->getStats(DB, false);
return ret;
}
@ -9382,6 +9476,9 @@ static processing_state process_statement(const TEXT* str2)
if (setValues.Stats && (print_performance(perf_before) == ps_ERR))
ret = ps_ERR;
if (setValues.PerTableStats)
perTableStats->getStats(DB, false);
if (pad)
ISQL_FREE(pad);
if (line)
@ -9619,3 +9716,263 @@ static const char* charset_to_string(unsigned charset)
return UNKNOWN;
}
/// class PerTableStats
const unsigned char PerTableStats::m_items[] =
{
isc_info_read_seq_count, isc_info_read_idx_count,
isc_info_insert_count, isc_info_update_count, isc_info_delete_count,
isc_info_backout_count, isc_info_purge_count, isc_info_expunge_count,
isc_info_end
};
const char* PerTableStats::m_itemNames[] =
{
" Natural", " Index",
" Insert", " Update", " Delete",
" Backout", " Purge", " Expunge"
};
void PerTableStats::getStats(Firebird::IAttachment* att, bool initial)
{
if (initial)
m_stat.clear();
else if (!m_initialized)
return;
Firebird::Array<unsigned char> buffer(*getDefaultMemoryPool(), 32 * 1024);
unsigned char* buff = buffer.begin();
att->getInfo(fbStatus, sizeof(m_items), m_items, buffer.getCapacity(), buff);
if (ISQL_errmsg(fbStatus))
return;
const unsigned char* p = buff;
const unsigned char* end = buff + buffer.getCapacity();
while (p < end)
{
StatItem item;
item.statId = *p++;
if (item.statId == isc_info_end)
break;
const FB_SIZE_T len = isc_portable_integer(p, 2);
p += 2;
const unsigned char* const end2 = p + len;
while (p < end2)
{
item.relId = isc_portable_integer(p, 2);
p += 2;
item.value = isc_portable_integer(p, 4);
p += 4;
if (initial)
{
if (item.value)
m_stat.add(item);
}
else
{
FB_SIZE_T pos;
if (m_stat.find(item, pos))
{
m_stat[pos].value = item.value - m_stat[pos].value;
if (m_stat[pos].value == 0)
m_stat.remove(pos);
}
else if (item.value)
m_stat.add(item);
}
}
}
if (!initial)
{
printStats(att);
m_stat.clear();
}
m_initialized = initial;
}
void PerTableStats::reset()
{
m_relNames.clear();
}
void PerTableStats::printStats(Firebird::IAttachment* att)
{
if (m_stat.isEmpty())
return;
unsigned maxTabLen = loadRelNames(att);
IUTILS_printf2(Diag, "Per table statistics:%s", NEWLINE);
Firebird::string h;
unsigned lenTable = maxTabLen < 32 ? 32 : FB_ALIGN(maxTabLen, 4);
unsigned lenValue = 9;
h.append(lenTable, '-');
h.append(1, '+');
for (int i = 0; i < ITEM_COUNT; i++)
{
h.append(lenValue, '-');
h.append(1, '+');
}
IUTILS_printf2(Diag, "%s%s", h.c_str(), NEWLINE);
Firebird::string s;
s.printf("%-*s|", lenTable, " Table name");
for (int i = 0; i < ITEM_COUNT; i++)
{
Firebird::string l;
l.printf("%-*s|", lenValue, m_itemNames[i]);
s.append(l);
}
IUTILS_printf2(Diag, "%s%s", s.c_str(), NEWLINE);
s = h;
USHORT relId = MAX_USHORT;
FB_SIZE_T lenName;
for (StatItem* item = m_stat.begin(); item < m_stat.end(); item++)
{
if (relId != item->relId)
{
IUTILS_printf2(Diag, "%s%s", s.c_str(), NEWLINE);
relId = item->relId;
if (Firebird::string* relName = m_relNames.get(relId))
{
IcuUtil::pad(s.getBuffer(lenTable * 4 + 1), isqlGlob.att_charset,
relName->length(), relName->c_str(),
lenTable, false);
s.recalculate_length();
lenName = s.length();
s += '|';
}
else
s.printf("%-*d|", lenTable, item->relId);
for (int i = 0; i < ITEM_COUNT; i++)
{
s.append(lenValue, ' ');
s.append(1, '|');
}
}
for (int i = 0; i < sizeof(m_items) - 1; i++)
{
if (m_items[i] == item->statId)
{
Firebird::string v;
v.printf("%*" UQUADFORMAT, lenValue, item->value);
FB_SIZE_T pos = lenName + 1 + (lenValue + 1) * i;
pos += lenValue - v.length();
s.replace(pos, v.length(), v.begin(), v.length());
}
}
}
if (relId)
{
IUTILS_printf2(Diag, "%s%s", s.c_str(), NEWLINE);
IUTILS_printf2(Diag, "%s%s", h.c_str(), NEWLINE);
}
}
unsigned PerTableStats::loadRelNames(Firebird::IAttachment* att)
{
Firebird::string sIds;
unsigned maxLen = 0;
USHORT relId = MAX_USHORT;
for (StatItem* item = m_stat.begin(); item < m_stat.end(); item++)
{
if (relId == item->relId)
continue;
relId = item->relId;
if (m_relNames.exist(relId))
{
const auto str = m_relNames.get(relId);
const unsigned len = IcuUtil::charLength(isqlGlob.att_charset, str->length(), str->c_str());
if (maxLen < len)
maxLen = len;
continue;
}
Firebird::string s;
s.printf("%u", relId);
if (!sIds.isEmpty())
sIds.append(", ");
sIds.append(s);
}
if (sIds.isEmpty())
return maxLen;
if (!frontendTransaction())
return maxLen; // ?
Firebird::string sql;
sql.printf("SELECT RDB$RELATION_ID, RDB$RELATION_NAME FROM RDB$RELATIONS WHERE RDB$RELATION_ID IN (%s)",
sIds.c_str());
Firebird::IResultSet* rs = att->openCursor(fbStatus, fbTrans,
sql.length(), sql.c_str(), SQL_DIALECT_CURRENT,
NULL, NULL, NULL, NULL,
Firebird::IStatement::PREPARE_PREFETCH_METADATA);
Firebird::IMessageMetadata* outMeta = rs->getMetadata(fbStatus);
unsigned msgLen = outMeta->getMessageLength(fbStatus);
Firebird::HalfStaticArray<char, 512> buff(msgLen);
char* msg = buff.getBuffer(msgLen);
if (outMeta->getType(fbStatus, 0) != SQL_SHORT)
return maxLen;
if (outMeta->getType(fbStatus, 1) != SQL_TEXT)
return maxLen;
SSHORT* pRelID = (SSHORT*) (msg + outMeta->getOffset(fbStatus, 0));
SSHORT* pRelID_Null = (SSHORT*)(msg + outMeta->getNullOffset(fbStatus, 0));
char* pRelName = msg + outMeta->getOffset(fbStatus, 1);
SSHORT* pRelName_Null = (SSHORT*)(msg + outMeta->getNullOffset(fbStatus, 1));
const unsigned relNameSize = outMeta->getLength(fbStatus, 1);
while (rs->fetchNext(fbStatus, msg) != Firebird::IStatus::RESULT_NO_DATA)
{
if (*pRelID_Null == FB_TRUE || *pRelName_Null == FB_TRUE)
continue;
pRelName[relNameSize] = 0;
char* name = fb_utils::exact_name(pRelName);
const unsigned len = IcuUtil::charLength(isqlGlob.att_charset, strlen(name), pRelName);
if (maxLen < len)
maxLen = len;
m_relNames.put(*pRelID, name);
}
outMeta->release();
rs->release();
return maxLen;
}