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:
commit
885ff64f93
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user