Source for PostgreSQL patch set
--- /export/mysql/extsql-build/builds/8.4.1/rhel4/x86/1/0.0/src/backend/utils/misc/Makefile 2008-02-19 05:30:09.000000000 -0500
+++ src/backend/utils/misc/Makefile 2009-11-28 09:57:35.000000000 -0500
@@ -15,5 +15,7 @@
override CPPFLAGS := -I$(srcdir) $(CPPFLAGS)
-OBJS = guc.o help_config.o pg_rusage.o ps_status.o superuser.o tzparser.o
+# EXTSQL START
+OBJS = guc.o help_config.o pg_rusage.o ps_status.o superuser.o tzparser.o extsql.o
+# EXTSQL END
# This location might depend on the installation directories. Therefore
--- /export/mysql/extsql-build/builds/8.4.1/rhel4/x86/1/0.0/src/backend/nodes/copyfuncs.c 2009-06-17 21:27:02.000000000 -0400
+++ src/backend/nodes/copyfuncs.c 2009-11-28 10:01:07.000000000 -0500
@@ -2926,4 +2926,65 @@
}
+// EXTSQL START
+static VariableShowStatsStmt *
+_copyVariableShowStatsStmt(VariableShowStatsStmt *from)
+{
+ VariableShowStatsStmt *newnode = makeNode(VariableShowStatsStmt);
+
+ COPY_NODE_FIELD(stats_vars);
+ COPY_STRING_FIELD(stats_class);
+ COPY_STRING_FIELD(wild);
+ COPY_STRING_FIELD(stats_order_var);
+ COPY_STRING_FIELD(stats_where_var);
+ COPY_SCALAR_FIELD(stats_order_dir);
+ COPY_SCALAR_FIELD(stats_order_limit);
+ COPY_SCALAR_FIELD(stats_hourly);
+ COPY_SCALAR_FIELD(stats_where_num);
+ COPY_SCALAR_FIELD(stats_where_char);
+
+ return newnode;
+}
+
+static VariableStatisticsUsage *
+_copyVariableStatisticsUsage(VariableStatisticsUsage *from)
+{
+ VariableStatisticsUsage *newnode = makeNode(VariableStatisticsUsage);
+
+ return newnode;
+}
+
+static VariableStatisticsEnable *
+_copyVariableStatisticsEnable(VariableStatisticsEnable *from)
+{
+ VariableStatisticsEnable *newnode = makeNode(VariableStatisticsEnable);
+
+ COPY_SCALAR_FIELD(stats_status);
+
+ return newnode;
+}
+
+static VariableStatisticsReset *
+_copyVariableStatisticsReset(VariableStatisticsReset *from)
+{
+ VariableStatisticsReset *newnode = makeNode(VariableStatisticsReset);
+
+ COPY_STRING_FIELD(config_file);
+
+ return newnode;
+}
+
+static VariableStatisticsFileIO *
+_copyVariableStatisticsFileIO(VariableStatisticsFileIO *from)
+{
+ VariableStatisticsFileIO *newnode = makeNode(VariableStatisticsFileIO);
+
+ COPY_SCALAR_FIELD(stats_io_mode);
+ COPY_SCALAR_FIELD(stats_confirm);
+ COPY_STRING_FIELD(stats_reload_file);
+
+ return newnode;
+}
+// EXTSQL END
+
static DiscardStmt *
_copyDiscardStmt(DiscardStmt *from)
@@ -3892,4 +3953,21 @@
retval = _copyVariableShowStmt(from);
break;
+ // EXTSQL START
+ case T_VariableShowStatsStmt:
+ retval = _copyVariableShowStatsStmt(from);
+ break;
+ case T_VariableStatisticsUsage:
+ retval = _copyVariableStatisticsUsage(from);
+ break;
+ case T_VariableStatisticsEnable:
+ retval = _copyVariableStatisticsEnable(from);
+ break;
+ case T_VariableStatisticsReset:
+ retval = _copyVariableStatisticsReset(from);
+ break;
+ case T_VariableStatisticsFileIO:
+ retval = _copyVariableStatisticsFileIO(from);
+ break;
+ // EXTSQL END
case T_DiscardStmt:
retval = _copyDiscardStmt(from);
--- /export/mysql/extsql-build/builds/8.4.1/rhel4/x86/1/0.0/src/backend/nodes/equalfuncs.c 2009-06-17 21:27:02.000000000 -0400
+++ src/backend/nodes/equalfuncs.c 2009-11-28 10:04:00.000000000 -0500
@@ -1510,4 +1510,54 @@
}
+// EXTSQL START
+static bool
+_equalVariableShowStatsStmt(VariableShowStatsStmt *a, VariableShowStatsStmt *b)
+{
+ COMPARE_NODE_FIELD(stats_vars);
+ COMPARE_STRING_FIELD(stats_class);
+ COMPARE_STRING_FIELD(wild);
+ COMPARE_STRING_FIELD(stats_order_var);
+ COMPARE_STRING_FIELD(stats_where_var);
+ COMPARE_SCALAR_FIELD(stats_order_dir);
+ COMPARE_SCALAR_FIELD(stats_order_limit);
+ COMPARE_SCALAR_FIELD(stats_where_num);
+ COMPARE_SCALAR_FIELD(stats_where_char);
+
+ return true;
+}
+
+static bool
+_equalVariableStatisticsUsage(VariableStatisticsUsage *a, VariableStatisticsUsage *b)
+{
+ return true;
+}
+
+static bool
+_equalVariableStatisticsEnable(VariableStatisticsEnable *a, VariableStatisticsEnable *b)
+{
+ COMPARE_SCALAR_FIELD(stats_status);
+
+ return true;
+}
+
+static bool
+_equalVariableStatisticsReset(VariableStatisticsReset *a, VariableStatisticsReset *b)
+{
+ COMPARE_STRING_FIELD(config_file);
+
+ return true;
+}
+
+static bool
+_equalVariableStatisticsFileIO(VariableStatisticsFileIO *a, VariableStatisticsFileIO *b)
+{
+ COMPARE_SCALAR_FIELD(stats_io_mode);
+ COMPARE_SCALAR_FIELD(stats_confirm);
+ COMPARE_STRING_FIELD(stats_reload_file);
+
+ return true;
+}
+// EXTSQL END
+
static bool
_equalDiscardStmt(DiscardStmt *a, DiscardStmt *b)
@@ -2669,4 +2719,21 @@
retval = _equalVariableShowStmt(a, b);
break;
+ // EXTSQL START
+ case T_VariableShowStatsStmt:
+ retval = _equalVariableShowStatsStmt(a, b);
+ break;
+ case T_VariableStatisticsUsage:
+ retval = _equalVariableStatisticsUsage(a, b);
+ break;
+ case T_VariableStatisticsEnable:
+ retval = _equalVariableStatisticsEnable(a, b);
+ break;
+ case T_VariableStatisticsReset:
+ retval = _equalVariableStatisticsReset(a, b);
+ break;
+ case T_VariableStatisticsFileIO:
+ retval = _equalVariableStatisticsFileIO(a, b);
+ break;
+ // EXTSQL END
case T_DiscardStmt:
retval = _equalDiscardStmt(a, b);
--- /export/mysql/extsql-build/builds/8.4.1/rhel4/x86/1/0.0/src/backend/parser/gram.y 2009-07-05 22:58:48.000000000 -0400
+++ src/backend/parser/gram.y 2009-11-28 10:10:19.000000000 -0500
@@ -391,4 +391,13 @@
%type TableLikeOptionList
%type TableLikeOption
+
+/* EXTSQL START */
+ %type stats_order_dir stats_compare stats_hourly stats_limit_clause
+ maybe_confirm
+ %type wild stats_class stats_var maybe_file_name
+ %type stats_var_list
+ %type stats_where_clause stats_order_clause StatisticsStmt
+/* EXTSQL END */
+
%type ColQualList
%type ColConstraint ColConstraintElem ConstraintAttr
@@ -456,4 +465,9 @@
HANDLER HAVING HEADER_P HOLD HOUR_P
+ /* EXTSQL START */
+ CONFIRM
+ HISTORY
+ /* EXTSQL END */
+
IDENTITY_P IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P
INCLUDING INCREMENT INDEX INDEXES INHERIT INHERITS INITIALLY
@@ -690,4 +704,7 @@
| RuleStmt
| SelectStmt
+ // EXTSQL START
+ | StatisticsStmt
+ // EXTSQL END
| TransactionStmt
| TruncateStmt
@@ -1400,4 +1417,38 @@
$$ = (Node *) n;
}
+ /* EXTSQL START */
+ | SHOW STATISTICS
+ {
+ VariableShowStatsStmt *n = makeNode(VariableShowStatsStmt);
+ n->stats_vars = NULL;
+ n->stats_class = NULL;
+ n->wild = NULL;
+ n->stats_where_var = NULL;
+ n->stats_where_num = 0;
+ n->stats_where_char = '0';
+ n->stats_order_var = NULL;
+ n->stats_order_dir = 0;
+ n->stats_hourly = 0;
+ n->stats_order_limit = 0;
+ $$ = (Node *) n;
+ }
+
+ /* 3 4 5 6 7 8 9 10 */
+ | SHOW STATISTICS stats_var_list FROM stats_class wild stats_where_clause stats_order_clause stats_hourly stats_limit_clause
+ {
+ VariableShowStatsStmt *n = makeNode(VariableShowStatsStmt);
+ n->stats_vars = $3;
+ n->stats_class = $5;
+ n->wild = $6;
+ n->stats_where_var = ((VariableShowStatsStmt *) $7)->stats_where_var;
+ n->stats_where_num = ((VariableShowStatsStmt *) $7)->stats_where_num;
+ n->stats_where_char = ((VariableShowStatsStmt *) $7)->stats_where_char;
+ n->stats_order_var = ((VariableShowStatsStmt *) $8)->stats_order_var;
+ n->stats_order_dir = ((VariableShowStatsStmt *) $8)->stats_order_dir;
+ n->stats_hourly = $9;
+ n->stats_order_limit = $10;
+ $$ = (Node *) n;
+ }
+ /* EXTSQL END */
| SHOW TIME ZONE
{
@@ -1426,4 +1477,152 @@
;
+/* EXTSQL START */
+
+stats_class:
+ '*' { $$ = "*"; }
+ | ColId ;
+
+stats_compare:
+ '=' { $$ = '='; }
+ | '<' { $$ = '<'; }
+ | '>' { $$ = '>'; };
+
+stats_hourly :
+ /* empty */ { $$=0; }
+ | HISTORY { $$=1; };
+
+stats_limit_clause:
+ /* empty */
+ {
+ $$ = 0;
+ }
+
+ | LIMIT ICONST
+ {
+ $$ = $2;
+ }
+ ;
+
+stats_order_clause:
+ /* empty */
+ {
+ VariableShowStatsStmt *n = makeNode(VariableShowStatsStmt);
+ n->stats_order_var = NULL;
+ n->stats_order_dir = 0 ;
+ $$ = (Node *) n;
+ }
+
+ | ORDER BY stats_var stats_order_dir
+ {
+
+ VariableShowStatsStmt *n = makeNode(VariableShowStatsStmt);
+ n->stats_order_var = $3;
+ n->stats_order_dir = $4 ;
+ $$ = (Node *) n;
+ }
+ ;
+
+stats_order_dir:
+ /* empty */ { $$ = 1; }
+ | ASC { $$ =1; }
+ | DESC { $$ =0; };
+
+stats_var: '*'
+ | ColId ;
+
+stats_var_list:
+ stats_var_list ',' stats_var { $$ = lappend($1, makeString($3)); }
+ | stats_var { $$ = list_make1(makeString($1)); }
+ ;
+
+stats_where_clause:
+ /* empty */
+ {
+ VariableShowStatsStmt *n = makeNode(VariableShowStatsStmt);
+ n->stats_where_var = NULL;
+ n->stats_where_char = 0;
+ n->stats_where_num = 0;
+ $$ = (Node *) n;
+ }
+
+ | WHERE stats_var stats_compare ICONST
+ {
+ VariableShowStatsStmt *n = makeNode(VariableShowStatsStmt);
+ n->stats_where_var = $2;
+ n->stats_where_char = $3;
+ n->stats_where_num = $4;
+ $$ = (Node *) n;
+ }
+ ;
+
+wild:
+ LIKE SCONST { $$ = $2; }
+| /* empty */{ $$ = NULL;} ;
+
+
+StatisticsStmt:
+ STATISTICS
+ {
+ VariableStatisticsUsage *n = makeNode(VariableStatisticsUsage);
+ $$ = (Node *) n;
+ }
+ | STATISTICS ON
+ {
+ VariableStatisticsEnable *n = makeNode(VariableStatisticsEnable);
+ n->stats_status = 1;
+ $$ = (Node *) n;
+ }
+ | STATISTICS OFF
+ {
+ VariableStatisticsEnable *n = makeNode(VariableStatisticsEnable);
+ n->stats_status = 0;
+ $$ = (Node *) n;
+ }
+ | STATISTICS RESET maybe_file_name
+ {
+ VariableStatisticsReset *n = makeNode(VariableStatisticsReset);
+ n->config_file = $3;
+ $$ = (Node *) n;
+ }
+ | STATISTICS READ maybe_confirm file_name
+ {
+ VariableStatisticsFileIO *n = makeNode(VariableStatisticsFileIO);
+ n->stats_io_mode = 0;
+ n->stats_confirm = $3;
+ n->stats_reload_file = $4;
+ $$ = (Node *) n;
+ }
+ | STATISTICS WRITE file_name
+ {
+ VariableStatisticsFileIO *n = makeNode(VariableStatisticsFileIO);
+ n->stats_io_mode = 1;
+ n->stats_confirm = 0;
+ n->stats_reload_file = $3;
+ $$ = (Node *) n;
+ }
+ ;
+
+maybe_file_name:
+ /* nothing */
+ {
+ $$ = NULL;
+ }
+ | file_name
+ {
+ $$ = $1;
+ }
+ ;
+
+maybe_confirm:
+ /* nothing */
+ {
+ $$ = 0;
+ }
+ | CONFIRM
+ {
+ $$ = 1;
+ }
+ ;
+/* EXTSQL END */
ConstraintsSetStmt:
--- /export/mysql/extsql-build/builds/8.4.1/rhel4/x86/1/0.0/src/backend/postmaster/postmaster.c 2009-08-24 16:08:40.000000000 -0400
+++ src/backend/postmaster/postmaster.c 2009-11-28 13:50:13.000000000 -0500
@@ -1024,4 +1024,21 @@
load_ident();
+ // EXTSQL START
+ int startType = 0;
+
+ // if the default config params contain a non null
+ // extsql_reload_file and its contents look valid, we will set
+ // startType to STATS_INIT_RELOAD, otherwise STATS_INIT_NORMAL
+ elog(LOG, "ExtSQL(postmaster): ready to call extsql_prep and extsql_init");
+ extsql_prep(&startType, NULL, extsql_reload_file);
+
+ // if reload fails here, just do a regular startup
+ if (startType == STATS_INIT_FAILURE) {
+ startType = STATS_INIT_NORMAL;
+ }
+
+ extsql_init(Shared->extsql_class_list, startType, Shared->extsql_reload_file);
+ // EXTSQL END
+
/*
* Remember postmaster startup time
@@ -4034,4 +4051,9 @@
ExitPostmaster(int status)
{
+ // EXTSQL START
+ if (*extsql_reload_file)
+ if (extsql_reload(STATS_WRITE_RELOAD, extsql_reload_file))
+ sql_print_error("ExtSQL: Error encountered during reload WRITE");
+ // EXTSQL END
/* should cleanup shared memory and kill all backends */
--- /export/mysql/extsql-build/builds/8.4.1/rhel4/x86/1/0.0/src/backend/tcop/pquery.c 2009-06-11 10:49:02.000000000 -0400
+++ src/backend/tcop/pquery.c 2009-11-28 13:59:56.000000000 -0500
@@ -1176,4 +1176,11 @@
IsA(utilityStmt, VariableSetStmt) ||
IsA(utilityStmt, VariableShowStmt) ||
+ // EXTSQL START
+ IsA(utilityStmt, VariableShowStatsStmt) ||
+ IsA(utilityStmt, VariableStatisticsUsage) ||
+ IsA(utilityStmt, VariableStatisticsEnable) ||
+ IsA(utilityStmt, VariableStatisticsReset) ||
+ IsA(utilityStmt, VariableStatisticsFileIO) ||
+ // EXTSQL END
IsA(utilityStmt, ConstraintsSetStmt) ||
/* efficiency hacks from here down */
--- /export/mysql/extsql-build/builds/8.4.1/rhel4/x86/1/0.0/src/backend/tcop/utility.c 2009-06-11 16:46:11.000000000 -0400
+++ src/backend/tcop/utility.c 2009-11-28 13:58:22.000000000 -0500
@@ -58,4 +58,9 @@
#include "utils/syscache.h"
+// EXTSQL START
+#ifndef EXTSQL_H
+#include "utils/extsql.h"
+#endif
+// EXTSQL END
/*
@@ -924,4 +929,62 @@
break;
+ // EXTSQL START
+ case T_VariableShowStatsStmt:
+ {
+ char varList[100] = {'\0'};
+ VariableShowStatsStmt *n = (VariableShowStatsStmt *) parsetree;
+
+ if (0) {
+ elog(LOG, "ExtSQL: internal variables (%s) class %s, \
+ hourly %d, wild %s, ORDER BY %s, direction %d, LIMIT %d, \
+ WHERE var %s, compare %d, value %d",
+ varList, n->stats_class, n->stats_hourly, n->wild,
+ n->stats_order_var,
+ n->stats_order_dir, n->stats_order_limit,
+ n->stats_where_var,
+ n->stats_where_char, n->stats_where_num);
+
+ }
+
+ mysql_show_extsql(MyProc, n->stats_class, dest, n->stats_vars,
+ n->stats_hourly, n->wild, n->stats_order_var,
+ n->stats_order_dir, n->stats_order_limit,
+ n->stats_where_var, n->stats_where_num,
+ n->stats_where_char);
+ }
+ break;
+
+ case T_VariableStatisticsUsage:
+ {
+ extsql_usage(MyProc, dest);
+ }
+ break;
+
+ case T_VariableStatisticsEnable:
+ {
+ VariableStatisticsEnable *n = (VariableStatisticsEnable *) parsetree;
+
+ extsql_enable(MyProc, dest, n->stats_status);
+ }
+ break;
+
+ case T_VariableStatisticsReset:
+ {
+ VariableStatisticsReset *n = (VariableStatisticsReset *) parsetree;
+
+ extsql_reset(MyProc, dest, n->config_file);
+ }
+ break;
+
+ case T_VariableStatisticsFileIO:
+ {
+ VariableStatisticsFileIO *n = (VariableStatisticsFileIO *) parsetree;
+
+ extsql_file_io(MyProc, dest, n->stats_io_mode,
+ n->stats_confirm, n->stats_reload_file);
+ }
+ break;
+ // EXTSQL END
+
case T_DiscardStmt:
DiscardCommand((DiscardStmt *) parsetree, isTopLevel);
@@ -1151,4 +1214,21 @@
return true;
+ // EXTSQL START
+ case T_VariableShowStatsStmt:
+ return false;
+
+ case T_VariableStatisticsUsage:
+ return false;
+
+ case T_VariableStatisticsEnable:
+ return false;
+
+ case T_VariableStatisticsReset:
+ return false;
+
+ case T_VariableStatisticsFileIO:
+ return false;
+ // EXTSQL END
+
default:
return false;
@@ -1205,4 +1285,38 @@
}
+ // EXTSQL START
+ case T_VariableShowStatsStmt:
+ {
+ VariableShowStatsStmt *n = (VariableShowStatsStmt *) parsetree;
+
+ return GetPGVariableResultDesc(n->stats_class);
+ }
+
+ case T_VariableStatisticsUsage:
+ {
+ return NULL;
+ }
+
+ case T_VariableStatisticsEnable:
+ {
+ VariableStatisticsEnable *n = (VariableStatisticsEnable *) parsetree;
+
+ return GetPGVariableResultDesc(n->stats_status);
+ }
+
+ case T_VariableStatisticsReset:
+ {
+ VariableStatisticsReset *n = (VariableStatisticsReset *) parsetree;
+
+ return GetPGVariableResultDesc(n->config_file);
+ }
+ case T_VariableStatisticsFileIO:
+ {
+ VariableStatisticsFileIO *n = (VariableStatisticsFileIO *) parsetree;
+
+ return GetPGVariableResultDesc(n->stats_reload_file);
+ }
+ // EXTSQL END
+
default:
return NULL;
@@ -1262,4 +1376,8 @@
const char *tag;
+ // EXTSQL START ZZ - put a function call here to capture all
+ extsql_inc(&Questions, 1);
+ // EXTSQL END
+
switch (nodeTag(parsetree))
{
@@ -1267,16 +1385,28 @@
case T_InsertStmt:
tag = "INSERT";
+ // EXTSQL START
+ extsql_inc(&Com_insert, 1);
+ // EXTSQL END
break;
case T_DeleteStmt:
tag = "DELETE";
+ // EXTSQL START
+ extsql_inc(&Com_delete, 1);
+ // EXTSQL END
break;
case T_UpdateStmt:
tag = "UPDATE";
+ // EXTSQL START
+ extsql_inc(&Com_update, 1);
+ // EXTSQL END
break;
case T_SelectStmt:
tag = "SELECT";
+ // EXTSQL START
+ extsql_inc(&Com_select, 1);
+ // EXTSQL END
break;
@@ -1290,4 +1420,7 @@
case TRANS_STMT_BEGIN:
tag = "BEGIN";
+ // EXTSQL START
+ extsql_inc(&Com_begin, 1);
+ // EXTSQL END
break;
@@ -1298,4 +1431,7 @@
case TRANS_STMT_COMMIT:
tag = "COMMIT";
+ // EXTSQL START
+ extsql_inc(&Com_commit, 1);
+ // EXTSQL END
break;
@@ -1303,4 +1439,7 @@
case TRANS_STMT_ROLLBACK_TO:
tag = "ROLLBACK";
+ // EXTSQL START
+ extsql_inc(&Com_rollback, 1);
+ // EXTSQL END
break;
@@ -1365,4 +1504,7 @@
case T_CreateStmt:
tag = "CREATE TABLE";
+ // EXTSQL START
+ extsql_inc(&Com_create_table, 1);
+ // EXTSQL END
break;
@@ -1416,4 +1558,7 @@
case OBJECT_TABLE:
tag = "DROP TABLE";
+ // EXTSQL START
+ extsql_inc(&Com_drop_table, 1);
+ // EXTSQL END
break;
case OBJECT_SEQUENCE:
@@ -1828,4 +1973,26 @@
break;
+ // EXTSQL START
+ case T_VariableShowStatsStmt:
+ tag = "SHOW STATISTICS";
+ break;
+
+ case T_VariableStatisticsUsage:
+ tag = "STATISTICS";
+ break;
+
+ case T_VariableStatisticsEnable:
+ tag = "STATISTICS";
+ break;
+
+ case T_VariableStatisticsReset:
+ tag = "STATISTICS";
+ break;
+
+ case T_VariableStatisticsFileIO:
+ tag = "STATISTICS";
+ break;
+ // EXTSQL END
+
case T_DiscardStmt:
switch (((DiscardStmt *) parsetree)->target)
--- /export/mysql/extsql-build/builds/8.4.1/rhel4/x86/1/0.0/src/backend/utils/init/postinit.c 2009-07-08 13:53:32.000000000 -0400
+++ src/backend/utils/init/postinit.c 2009-11-28 14:02:41.000000000 -0500
@@ -652,4 +652,27 @@
pgstat_bestart();
+ // EXTSQL START
+ if (bootstrap) {
+ int startType = 0;
+
+ // if the default config params contain a non null
+ // extsql_reload_file and its contents look valid, we will set
+ // startType to STATS_INIT_RELOAD, otherwise STATS_INIT_NORMAL
+ // elog(LOG, "ExtSQL(postinit-1): ready to call extsql_prep and extsql_init");
+ extsql_prep(&startType, NULL, extsql_reload_file);
+
+ // if reload fails here, just do a regular startup
+ if (startType == STATS_INIT_FAILURE) {
+ startType = STATS_INIT_NORMAL;
+ }
+ extsql_init(Shared->extsql_class_list, startType, Shared->extsql_reload_file);
+
+ } else {
+ // elog(LOG, "ExtSQL(postinit-2): skip extsql_prep, ready to call extsql_thread_init");
+ extsql_thread_init(MyProc, STATS_THD_INIT_NORM);
+
+ }
+ // EXTSQL END
+
/* close the transaction we started above */
if (!bootstrap)
--- /export/mysql/extsql-build/builds/8.4.1/rhel4/x86/1/0.0/src/backend/utils/misc/guc.c 2009-09-03 18:08:14.000000000 -0400
+++ src/backend/utils/misc/guc.c 2009-11-29 09:09:33.000000000 -0500
@@ -1641,4 +1641,33 @@
},
+ // EXTSQL START
+ {
+ {"extsql_active", PGC_POSTMASTER, DEVELOPER_OPTIONS,
+ gettext_noop("Set the Extended Statistics internal debug level for output to error log. Use with caution!"),
+ NULL
+ },
+ (int *)&extsql_active,
+ 0, 0, INT_MAX, NULL, NULL
+ },
+
+ {
+ {"extsql_counter", PGC_POSTMASTER, DEVELOPER_OPTIONS,
+ gettext_noop("Set the Extended Statistics internal counter. Use with caution!"),
+ NULL
+ },
+ (int *)&extsql_counter,
+ 0, 0, INT_MAX, NULL, NULL
+ },
+
+ {
+ {"extsql_debug", PGC_POSTMASTER, DEVELOPER_OPTIONS,
+ gettext_noop("Set the Extended Statistics debug level for output to error log. Use with caution!"),
+ NULL
+ },
+ (int *)&extsql_debug,
+ 0, 0, INT_MAX, NULL, NULL
+ },
+ // EXTSQL END
+
{
{"commit_delay", PGC_USERSET, WAL_SETTINGS,
@@ -2301,4 +2330,42 @@
},
+ // EXTSQL START
+ {
+ {"extsql_class_list", PGC_POSTMASTER, DEVELOPER_OPTIONS,
+ gettext_noop("This is a specially formatted list of class definitions that define what statistics are collected."),
+ NULL
+ },
+ &extsql_class_list,
+ "", NULL, NULL
+ },
+
+ {
+ {"extsql_key", PGC_POSTMASTER, DEVELOPER_OPTIONS,
+ gettext_noop("This is a license key used with a binary distribution of ExtSQL."),
+ NULL
+ },
+ &extsql_key,
+ "", NULL, NULL
+ },
+
+ {
+ {"extsql_users", PGC_POSTMASTER, DEVELOPER_OPTIONS,
+ gettext_noop("This contains an additional list of userids allowed to run statistics (root always can)."),
+ NULL
+ },
+ &extsql_users,
+ "", NULL, NULL
+ },
+
+ {
+ {"extsql_reload_file", PGC_POSTMASTER, DEVELOPER_OPTIONS,
+ gettext_noop("Allow the server to record statistics data in the specified file and reload upon restart."),
+ NULL
+ },
+ &extsql_reload_file,
+ "", NULL, NULL
+ },
+ // EXTSQL END
+
{
/* Not for general use --- used by SET ROLE */
--- /export/mysql/extsql-build/builds/8.4.1/rhel4/x86/1/0.0/src/include/nodes/nodes.h 2009-06-11 10:49:11.000000000 -0400
+++ src/include/nodes/nodes.h 2009-11-28 14:08:32.000000000 -0500
@@ -291,4 +291,11 @@
T_VariableSetStmt,
T_VariableShowStmt,
+ /* EXTSQL START */
+ T_VariableShowStatsStmt,
+ T_VariableStatisticsUsage,
+ T_VariableStatisticsEnable,
+ T_VariableStatisticsReset,
+ T_VariableStatisticsFileIO,
+ /* EXTSQL END */
T_DiscardStmt,
T_CreateTrigStmt,
--- /export/mysql/extsql-build/builds/8.4.1/rhel4/x86/1/0.0/src/include/nodes/parsenodes.h 2009-06-17 21:27:02.000000000 -0400
+++ src/include/nodes/parsenodes.h 2009-11-28 14:11:34.000000000 -0500
@@ -1314,4 +1314,69 @@
} VariableShowStmt;
+/* EXTSQL START */
+/* ----------------------
+ * Show Statistics Statement
+ * ----------------------
+ */
+
+typedef struct VariableShowStatsStmt
+{
+ NodeTag type;
+ List *stats_vars;
+ char *stats_class;
+ char *wild;
+ char *stats_order_var;
+ char *stats_where_var;
+ int stats_order_dir;
+ int stats_order_limit;
+ int stats_hourly;
+ int stats_where_num;
+ int stats_where_char;
+} VariableShowStatsStmt;
+
+/* ----------------------
+ * Statistics Statement
+ * ----------------------
+ */
+
+typedef struct VariableStatisticsUsage
+{
+ NodeTag type;
+} VariableStatisticsUsage;
+
+/* ----------------------
+ * Statistics ON|OFF Statement
+ * ----------------------
+ */
+
+typedef struct VariableStatisticsEnable
+{
+ NodeTag type;
+ int stats_status;
+} VariableStatisticsEnable;
+
+/* ----------------------
+ * Statistics RESET Statement
+ * ----------------------
+ */
+
+typedef struct VariableStatisticsReset
+{
+ NodeTag type;
+ char *config_file;
+} VariableStatisticsReset;
+
+/* ----------------------
+ * Statistics READ|WRITE Statement
+ * ----------------------
+ */
+typedef struct VariableStatisticsFileIO
+{
+ NodeTag type;
+ int stats_io_mode, stats_confirm;
+ char *stats_reload_file;
+} VariableStatisticsFileIO;
+/* EXTSQL END */
+
/* ----------------------
* Create Table Statement
--- /export/mysql/extsql-build/builds/8.4.1/rhel4/x86/1/0.0/src/include/storage/proc.h 2009-02-23 04:28:50.000000000 -0500
+++ src/include/storage/proc.h 2009-11-28 14:13:14.000000000 -0500
@@ -99,4 +99,12 @@
struct PGPROC *lwWaitLink; /* next waiter for same LW lock */
+ // EXTSQL START
+ #define STATS_MAX_CLASSES 7
+ int statIndex[STATS_MAX_CLASSES]; // ZZ - stores the indexes to use when incrementing
+ // quick lookup into statsData
+
+ int statVerify; // store the magic here to verify a good set
+ // EXTSQL END
+
/* Info about lock the process is currently waiting for, if any. */
/* waitLock and waitProcLock are NULL if not currently waiting. */
--- /export/mysql/extsql-build/builds/8.4.1/rhel4/x86/1/0.0/src/include/tcop/dest.h 2009-06-11 10:49:12.000000000 -0400
+++ src/include/tcop/dest.h 2009-11-28 14:14:46.000000000 -0500
@@ -123,4 +123,7 @@
CommandDest mydest;
/* Private fields might appear beyond this point... */
+ // EXTSQL START
+ int verify;
+ // EXTSQL END
};
--- /export/mysql/extsql-build/builds/8.4.1/rhel4/x86/1/0.0/src/include/utils/guc.h 2009-09-03 18:08:14.000000000 -0400
+++ src/include/utils/guc.h 2009-11-28 14:15:42.000000000 -0500
@@ -18,4 +18,7 @@
#include "utils/array.h"
+// EXTSQL START
+#include "utils/extsql.h"
+// EXTSQL END
/*
--- /export/mysql/extsql-build/builds/8.4.1/rhel4/x86/1/0.0/src/include/parser/kwlist.h 2009-04-06 04:42:53.000000000 -0400
+++ src/include/parser/kwlist.h 2009-11-28 10:14:27.000000000 -0500
@@ -85,4 +85,7 @@
PG_KEYWORD("concurrently", CONCURRENTLY, UNRESERVED_KEYWORD)
PG_KEYWORD("configuration", CONFIGURATION, UNRESERVED_KEYWORD)
+// EXTSQL START
+PG_KEYWORD("confirm", CONFIRM, UNRESERVED_KEYWORD)
+// EXTSQL END
PG_KEYWORD("connection", CONNECTION, UNRESERVED_KEYWORD)
PG_KEYWORD("constraint", CONSTRAINT, RESERVED_KEYWORD)
--- /export/mysql/extsql-build/builds/8.4.1/rhel4/x86/1/0.0/src/backend/utils/misc/extsql.c 1969-12-31 19:00:00.000000000 -0500
+++ src/backend/utils/misc/extsql.c 2009-12-01 08:13:40.000000000 -0500
@@ -0,0 +1,4437 @@
+/* Copyright (C) 2006, 2007, 2008, 2009, 2010 Software Workshop Incorporated
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#define PGSQL 1
+
+#ifdef PGSQL
+#include "utils/extsql.h"
+
+#else // MYSQL
+
+#include "mysql_priv.h"
+#include "stacktrace.h"
+
+#endif // PGSQL or MYSQL
+
+// DEBUG FLAGS -- we show decimal, has to be used in SET GLOBAL extsql_debug= val
+#define STATS_DUMP 1 // dump stats internal data on every SHOW command
+#define STATS_DUMP_ONCE 2 // just dump it once and clear stats_debug ZZ
+#define STATS_SHOW_PARAMS 4 // show parameters to SHOW STATISTICS
+#define STATS_INC_PARAMS 8 // print warning on bad param to extsql_inc
+#define STATS_ADMIN 16 // allow write/read/reset commands
+#define STATS_TEST_TWO 32 // testing
+#define STATS_INC_VAR 64 // show details on stats increment
+#define STATS_THD_INIT_PARAMS 128 // show parameters on thread init
+#define STATS_THD_INIT_LOOP 256 // show search for matching/new instance of class
+#define STATS_DISABLE_INC 512 // disable extsql_inc processing
+#define STATS_THREAD_CHK 1024 // do a check of the thread on each inc, only print errors
+#define STATS_DUMP_START 2048 // dump all tracking info at server start
+#define STATS_TIME_DETAIL 4096 // show start secs/minutes if min/hour are units
+#define STATS_EXTRA_VARS 8192 // allow user to select untested vars for tracking
+#define STATS_MEM_ALLOC 16384 // show startup mem allocs
+#define STATS_TIME_CHANGE 32768 // show start of time roll over processing
+#define STATS_RELOAD 65536 // show reload processing
+#define STATS_TEST_ONE 131072 // testing
+#define STATS_PID 262144 // show server pid as part of show statistics, for gdb
+
+#define PROG_POINT(x,y) sql_print_information("ExtSQL: PROGRESS POINT %s (0x%x)", x, y)
+
+// LIMITS
+#define STATS_MAX_TIME_LABEL 20
+#define STATS_WARNING_LIMIT 10
+
+#define MAX_BUFF_SIZE 2048 // used for temporary buffs of user input (product of next two)
+#define MAX_VAR_SIZE 45 // max length we allows for a Var name that is tracked.
+#define MAX_VARS_IN_CLASS 50 // max number of vars in single class def & row of output
+#define DATE_SIZE 30
+
+// MISC
+#define ARRAY_INDEX(i,j,size_j,k,size_k) ( (i * size_j +j) * size_k + k)
+
+
+// just testing
+
+time_t StartTime;
+
+// GLOBAL's defined in GLOBAL struct, extsql.h,
+// few vars here where compiler needs static addresses. Set global in extsql_init_globals
+
+// these are globals initially set in postgresql.conf -- extsql_init has not been called yet,
+// need to have a static address allocated, extsql_init_globals will then copy to Shared.
+ulong extsql_active=0, extsql_counter=0, extsql_debug=0, queries=0, connects=0;
+char *extsql_class_list=NULL, *extsql_reload_file=NULL;
+
+
+#ifdef PGSQL
+
+pGLOBAL Shared; // Used for shared memory/globals - only dynamic allocation at start.
+
+#else // MYSQL
+
+GLOBAL GShared; // can be static, shared between threads
+pGLOBAL Shared = &GShared;
+
+#endif
+
+
+// CONSTANT GLOBALS HERE
+
+char *extsqlClassesSupported[STATS_MAX_CLASSES] = {"condb", "conuser", "db", "host", "server", "user", NULL};
+char *extsql_key, *extsql_users;
+static char *extsql_version = "\nExtSQL version: __STATS_VERSION__\n"; // keep \n at start and end!
+
+
+// vars we have tested and confirmed with, they can set STATS_EXTRA_VARS to override.
+static char *allowedVarsArray[MAX_VARS] = {"Com_begin", "Com_commit", "Com_create_table",
+ "Com_delete", "Com_drop_table", "Com_insert", "Com_lock_tables", "Com_replace",
+ "Com_rollback", "Com_select", "Com_set_option", "Com_show_fields", "Com_stmt_execute",
+ "Com_stmt_prepare", "Com_update", "Connections", "Created_tmp_tables",
+ "Created_tmp_disk_tables", "Handler_read_rnd", "Handler_read_rnd_next",
+ "Qcache_hits", "Questions", "Select_full_join", "Select_range_check",
+ "Select_scan", "Slow_queries", NULL };
+
+static char *startTypeName[] = { "FAILURE", "NORMAL", "RESET", "RELOAD" };
+
+char **allowedVars = allowedVarsArray;
+
+static char *adminDisabled = "ExtSQL: In this release STATISTICS RESET/READ/WRITE are disabled by default due \
+to incomplete testing. If you wish to use these commands you must added the following to the server \
+config file and restart the server: extsql_debug = 16";
+
+
+// FUNCTION prototypes
+
+int extsql_init_globals(void);
+static void extsql_stats_dump(void);
+static void extsql_thread_dump(THD *thd, int doDump);
+static int extsql_store_var_addr(char *varAddr);
+static void extsql_init_proc_vars(int *threadID, int *command, char **user, char **db, char **host,
+ char **proc_info, int **indexPtr, int **statVerify);
+
+
+#ifdef PGSQL
+
+ulong Com_begin, Com_commit, Com_create_table, Com_delete,
+ Com_drop_table, Com_insert, Com_rollback, Com_select, Com_update, Connections, Questions;
+
+char *ShmemPtr = NULL; // for shared memory
+static slock_t *MemLock = NULL; // spin lock needs to be in shared mem region.
+char **NextFree = NULL; // for shared memory!
+
+struct show_var_st status_vars[] = {
+ {"Com_begin", &Com_begin},
+ {"Com_commit", &Com_commit},
+ {"Com_create_table", &Com_create_table},
+ {"Com_delete", &Com_delete},
+ {"Com_drop_table", &Com_drop_table},
+ {"Com_insert", &Com_insert},
+ {"Com_rollback", &Com_rollback},
+ {"Com_select", &Com_select},
+ {"Com_update", &Com_update},
+ {"Connections", &Connections},
+ {"Questions", &Questions},
+ {NullS, (ulong *) 0}
+};
+char *command_name[] = {"n/a for pgsql", "n/a for pgsql", NULL};
+
+// change in number of args from 7.4.19 -> 8.4
+#ifdef PGSQL_8
+#define TupleDescInitEntry(a,b,c,d,e,f,g) TupleDescInitEntry(a,b,c,d,e,f)
+#endif
+
+int mysql_show_extsql(THD *thd, const char *stats_class, DestReceiver *dest, List *stats_vars,
+ uint stats_hourly, const char *wild, char *stats_order_var,
+ const uint stats_order_dir, const uint stats_order_limit, char *stats_where_var,
+ const uint stats_where_num, const uint stats_where_char);
+
+#define SHMEM_SIZE 1000000 // ZZ - need to predict based on class list for pgsql
+#define SHMEM_RESERVE 100000
+
+// need our own my_malloc to zero alloc'd memory
+// NOTE - this allocs from our limited shared memory area, we don't FREE. This should
+// only be used for extsql data
+// Temporary buffers used for i/o should be alloced with just "malloc/free"
+char * extsql_malloc(int, uint);
+char * extsql_malloc(int size, uint flags) { // from our shared memory segment, pointed at by ShmemPtr;
+
+ char *newStart; // similiar code in shmem.c
+ char *newFree;
+ char *addr;
+
+
+ if (ShmemPtr == NULL || NextFree == NULL) { // not allocated yet
+ sql_print_error("ExtSQL extsql_malloc failure, shared memory not allocated!");
+ return(NULL);
+ }
+
+ size = MAXALIGN(size);
+
+ SpinLockAcquire(MemLock);
+
+ newStart = *NextFree;
+
+ /* extra alignment for large requests, since they are probably buffers */
+ if (size >= BLCKSZ) {
+ newStart = (char *) BUFFERALIGN(newStart);
+ }
+
+ newFree = newStart + size;
+ if (newFree <= SHMEM_SIZE + ShmemPtr) {
+ addr = newStart;
+ *NextFree = newFree;
+
+ } else {
+ addr = NULL;
+ }
+
+ SpinLockRelease(MemLock);
+
+ if (!addr)
+ ereport(WARNING,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("ExtSQL - out of shared memory")));
+
+ bzero(addr, size);
+
+ if STATS_DEBUG(STATS_MEM_ALLOC ) {
+ sql_print_information("ExtSQL: alloc at 0x%x, of %d bytes",
+ (uint)addr, size);
+ }
+
+ return(addr);
+
+} // end my_malloc for PGSQL
+
+// need our own strdup for pgsql
+// NOTE - this allocs from our limited shared memory area, we don't FREE. This should
+// only be used for extsql data
+// Temporary buffers used for i/o should be alloced with just "strdup"
+char * extsql_strdup(char *, uint flags);
+char * extsql_strdup(char *srcStr, uint flags) {
+
+ int size = strlen(srcStr);
+ char *newStr = NULL;
+
+ newStr = extsql_malloc(size+1, 0); // second param doesn't matter for PGSQL
+
+ if (!newStr) {
+ sql_print_error("ExtSQL my_strdup: failed to allocate size (%d)", size);
+ return(NULL);
+ } else {
+ strcpy(newStr, srcStr);
+
+ }
+
+ return(newStr);
+
+} // end my_strdup for PGSQL
+
+
+static void my_datetime_to_str(time_t *, char *);
+static void my_datetime_to_str(time_t *secsNow, char *date) {
+
+ struct tm *timeNow;
+ timeNow=localtime(secsNow);
+
+ strftime(date, DATE_SIZE , "%m/%d/%y %H:%M:%S", timeNow);
+
+}
+
+// wild_case_compare -- from MySQL source, not ours. We don't use
+// charset info for PGSQL.
+int wild_case_compare(int cs, const char *str,const char *wildstr);
+int wild_case_compare(int cs, const char *str,const char *wildstr)
+{
+ char wild_many='%', wild_one='_', wild_prefix='\\';
+ int flag;
+
+ while (*wildstr) {
+
+ while (*wildstr && *wildstr != wild_many && *wildstr != wild_one) {
+
+ if (*wildstr == wild_prefix && wildstr[1])
+ wildstr++;
+
+ if (toupper(*wildstr++) !=
+ toupper(*str++))
+ return(1);
+ }
+
+ if (! *wildstr ) return(*str != 0);
+
+ if (*wildstr++ == wild_one) {
+ if (! *str++)
+ return(1); /* One char; skip */
+
+ } else { /* Found '*' */
+ if (!*wildstr)
+ return(0); /* '*' as last char: OK */
+
+ flag=(*wildstr != wild_many && *wildstr != wild_one);
+ do {
+
+ if (flag) {
+ char cmp;
+ if ((cmp= *wildstr) == wild_prefix && wildstr[1])
+ cmp=wildstr[1];
+ cmp=toupper(cmp);
+ while (*str && toupper(*str) != cmp)
+ str++;
+ if (!*str)
+ return(1);
+ }
+
+ if (wild_case_compare(0, str,wildstr) == 0)
+ return(0);
+
+ } while (*str++);
+ return(1);
+ }
+ }
+ return(*str != '\0');
+} // end wild_case_compare
+
+
+#else // MYSQL
+
+
+// for mysql we just use what they have
+#define extsql_malloc(a,b) my_malloc(a,b)
+#define extsql_strdup(a,b) my_strdup(a,b)
+
+static int extsql_output_prep_2_col(char *col1, char *col2, Protocol *protocol,
+ List- field_list, char *fill2);
+static int extsql_output_2_col(char *col1Str, int col1Int, char *col2Str,
+ int col2Int, Protocol *protocol, char *fill1);
+
+#endif // PGSQL or MYSQL build
+
+
+
+#ifdef EXTSQL_50
+#define net_printf net_printf_error
+#define PROTOCOL Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF
+#else
+#define PROTOCOL 1
+#endif
+
+
+// remove this define if you are building from source, will skip license checks.
+// #define EXTSQL_BINARY
+
+
+#ifndef DBUG_RETURN
+#define DBUG_RETURN(x) return(x)
+#endif
+
+//
+// START ACTUAL EXTSQL FUNCTION DEFINITIONS
+//
+
+#ifdef PGSQL
+int extsql_usage(THD* thd, DestReceiver *protocol) {
+
+#ifdef NEVER__
+}
+#endif
+
+#else
+int extsql_usage(THD* thd) {
+#endif
+
+#ifdef PGSQL
+ TupOutputState *tstate;
+ TupleDesc tupdesc = NULL;
+#else
+ Item* item;
+ List
- field_list;
+ Protocol *protocol = thd->protocol;
+ char *tstate = NULL, *tupdesc = '\0';
+#endif
+
+ char* enableHelp = "Turn statistics on or off";
+ char* enableUsage = "STATISTICS ON|OFF";
+ char* resetHelp = "Reset statistics [reread conf file]";
+ char* resetUsage = "STATISTICS RESET ['conf file']";
+ char* readHelp = "Read saved statistics data";
+ char* readUsage = "STATISTICS READ [CONFIRM] 'data file'";
+ char* writeHelp = "Write out statistics data";
+ char* writeUsage = "STATISTICS WRITE 'data file'";
+
+ DBUG_ENTER("extsql_usage");
+
+ // return if not authorized, disabled or inactive
+ if (extsql_check(thd, 1)) DBUG_RETURN(1);
+
+#ifdef PGSQL
+ if (extsql_output_prep_2_col("Function", "Usage", protocol, &tstate,
+ tupdesc)) {
+ DBUG_RETURN(1);
+ }
+#else
+ if (extsql_output_prep_2_col("Function", "Usage", protocol, field_list,
+ tupdesc)) {
+ DBUG_RETURN(1);
+ }
+#endif
+
+ if (extsql_output_2_col(enableHelp, 0, enableUsage, 0, protocol, tstate)) {
+ DBUG_RETURN(1);
+ }
+
+ if (extsql_output_2_col(resetHelp, 0, resetUsage, 0, protocol, tstate)) {
+ DBUG_RETURN(1);
+ }
+
+ if (extsql_output_2_col(readHelp, 0, readUsage, 0, protocol, tstate)) {
+ DBUG_RETURN(1);
+ }
+
+ if (extsql_output_2_col(writeHelp, 0, writeUsage, 0, protocol, tstate)) {
+ DBUG_RETURN(1);
+ }
+
+ extsql_output_complete(thd, tstate);
+
+ DBUG_RETURN(0);
+}
+
+// simple routine to turn extsql on or off using "STATISTICS ON|OFF"
+// grammer
+#ifdef PGSQL
+int extsql_enable(THD* thd, DestReceiver *protocol, uint status) {
+
+#ifdef NEVER__
+}
+#endif
+
+#else
+int extsql_enable(THD* thd, uint status) {
+#endif
+
+#ifdef PGSQL
+ TupOutputState *tstate;
+ TupleDesc tupdesc = NULL;
+#else
+ Item* item;
+ List
- field_list;
+ Protocol *protocol = thd->protocol;
+ char *tstate = NULL, *tupdesc = '\0';
+#endif
+ DBUG_ENTER("extsql_enable");
+
+ // return if not authorized, disabled or inactive
+ if (extsql_check(thd, 1)) DBUG_RETURN(1);
+
+ // accept any positive integer
+ if (status > 0) { status = 1; }
+
+ Shared->extsql_active = status;
+
+#ifdef PGSQL
+ if (extsql_output_prep_2_col("extsql_active", "status", protocol, &tstate,
+ tupdesc)) {
+ DBUG_RETURN(1);
+ }
+#else
+ if (extsql_output_prep_2_col("extsql_active", "status", protocol, field_list,
+ tupdesc)) {
+ DBUG_RETURN(1);
+ }
+#endif
+
+ if (extsql_output_2_col(NULL, Shared->extsql_active, NULL, 1, protocol, tstate)) {
+ DBUG_RETURN(1);
+ }
+
+ extsql_output_complete(thd, tstate);
+
+ DBUG_RETURN(0);
+}
+
+// handler for "STATISTICS RESET"
+#ifdef PGSQL
+int extsql_reset(THD* thd, DestReceiver *protocol, const char* conf) {
+
+#ifdef NEVER__
+}
+#endif
+
+#else
+int extsql_reset(THD* thd, const char* conf) {
+#endif
+
+#ifdef PGSQL
+ TupOutputState *tstate;
+ TupleDesc tupdesc = NULL;
+#else
+ Item* item;
+ List
- field_list;
+ Protocol *protocol = thd->protocol;
+ char *tstate = NULL, *tupdesc = '\0';
+#endif
+
+ int startType = STATS_INIT_RESET;
+
+ DBUG_ENTER("extsql_reset");
+
+ if (! STATS_DEBUG(STATS_ADMIN)) { // disabled
+ net_printf(thd, 0, adminDisabled);
+ return(0);
+ }
+
+ // return if not authorized, disabled or inactive
+ if (extsql_check(thd, 0)) DBUG_RETURN(1);
+
+ // in this case, extsql_prep will parse and process the conf
+ // file.
+ if (conf != NULL) {
+ extsql_prep(&startType, conf, NULL);
+ if (startType == STATS_INIT_FAILURE) {
+ net_printf(thd, 0, "ExtSQL: Error reading config file");
+ DBUG_RETURN(1);
+ }
+ }
+
+ if (extsql_init(Shared->extsql_class_list, startType, Shared->extsql_reload_file)) {
+ net_printf(thd, 0, "ExtSQL: Reset failed");
+ DBUG_RETURN(1);
+ }
+
+#ifdef PGSQL
+ if (extsql_output_prep_2_col("message", "status", protocol, &tstate,
+ tupdesc)) {
+ DBUG_RETURN(1);
+ }
+#else
+ if (extsql_output_prep_2_col("message", "status", protocol, field_list,
+ tupdesc)) {
+ DBUG_RETURN(1);
+ }
+#endif
+
+ if (extsql_output_2_col("Statistics reset", 0, NULL, 1, protocol, tstate)) {
+ DBUG_RETURN(1);
+ }
+
+ extsql_output_complete(thd, tstate);
+
+ return 0;
+}
+
+// handler for "STATISTICS READ|WRITE"
+#ifdef PGSQL
+int extsql_file_io(THD* thd, DestReceiver *protocol, uint io_mode, uint confirm,
+ char* reload_file) {
+
+#ifdef NEVER__
+}
+#endif
+
+#else
+int extsql_file_io(THD* thd, uint io_mode, uint confirm,
+ char* reload_file) {
+#endif
+
+#ifdef PGSQL
+ TupOutputState *tstate;
+ TupleDesc tupdesc = NULL;
+#else
+ Item* item;
+ List
- field_list;
+ Protocol *protocol = thd->protocol;
+ char *tstate = NULL, *tupdesc = '\0';
+#endif
+
+ int startType = STATS_INIT_RELOAD;
+ int prevState = Shared->extsql_active;
+
+ char* reloadFileCopy = extsql_strdup(reload_file, MYF(MY_WME));
+ char* message = extsql_strdup((io_mode ? "Reload file written successfully." :
+ "Reload file read successfully. Statistics are disabled. "),
+ MYF(MY_WME));
+
+ DBUG_ENTER("extsql_file-io");
+
+ if (! STATS_DEBUG(STATS_ADMIN)) { // disabled
+ net_printf(thd, 0, adminDisabled);
+ return(0);
+ }
+
+ // return if not authorized, disabled or inactive
+ if (extsql_check(thd, 0)) DBUG_RETURN(1);
+
+ Shared->extsql_active = 0;
+
+
+ // writing file
+ if (io_mode) {
+
+ if (extsql_reload(STATS_WRITE_RELOAD, reloadFileCopy)) {
+ net_printf(thd, 0, "ExtSQL: Error encountered during reload WRITE");
+ Shared->extsql_active = prevState;
+ DBUG_RETURN(1);
+ }
+
+ Shared->extsql_active = 1;
+
+ // reading file
+ } else {
+
+ extsql_prep(&startType, NULL, reloadFileCopy);
+
+ if (startType == STATS_INIT_FAILURE) {
+ net_printf(thd, 0, "Error reading reload file or version incompatibility.");
+ Shared->extsql_active = prevState;
+ DBUG_RETURN(1);
+ }
+
+ if (startType == STATS_INIT_CONFIRM && !confirm) {
+ net_printf(thd, 0,
+ "Class data in reload file or hostname does not match. Use STATISTICS READ CONFIRM '%s' to override.",
+ reload_file);
+ Shared->extsql_active = prevState;
+ DBUG_RETURN(1);
+ }
+
+ if (extsql_init(Shared->extsql_class_list, STATS_INIT_RELOAD, reloadFileCopy)) {
+ net_printf(thd, 0, "ExtSQL: Read failed");
+ Shared->extsql_active = prevState;
+ DBUG_RETURN(1);
+ }
+
+ Shared->extsql_active = 0;
+ }
+
+
+#ifdef PGSQL
+ if (extsql_output_prep_2_col("message", "status", protocol, &tstate,
+ tupdesc)) {
+ DBUG_RETURN(1);
+ }
+#else
+ if (extsql_output_prep_2_col("message", "status", protocol, field_list,
+ tupdesc)) {
+ DBUG_RETURN(1);
+ }
+#endif
+
+ if (extsql_output_2_col(message, 0, NULL, 1, protocol, tstate)) {
+ DBUG_RETURN(1);
+ }
+
+ extsql_output_complete(thd, tstate);
+
+ return 0;
+}
+
+// convenience function to check user authorization
+// checks to see if the specified 'user' is in the string containing
+// authorized users 'authUsers', set by admin with extsql_users=".."
+// return 1 if authorized, 0 otherwise
+static int extsql_user_auth(char *user, char *authUsers) {
+
+ char *tPtr, *token;
+ char tmpUser[STATS_TMP_BUFF_SIZE];
+ int found;
+
+ DBUG_ENTER("extsql_user_auth");
+
+ // we search allowed users each time in case list changes
+ if (strlen(authUsers) >= STATS_TMP_BUFF_SIZE-1) {
+ sql_print_error("ExtSQL: user list has exceeded storage limits");
+ return(0);
+ }
+
+ strcpy(tmpUser, authUsers);
+ tPtr = tmpUser;
+ found = 0;
+
+ while ( (token = strtok(tPtr, ", ")) ) {
+
+ tPtr = NULL;
+ if (!strcmp(token, user) || !strcmp("root", user)) { // match
+ found = 1;
+ break;
+ }
+ } // end while - more user names
+
+ if (!found) {
+ return(0);
+ }
+
+ return(1); // they are okay!
+
+} // end extsql_user_auth
+
+// Check for authorized user, enabled and active extsql
+int extsql_check(THD* thd, int overrideActive) {
+
+ char *user;
+
+ DBUG_ENTER("extsql_check");
+
+ if (Shared->extsql_disabled) { // just get out
+ net_printf(thd, 0, "ExtSQL: disabled");
+ DBUG_RETURN(1);
+ }
+
+ if (!(Shared->extsql_active || overrideActive)) { // just get out
+ net_printf(thd, 0, "ExtSQL: deactivated");
+ DBUG_RETURN(1);
+ }
+
+#ifdef PGSQL
+ user = MyProcPort->user_name;
+#else
+
+#ifdef EXTSQL_50
+ user = thd->security_ctx->user;
+#else
+ user = thd->user;
+#endif
+
+#endif
+
+ // are they authorized
+ if (!extsql_user_auth(user, extsql_users)) {
+
+ // let the DBA know just in case.
+ sql_print_warning("ExtSQL: attempted access by user: %s", user);
+ net_printf(thd, 0, "ExtSQL: access not allowed to user: %s",
+ user);
+ DBUG_RETURN(1);
+ }
+
+ DBUG_RETURN(0);
+}
+
+// generic function to do output prep, identify column headers, types
+// INPUT: col1 and col2 (col2 can be NULL)
+// non-zero return on failure, one or two columns
+// NOTE dest,tstate, and tupdesc are declared in the calling function,
+// same vars will be used in call to extsql_output_2_col
+// fill1 and fill2 are placeholders to keep function calls the same
+int extsql_output_prep_2_col(char *col1, char *col2
+
+#ifdef PGSQL
+ ,DestReceiver *dest, TupOutputState **tstate, TupleDesc tupdesc
+#else
+ ,Protocol *protocol, List
- field_list, char *fill2
+#endif
+
+) {
+
+ if (!col1) {
+ sql_print_error("ExtSQL: extsql_output_prep_2_col missing col1.");
+ return(1);
+ }
+
+#ifdef PGSQL
+
+ // tuple descriptor for up to two cols
+ if (col2) {
+
+ tupdesc = CreateTemplateTupleDesc(2, false);
+
+ TupleDescInitEntry(tupdesc, (AttrNumber) 1, col1,
+ TEXTOID, -1, 0, false);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 2, col2,
+ TEXTOID, -1, 0, false);
+ } else {
+ tupdesc = CreateTemplateTupleDesc(1, false);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 1, col1,
+ TEXTOID, -1, 0, false);
+ }
+
+ // prepare for projection of tuples
+ *tstate = begin_tup_output_tupdesc(dest, tupdesc);
+
+#else
+ Item *item;
+
+ // column headers
+ field_list.push_back(item = new Item_empty_string(col1, MAX_VAR_SIZE));
+ if (col2) {
+ field_list.push_back(item = new Item_empty_string(col2, MAX_VAR_SIZE));
+ }
+
+ // output the column headers
+ if (protocol->send_fields(&field_list, PROTOCOL)) {
+ sql_print_error("ExtSQL: output_prep_2_col protocol write failed");
+ return(1);
+ }
+
+#endif
+
+ return(0);
+
+} // end extsql_output_prep_2_col
+
+
+// generic function to send output rows (up to 2 cols, char data)
+// INPUT: col1Str (value), col2Str maybe NULL
+// NOTE tstate is declared in the calling function,
+// same vars will be used in call to extsql_output_prep_2_col
+// null strings will result in using integer values
+int extsql_output_2_col(char *col1Str, int col1Int, char *col2Str, int col2Int
+
+#ifdef PGSQL
+ ,DestReceiver *dest, TupOutputState *tstate
+#else
+ ,Protocol *protocol, char *fill1
+#endif
+
+
+) {
+
+ bool cleanup1 = false;
+ bool cleanup2 = false;
+
+#ifdef PGSQL
+ char *values[2];
+#endif
+
+ // Convert ints to char*
+ if (col1Str == NULL) {
+ cleanup1 = true;
+ col1Str = (char*)my_malloc(8 * sizeof (char), MYF(MY_WME | MY_ZEROFILL));
+ sprintf(col1Str, "%d", col1Int);
+ }
+
+ if (col2Str == NULL) {
+ cleanup2 = true;
+ col2Str = (char*)my_malloc(8 * sizeof (char), MYF(MY_WME | MY_ZEROFILL));
+ sprintf(col2Str, "%d", col2Int);
+ }
+
+#ifdef PGSQL
+
+
+ // assign to the values array ZZ - not done
+ if (col1Str) {
+ values[0] = col1Str;
+ }
+
+ if (col2Str) {
+ values[1] = col2Str;
+ }
+
+ // send it to dest
+ do_tup_output(tstate, values);
+
+
+#else
+ protocol->prepare_for_resend();
+ if (col1Str) {
+ protocol->store(col1Str, strlen(col1Str), system_charset_info);
+ } else { // must be int
+ protocol->store((longlong) col1Int);
+ }
+
+ if (col2Str) {
+ protocol->store(col2Str, strlen(col2Str), system_charset_info);
+ } else { // must be int
+ protocol->store((longlong) col2Int);
+ }
+
+ if (protocol->write()) {
+ sql_print_error("ExtSQL: output_2_col protocol write failed");
+ return(1);
+ }
+#endif
+
+ // cleanup dynamic memory allocation
+ if (cleanup1) { my_free(col1Str, MYF(0)); }
+ if (cleanup2) { my_free(col2Str, MYF(0)); }
+
+ return(0);
+
+} // end extsql_output_2_col
+
+// generic function to handle transmission end
+void extsql_output_complete(
+
+#ifdef PGSQL
+ THD *thd, TupOutputState *tstate
+#else
+ THD *thd, char *tstate
+#endif
+
+ ) {
+
+#ifdef PGSQL
+ end_tup_output(tstate);
+#else
+
+ send_eof(thd);
+
+#endif
+
+} // end extsql_output_complete
+
+
+// called from the parser in response to a SHOW STATISTICS command.
+// wild is what is given in the LIKE 'wild'
+int mysql_show_extsql(THD *thd,
+ const char *stats_class,
+#ifdef PGSQL
+ DestReceiver *protocol,
+ List *stats_vars,
+#else
+ List &stats_vars,
+#endif
+ uint stats_hourly,
+ const char *wild,
+#ifdef PGSQL
+ char *stats_order_var,
+#else
+ const char *stats_order_var,
+#endif
+ const uint stats_order_dir,
+ const uint stats_order_limit,
+#ifdef PGSQL
+ char *stats_where_var,
+#else
+ const char *stats_where_var,
+#endif
+ const uint stats_where_num,
+ const uint stats_where_char)
+{
+#ifdef PGSQL
+
+#define VALUE_LIMIT 100
+ TupOutputState *tstate;
+ TupleDesc tupdesc = NULL;
+
+#ifdef PGSQL_8
+ ListCell *item;
+#else
+ List *item;
+#endif
+
+ int listCount;
+ char *values[MAX_VARS_IN_CLASS], *valuesPtr[VALUE_LIMIT], valueBuff[256]; // output
+ int ptrIndex = 0;
+ int system_charset_info = 0; // not used in PGSQL, just placeholder, call our func.
+
+#else
+
+ Item *item;
+ List
- field_list; // has column headings, all strings.
+ List check_vars;
+ Protocol *protocol= thd->protocol;
+ LEX_COLUMN *column;
+ char *tstate = NULL; char *tupdesc = '\0'; // placeholders for tstate and tupdesc
+
+#endif
+
+ char *columnName; // used in output for MySQL PGSQL
+ int res = 0;
+ int namedVars = 0; // are they asking for specific vars
+ char listBuff[MAX_BUFF_SIZE], timeBuff[DATE_SIZE];
+ char *varListPtr = listBuff;
+ int timeLimit = 0, currentTime = 0, indexTime = 0, varIndex = 0;
+ char *varName, *termChar;
+ int varNameLength = 0, timeCount = 0, rowCount = 0, whereVarAddedList = 0;
+
+ pSTATS_CLASS_DATA stats_class_data;
+ STAT_VAR **statPtr;
+ int i, maxVar, maxTime, instance, maxInstance, var, found=0, value = 0, skip = 0,
+ rowHasData;
+ char *user;
+ MYSQL_TIME mysql_time;
+
+#ifdef EXTSQL_50
+ user = thd->security_ctx->user;
+#else
+
+#ifdef PGSQL
+ user = MyProcPort->user_name;
+#else
+ user = thd->user;
+#endif
+#endif
+
+ DBUG_ENTER("mysql_show_extsql");
+
+ // CHECK FOR DATA DUMPS OR TESTS
+ if STATS_DEBUG(STATS_DUMP) {
+ extsql_stats_dump();
+ extsql_thread_dump(thd, 1);
+
+ } else if STATS_DEBUG(STATS_DUMP_ONCE) { // just once
+ extsql_stats_dump();
+ extsql_thread_dump(thd, 1);
+ Shared->extsql_debug -= STATS_DUMP_ONCE; // clear
+
+ }
+
+ if STATS_DEBUG(STATS_TEST_ONE) {
+
+ Shared->extsql_active = 0;
+ Shared->extsql_debug -= STATS_TEST_ONE; // clear
+ extsql_stats_dump();
+ if (extsql_reload(STATS_WRITE_RELOAD, Shared->extsql_reload_file)) {
+ sql_print_error("ExtSQL: Error encountered during reload WRITE");
+ }
+ extsql_stats_dump();
+ Shared->extsql_active = 1;
+
+ }
+
+ if STATS_DEBUG(STATS_TEST_TWO) {
+
+ Shared->extsql_debug -= STATS_TEST_TWO; // clear
+ extsql_stats_dump();
+
+ if (0) {
+ extsql_init(Shared->extsql_class_list, STATS_INIT_RELOAD, Shared->extsql_reload_file);
+ } else {
+ extsql_init(Shared->extsql_class_list, STATS_INIT_RESET, Shared->extsql_reload_file);
+ }
+
+ extsql_stats_dump();
+
+ } // end if-else testing
+
+
+ // return if not authorized, disabled or inactive
+ if (extsql_check(thd, 0)) DBUG_RETURN(1);
+
+ // okay, statistics are active, prepare for output.
+ if STATS_DEBUG(STATS_SHOW_PARAMS) {
+ sql_print_information("ExtSQL: internal variables class %s,\
+ hourly %d, wild %s, ORDER BY %s, direction %d, LIMIT %d, \
+ WHERE var %s, compare %d, value %d",
+ stats_class, stats_hourly, wild,
+ stats_order_var,
+ stats_order_dir, stats_order_limit,
+ stats_where_var,
+ stats_where_char, stats_where_num);
+ }
+
+ if (stats_class == NULL) { // they want help/status
+
+ pSTATS_CLASS_DATA stats_class_data;
+ STAT_VAR **statPtr;
+
+
+ char *Usage = "Usage";
+ char *Help = "SHOW STATISTICS (* | Var[,Var]) FROM Class [LIKE 'Instance']";
+ char *Help2 = " [WHERE Var ('<'|'>'|'=') num] [ORDER BY Var] [HISTORY] [LIMIT rows_or_time]";
+#ifdef EXTSQL_50
+ char *Help3 = "also available from INFORMATION_SCHEMA, SHOW TABLES FROM INFORMATION_SCHEMA for list.\
+ExtSQL reported as part of EXTSTATS_(Class) tables.";
+#endif
+ char *Version = "Version";
+#ifdef EXTSQL_BINARY
+ char *Key = "License Key";
+#endif
+ char *Users = "Stat Users";
+ char *Blank = " ";
+ int maxVar, maxTime, maxInstance, var, instance, classCount;
+
+#ifdef PGSQL
+ if (extsql_output_prep_2_col("Item", "Value", protocol, &tstate, tupdesc)) {
+ DBUG_RETURN(1);
+ }
+#else
+ if (extsql_output_prep_2_col("Item", "Value", protocol, field_list, tupdesc)) {
+ DBUG_RETURN(1);
+ }
+#endif
+
+ if (extsql_output_2_col(Usage, 0, Help, 0, protocol, tstate)) {
+ DBUG_RETURN(1);
+ }
+
+ if (extsql_output_2_col(Usage, 0, Help2, 0, protocol, tstate)) {
+ DBUG_RETURN(1);
+ }
+
+#ifdef EXTSQL_50
+ if (extsql_output_2_col(Usage, 0, Help3, 0, protocol, tstate)) {
+ DBUG_RETURN(1);
+ }
+#endif
+
+ if (extsql_output_2_col(Version, 0, Shared->extsql_version+1, 0, protocol, tstate)) {
+ DBUG_RETURN(1);
+ }
+
+#ifdef EXTSQL_BINARY
+ if (extsql_output_2_col(Key, 0, extsql_key, 0, protocol, tstate)) {
+ DBUG_RETURN(1);
+ }
+#endif
+
+ if (extsql_output_2_col(Users, 0, extsql_users, 0, protocol, tstate)) {
+ DBUG_RETURN(1);
+ }
+
+ if STATS_DEBUG(STATS_PID) { // show the pid of the server for gdb
+ sprintf(listBuff, "(%d)", getpid());
+ if (extsql_output_2_col("Server PID", 0, listBuff, 0, protocol, tstate)) {
+ DBUG_RETURN(1);
+ }
+ }
+
+ // now we show them the class list stuff
+ classCount = 0;
+ for (stats_class_data = Shared->statData; classCount < STATS_MAX_CLASSES && stats_class_data->maxInstance;
+ stats_class_data++, classCount++) {
+
+ maxVar = stats_class_data->maxVar;
+ maxTime = stats_class_data->maxTime-1;
+ maxInstance = stats_class_data->maxInstance;
+
+ if (extsql_output_2_col(Blank, 0, Blank, 0, protocol, tstate)) {
+ DBUG_RETURN(1);
+ }
+
+ // how many instances are in use
+ for (instance=0;
+ instance < maxInstance && stats_class_data->instanceIndex[instance];
+ instance++) {
+ }
+
+ sprintf(listBuff, "Max instances/in use (%d/%d), Max vars (%d), Max time (%d), Time units(%c)",
+ maxInstance, instance, maxVar, maxTime, stats_class_data->timeUnits);
+
+ if (extsql_output_2_col(stats_class_data->name, 0, listBuff, 0, protocol, tstate)) {
+ DBUG_RETURN(1);
+ }
+
+ strcpy(listBuff, " ");
+
+ for (var=0, statPtr = stats_class_data->varIndex;
+ (var < maxVar) && *statPtr ;
+ var++, statPtr++) {
+
+ sprintf(listBuff+strlen(listBuff),"%s ", (*statPtr)->name);
+ }
+
+ if (extsql_output_2_col(stats_class_data->name, 0, listBuff, 0, protocol, tstate)) {
+ DBUG_RETURN(1);
+ }
+
+ } // end for - all clasees
+
+
+ extsql_output_complete(thd, tstate);
+ DBUG_RETURN(0);
+
+ } // end if - just showing help
+ // NOTE - normal RETURN ONLY here!
+
+
+ // find the instance list for the class we are looking for
+ found = 0;
+ for (stats_class_data = Shared->statData; stats_class_data->maxInstance;
+ stats_class_data++) {
+ if (!strcmp(stats_class_data->name, stats_class)) { // found it
+ found = 1;
+ break;
+ }
+ } // end for
+
+ if (!found) {
+ net_printf(thd, 0, "ExtSQL: can't find requested class %s, enter SHOW STATISTICS for usage.",
+ stats_class);
+ DBUG_RETURN(1);
+ }
+
+ // AT THIS POINT stats_class_data points to the requested class
+ maxVar = stats_class_data->maxVar;
+ maxTime = stats_class_data->maxTime;
+ maxInstance = stats_class_data->maxInstance; // needed for array indexing
+
+
+ // NOTE - a LOT of checking first to make sure inputs make sense.
+
+ if (stats_order_var) { // they used ORDER BY
+ net_printf(thd, 0, "ExtSQL: ORDER BY not supported in this version.");
+ DBUG_RETURN(1);
+ }
+
+ // CHECK that the limit is NOT larger than allowed if done for time
+ if (stats_hourly && stats_order_limit >= maxTime) {
+ net_printf(thd, 0, "ExtSQL, limit of %d is greater than stored time for this class of %d.",
+ stats_order_limit, maxTime-1);
+ DBUG_RETURN(1);
+ }
+
+ // CHECK BLOCK - check the where character
+ if (stats_where_char) {
+
+ switch (stats_where_char) {
+
+ case '>':
+ case '<':
+ case '=':
+ break;
+
+ default:
+ net_printf(thd, 0, "ExtSQL: unsupported comparison (%c), enter SHOW_STATISTICS for usage.", stats_where_char);
+ DBUG_RETURN(1);
+
+ } // end switch
+ }
+
+ // Set listCount to the number of Var column headings we will have
+#ifdef PGSQL
+
+ // For PGSQL we need to count the columns what we have ahead of time
+ listCount = 0; namedVars = 0;
+ foreach(item, stats_vars) {
+
+ char *var1 = strVal(lfirst(item));
+
+ if (!strcmp(var1, "statistics") ) { // ZZ - not really best way to check, default list
+
+ // loop through for count
+ for (var=0, statPtr = stats_class_data->varIndex;
+ (var < stats_class_data->maxVar) && *statPtr ;
+ var++, statPtr++) {
+ listCount++;
+ }
+ break;
+ }
+
+ listCount++;
+ namedVars = 1; // redundant
+ } // end for - all vars in list
+
+#else
+
+ if (stats_vars.elements) {
+ namedVars = 1;
+ }
+
+#endif
+
+ // CHECK BLOCK - if they gave us specific columns (namedVars=1) to display,
+ // check those vars are actually being tracked
+ if (namedVars) {
+
+
+
+#ifdef PGSQL
+
+ foreach(item, stats_vars) { // output the column heading
+
+ columnName = strVal(lfirst(item));
+#else
+
+ List_iterator stats_column(stats_vars);
+
+ while ((column=stats_column++)) { // search for a match in class
+
+ columnName = (char *)column->column.ptr();
+#endif
+
+ found = 0;
+ for (var = 0, statPtr = stats_class_data->varIndex;
+ (var < maxVar) && *statPtr ;
+ var++, statPtr++) {
+
+ if (strlen(columnName) > MAX_VAR_SIZE - 5) {
+ net_printf(thd, 0, "ExtSQL: Var is too long");
+ DBUG_RETURN(1);
+ }
+
+ if (!strcasecmp((*statPtr)->name, columnName)) { // ZZ - low case input in postgres?
+ found = 1;
+ break;
+ }
+ } // end for - all vars
+
+ if (!found) {
+ net_printf(thd, 0, "ExtSQL: can't find Var %s, SHOW STATISTICS for usage.",
+ columnName);
+ DBUG_RETURN(1);
+ }
+
+ } // end while/for - more columns
+
+#ifdef NEVER__
+ } // to keep indent level correct in emacs
+#endif
+
+ } // end if - named vars are being tracked.
+
+
+ // CHECK BLOCK - if we have an ORDER by or WHERE clause variable, check those also
+ if (stats_where_var || stats_order_var) {
+
+#ifdef PGSQL
+
+ List *checkList;
+
+ if (stats_where_var && stats_order_var) {
+ checkList = (List *)list_make2(makeString(stats_where_var), makeString(stats_order_var));
+
+ } else if (stats_where_var) {
+ checkList = (List *)list_make1(makeString(stats_where_var));
+
+ } else {
+ checkList = (List *)list_make1(makeString(stats_order_var));
+ }
+
+ foreach(item, checkList) { // check the list
+
+ columnName = strVal(lfirst(item));
+
+#else
+ if (stats_where_var) {
+ String *tmpStr = new (thd->mem_root) String ((const char *)stats_where_var, strlen(stats_where_var), system_charset_info);
+ check_vars.push_back(new LEX_COLUMN (*tmpStr, 0));
+ }
+
+ if (stats_order_var) {
+ String *tmpStr = new (thd->mem_root) String ((const char *)stats_order_var, strlen(stats_where_var), system_charset_info);
+ check_vars.push_back(new LEX_COLUMN (*tmpStr, 0));
+ }
+
+ List_iterator stats_column(stats_vars);
+
+ while ((column=stats_column++)) { // search for a match in class
+
+ columnName = (char *)column->column.ptr();
+
+#endif
+
+ found = 0;
+ for (var = 0, statPtr = stats_class_data->varIndex;
+ (var < stats_class_data->maxVar) && *statPtr ;
+ var++, statPtr++) {
+
+ // make sure the name isn't too long
+ if (strlen(columnName) > MAX_VAR_SIZE - 5) {
+ net_printf(thd, 0, "ExtSQL: Var (%s) is too long, check entry.",
+ columnName);
+ DBUG_RETURN(1);
+ }
+
+ if (!strcasecmp((*statPtr)->name, columnName)) {
+ found = 1;
+ break;
+ }
+
+ } // end for - all vars
+
+ if (!found) {
+ net_printf(thd, 0, "ExtSQL: can't find Var %s, enter SHOW STATISTICS for usage.",
+ columnName);
+ DBUG_RETURN(1);
+ }
+ } // end while - more columns
+
+#ifdef NEVER__
+ } // to keep indent level correct in emacs
+#endif
+
+ } // end if - specified where or order by var
+
+ // START OUTPUT - prepare and output the header columns
+ // The class name (always FIRST column)
+ // PGSQL uses a predefined array of column headers, need to keep count
+ // MYSQL uses a stack approach.
+
+#ifdef PGSQL
+ if (stats_hourly) {
+ listCount += 2; // class name and time
+ } else {
+ listCount++; // add just one for the class name
+ }
+ i = 1; // current param position
+
+ tupdesc = CreateTemplateTupleDesc(listCount, false);
+
+ TupleDescInitEntry(tupdesc, (AttrNumber) i++, stats_class, TEXTOID, -1, 0, false);
+
+#else
+ field_list.push_back(item = new Item_empty_string(stats_class, STATS_MAX_NAME));
+#endif
+
+ // if HOURLY is asked for, it is always the SECOND column
+ if (stats_hourly) {
+ char *units;
+ switch (stats_class_data->timeUnits) {
+ case 'm':
+ units = "minutes";
+ break;
+ case 'h':
+ units = "hours";
+ break;
+ case 'd':
+ units = "days";
+ break;
+ default:
+ units = "UNKNOWN";
+ }
+
+#ifdef PGSQL
+ TupleDescInitEntry(tupdesc, (AttrNumber) i++, units, TEXTOID, -1, 0, false);
+#else
+ field_list.push_back(item = new Item_empty_string(units, 30)); // max size of 'minutes'
+#endif
+
+ }
+
+ // Then the variable list, this will either be what they specified, or
+ // the conf list. We do a SPECIAL check to see if they have included a
+ // WHERE clause. If they have, but it is not included in the column list
+ // we will add it, but it will not be displayed on output
+
+ // LARGE else block here depending on whether they specified list, or we use default!
+ strcpy(varListPtr,","); // init the list
+
+ if (namedVars) { // they identified the columns they want
+
+ char tempName[MAX_VAR_SIZE]; // ZZ
+
+#ifdef PGSQL
+ foreach(item, stats_vars) { // output the column heading
+
+ columnName = strVal(lfirst(item));
+#else
+
+ List_iterator stats_column2(stats_vars);
+ while ((column=stats_column2++)) { // search for a match in class
+
+ columnName = (char *)column->column.ptr();
+#endif
+
+ if (strlen(columnName) > MAX_VAR_SIZE - 5) {
+ net_printf(thd, 0, "ExtSQL: Var is too long");
+ DBUG_RETURN(1);
+ }
+
+
+#ifdef PGSQL
+ TupleDescInitEntry(tupdesc, (AttrNumber) i++, columnName, TEXTOID, -1, 0, false);
+
+#else
+ field_list.push_back(item = new Item_uint(columnName, MY_INT64_NUM_DECIMAL_DIGITS ));
+#endif
+
+ strcat(varListPtr, columnName);
+ strcat(varListPtr, ",");
+
+ } // end while - more columns
+#ifdef NEVER__
+ } // to keep indent level correct in emacs
+#endif
+
+ // is the where var on the list
+ if (stats_where_var) {
+ if (strlen(stats_where_var) > MAX_VAR_SIZE - 5) {
+ net_printf(thd, 0, "ExtSQL: WHERE Var too long");
+ DBUG_RETURN(1);
+ }
+ strcpy(tempName, ",");
+ strcat(tempName, stats_where_var);
+ strcat(tempName, ",");
+ if (strstr(varListPtr,tempName)) {
+ whereVarAddedList = 0;
+ } else {
+ whereVarAddedList = 1;
+ strcat(varListPtr, stats_where_var);
+ strcat(varListPtr,",");
+ }
+ } // end if - where clause
+
+
+ } else { // we output default column headings and create varListPtr
+
+ // loop through the var names for headings
+ for (var=0, statPtr = stats_class_data->varIndex;
+ (var < stats_class_data->maxVar) && *statPtr ;
+ var++, statPtr++) {
+
+#ifdef PGSQL
+ TupleDescInitEntry(tupdesc, (AttrNumber) i++, (*statPtr)->name, TEXTOID, -1, 0, false);
+#else
+ field_list.push_back(item = new Item_uint((*statPtr)->name, MY_INT64_NUM_DECIMAL_DIGITS ));
+ item->maybe_null=1;
+#endif
+ strcat(varListPtr,(*statPtr)->name );
+ strcat(varListPtr, ",");
+
+ // check to make sure we don't bust string
+ if (strlen(varListPtr) > MAX_BUFF_SIZE - 50) {
+ net_printf(thd, 0, "ExtSQL: Var list too long");
+ DBUG_RETURN(1);
+ }
+ } // end for - all instances
+ } // end if - use defaults
+
+ // output the column headers
+#ifdef PGSQL
+ tstate = begin_tup_output_tupdesc(protocol, tupdesc);
+#else
+ if (protocol->send_fields(&field_list, PROTOCOL)) {
+ DBUG_RETURN(1);
+ }
+
+#endif
+
+ // AT THIS POINT the output columns are in varListPtr
+ // next, if they will be asking for hourly output, we prepare an array
+ // of hour indexes.
+ if STATS_DEBUG(STATS_SHOW_PARAMS) {
+ sql_print_information("ExtSQL: varListPtr is (%s), whereVarAddedList(%d)",
+ varListPtr, whereVarAddedList);
+ }
+
+ timeLimit = 1; // by default we only show one time.
+ currentTime = stats_class_data->time; // time we are currently logging to.
+
+ if (stats_hourly) { // reset the proper inner loop limits
+ if (stats_order_limit) {
+ timeLimit = stats_order_limit; // whatever thy wanted
+ } else {
+ timeLimit = maxTime-1; // they get it all
+ } // end if else - time limit
+ } // end if - user wants hours displayed.
+
+
+ // AT THIS POINT - we have the varListPtr area with the oolumns in the
+ // display order we want. Remember, zero index holds the total. The
+ // currentTime has our present location and it is circular, i.e. if
+ // maxTime for the class is 4, we store current + 3. If the currentTime
+ // is 2, the proper way to display all hours, starting with the most
+ // recent is to show 2(current),1(prior),3(more prior) - skip 0
+
+ // the currentTime has no correlation to actual clock time, it is just
+ // a position within a circular buffer of times, each class could have
+ // a different limit.
+
+ // START outputing data for the every instance within the matching class
+ // we now go through all the instances.
+ for (instance=0; instance < maxInstance && stats_class_data->instanceIndex[instance] ;
+ instance++) {
+
+ if (wild && wild_case_compare(system_charset_info,
+ stats_class_data->instanceIndex[instance], wild) ) {
+
+ continue; // skip this one
+ } // end if - wild match
+
+ // are we over our output LIMIT
+ if (!stats_hourly && stats_order_limit) { // not doing times, put limit on general output
+ if (rowCount >= stats_order_limit) {
+ break;
+ }
+ }
+
+ // loop through the times, if stats_hourly is not set we just print the stuff for zero
+ // time, the grand totals
+ // if stats_timely is set we will output a number of hours starting at currentTime, and
+ // limited by timeLimit (starts at 1).
+ for (timeCount = 0, indexTime = stats_hourly ? currentTime : 0 ;
+ timeCount < timeLimit && indexTime >= 0;
+ timeCount++, indexTime-- ) {
+
+ // if we are outputing times, we have to adjust our indexTime
+ if (stats_hourly && !indexTime) { // we have gone back to the total
+ indexTime = maxTime - 1; // pick the last
+ }
+
+ // prepare the data row
+ skip = 0;
+
+#ifdef PGSQL
+ // nothing special required here
+ // NOTE, variable i keeps our index in values array
+ i = 0;
+#else
+ protocol->prepare_for_resend();
+#endif
+ res = 0;
+ rowHasData = 0; // do we have anything to show, only on HISTORY (stats_hourly set)
+
+ // first column - instance name - always
+#ifdef PGSQL
+ values[i++] = stats_class_data->instanceIndex[instance];
+#else
+ protocol->store(stats_class_data->instanceIndex[instance],
+ strlen(stats_class_data->instanceIndex[instance]), system_charset_info);
+#endif
+
+ // if we are doing times, second column is times - always, we use indexTime
+ if (stats_hourly) {
+ value = (int) stats_class_data->timeLabels[indexTime];
+
+ // convert to local time and then to a string for output
+#ifdef PGSQL
+ // ZZ need time conversion/zone
+ mysql_time = value;
+#else
+ thd->variables.time_zone->gmt_sec_to_TIME(&mysql_time, (my_time_t) value);
+#endif
+
+ my_datetime_to_str(&mysql_time, timeBuff);
+
+ // we normally chop seconds off the time, from: 2008-04-04 14:59:48 TO 2008-04-04 14:59
+ if (!STATS_DEBUG(STATS_TIME_DETAIL)) {
+ timeBuff[strlen(timeBuff) - 3] = '\0';
+ }
+#ifdef PGSQL
+ values[i++] = timeBuff;
+#else
+ protocol->store(timeBuff, strlen(timeBuff), system_charset_info);
+#endif
+
+ } // end if - hourly
+
+ // loop through varListPtr, output the vars in the proper order
+ // varListPtr = ",bytes,selects," varName will move through this
+ for ( varName = varListPtr; *varName ; varName = strstr(varName, ",")) {
+
+ varName++; // move off the comma
+ //sql_print_information(" target varName is (%s)", varName);
+
+ if (! *varName) { // we are done
+ break;
+ }
+
+ varNameLength = strchr(varName, ',') - varName;
+
+ // okay, varName points at a name, lets go through the index looking
+ // for a match
+ found = 0;
+ value = 0;
+
+ for (varIndex=0, statPtr = stats_class_data->varIndex;
+ varIndex < maxVar && *statPtr;
+ varIndex++, statPtr++) {
+
+ termChar = (*statPtr)->name + varNameLength +1; // point to end of the current var name
+
+ if (!strncasecmp( (*statPtr)->name, varName, varNameLength)) {
+ found = 1;
+ value = stats_class_data->data [ARRAY_INDEX(instance, varIndex, maxVar,
+ indexTime, maxTime)];
+
+ // before we send, check the value if they have set a WHERE condition
+ if (stats_where_var && !strncasecmp((*statPtr)->name, stats_where_var, varNameLength)) { // match
+
+ switch (stats_where_char) {
+
+ case '>':
+ if (value <= stats_where_num) skip = 1; // this row is no good
+ break;
+
+ case '<':
+ if (value >= stats_where_num) skip = 1; // this row is no good
+ break;
+
+ case '=':
+ if (value != stats_where_num) skip = 1; // this row is no good
+ break;
+
+ default:
+
+ sql_print_warning("ExtSQL: unsupported comparison (%d)", stats_where_char);
+
+ } // end switch
+
+ rowHasData = 1;
+
+ } // end if - match using WHERE clause
+
+ if (skip) {
+ break;
+ }
+
+ if (whereVarAddedList && !strncasecmp((*statPtr)->name, stats_where_var, varNameLength)) {
+ break; // don't output the where column, they didn't ask for it
+ }
+
+#ifdef PGSQL
+ // convert to text, store ptr addr and then free
+ // after send
+ sprintf(valueBuff, "%d", value);
+ values[i] = strdup(valueBuff);
+ valuesPtr[ptrIndex++] = values[i++]; // need to free after output
+ if (ptrIndex >= VALUE_LIMIT) {
+ sql_print_error("ExtSQL, output value limit reached");
+ DBUG_RETURN(1);
+ }
+#else
+ protocol->store((longlong) value);
+#endif
+
+ if (value) { // is it non-zero
+ rowHasData = 1;
+ }
+ break;
+ } // end if - matching
+
+ } // end for - a matching var in the list
+
+ if (skip) {
+ break;
+ }
+
+ if (!found) {
+
+#ifdef PGSQL
+ values[i++] = "n/a";
+#else
+ value=999999;
+ protocol->store((longlong) value);
+#endif
+
+ }
+
+ } // end for - requested values
+
+ if (skip || (stats_hourly && !rowHasData)) { // free it up!
+
+ // if PGSQL we allocate some value space above, make sure we free it in all situations.
+#ifdef PGSQL
+ if (ptrIndex) {
+ for (ptrIndex--; ptrIndex; ptrIndex--) {
+ if (valuesPtr[ptrIndex]) {
+ free(valuesPtr[ptrIndex]);
+ }
+ }
+ }
+#endif
+ continue;
+ }
+
+
+ // send the row to the user,
+
+#ifdef PGSQL
+ do_tup_output(tstate, values);
+
+ if (ptrIndex) {
+ for (ptrIndex--; ptrIndex; ptrIndex--) {
+ if (valuesPtr[ptrIndex]) {
+ free(valuesPtr[ptrIndex]);
+ }
+ }
+ }
+#else
+ if (protocol->write()) {
+ sql_print_error("ExtSQL, protocol write failed");
+ DBUG_RETURN(1);
+ }
+#endif
+
+ rowCount++; // keep track of the count if have a general LIMIT in effect.
+
+ } // end for - all requested times
+
+ } // end for - all instances
+
+
+ extsql_output_complete(thd, tstate);
+ DBUG_RETURN(0);
+
+} // end mysql_show_statistics
+
+
+// record a statistic_increment call
+void extsql_inc(ulong *V, ulong C)
+{
+
+#ifdef PGSQL
+ THD *thd = MyProc;
+ time_t log_sec, now_sec;
+#else
+ THD *thd = current_thd;
+ my_time_t log_sec, now_sec;
+#endif
+
+ pSTATS_CLASS_DATA stats_class_data;
+ STAT_VAR **var_ptr;
+ int i, var, offset, offsetLimit, classCount, found = 0, instance, maxVar, maxTime;
+ int threadID, command;
+ char *user = "", *host = "", *db = "", *proc_info = "";
+ int *statVerify = 0, *indexPtr = 0;
+
+ if (!Shared->extsql_active || Shared->extsql_disabled || STATS_DEBUG(STATS_DISABLE_INC)) {
+ return;
+ }
+
+#ifdef EXTSQL_50
+ // during init we stored just offsets from THD structure status_var to each stat
+ // for nonglobal items -- we got an address in V, need to determine if it is a real global
+ // or something incremented as part of a thread. We check to see if it is in the
+ // address range of the status_var structure of the current thread
+ char *off;
+ off = (char *) V;
+ if (off >= (char *)&(thd->status_var) && off < (char *)&(thd->status_var)+sizeof(thd->status_var)) {
+ V = (ulong *)(off - (char *)&(thd->status_var)) ;
+ if STATS_DEBUG(STATS_INC_VAR) sql_print_information(" altered to: 0x%x, base: 0x%x",
+ V, &(thd->status_var));
+ }
+#endif
+
+ // check against the list of Vars we are tracking
+ for (i = 0; i < Shared->varAddrCount; i++) {
+ if (Shared->varAddrTrackArray[i] == (char *) V) { // one we are tracking
+ found = 1;
+ break;
+ }
+ } // end for - all tracked vars
+
+ if (!found) {
+ return;
+ }
+
+#ifndef PGSQL
+ // ZZ ignore slow_launch_threads and early byte counts - unstable at this point
+ if (V == (ulong *) &slow_launch_threads || thd->command == COM_CONNECT) {
+ return;
+ }
+#endif
+
+ // input checks
+ if (!thd
+#ifndef PGSQL
+ || !thd->thread_id
+#endif
+ ) { // don't do thread zero for now
+ if STATS_DEBUG(STATS_INC_PARAMS) {
+ sql_print_information("ExtSQL: Got null thread or thd 0");
+ }
+ return;
+ }
+
+ extsql_init_proc_vars(&threadID, &command, &user, &db, &host, &proc_info, &indexPtr, &statVerify);
+
+ // we need something for these values
+ if (!user) {
+ user="(null)";
+ }
+
+ if (!host) {
+ host="(null)";
+ }
+
+ if (*statVerify != STATS_MAGIC_NUM) {
+ Shared->extsql_counter++; // ZZ - how many do we get?
+ return;
+ } // end if - badmagic
+
+
+ // too many warnings, turn it off
+ if (Shared->extsql_counter > STATS_WARNING_LIMIT) {
+ Shared->extsql_active = 0;
+ sql_print_warning("ExtSQL: deactivated due to high internal warning count(%d), dumping internals",
+ (int)Shared->extsql_counter);
+ extsql_stats_dump();
+ return;
+ }
+
+ if STATS_DEBUG(STATS_THREAD_CHK) {
+ extsql_thread_dump(thd, 0); // do a check of indexes, no output unless error
+ }
+
+ // we get time in seconds, for performance we only call localtime once/minute to do the min,hour,day conversions
+ // could use thd->start_time, always avail? ZZ
+ now_sec = time(NULL);
+
+ if (Shared->lastMin != (int)(now_sec/60)) { // we rolled over
+
+ // let's try and get the lock, if we fail, someone else doing rollover
+ if (pthread_mutex_trylock(&Shared->LOCK_stats_clock)) {
+ // nothing to do
+
+ } else {
+
+ struct tm now_date;
+ localtime_r(&now_sec, &now_date);
+
+ Shared->lastMin = (int)(now_sec/60);
+
+ // check all time for classes
+ classCount = 0;
+ for (stats_class_data = Shared->statData;
+ classCount < STATS_MAX_CLASSES && stats_class_data->maxInstance;
+ stats_class_data++, classCount++) {
+
+ int now_time;
+
+ switch (stats_class_data->timeUnits) {
+ case 'm':
+ now_time = now_date.tm_min;
+ break;
+
+ case 'h':
+ now_time = now_date.tm_hour;
+ break;
+
+ case 'd':
+ now_time = now_date.tm_mday;
+ break;
+
+ default:
+ now_time = now_date.tm_hour;
+ Shared->extsql_active = 0;
+ sql_print_error("ExtSQL: deactivated, got invalid time unit (%d)", stats_class_data->timeUnits);
+
+#ifdef PGSQL
+ pthread_mutex_unlock(&Shared->LOCK_stats_clock);
+#else
+ VOID(pthread_mutex_unlock(&Shared->LOCK_stats_clock));
+#endif
+ return;
+ }
+
+ if (now_time != stats_class_data->lastTime) { // rolled over
+ stats_class_data->lastTime = now_time;
+
+ // increment time and do checks
+ stats_class_data->time++;
+
+ if (stats_class_data->time >= stats_class_data->maxTime) { // reset to start
+ stats_class_data->time = 1; // zero index is used for totals
+ }
+
+ // create label, MM/DD HH:MM
+
+ if STATS_DEBUG(STATS_TIME_CHANGE) {
+ sql_print_information("ExtSQL, class %s, time change: cur clock time is %d, time index s %d",
+ stats_class_data->name, now_time, stats_class_data->time);
+ }
+
+ // reset counters for that time to zero for all variables
+ for (instance=0, maxVar = stats_class_data->maxVar, maxTime = stats_class_data->maxTime;
+ instance < stats_class_data->maxInstance; instance++) {
+ for (var=0; var < maxVar; var++) {
+
+ offset = ARRAY_INDEX(instance, var, maxVar, stats_class_data->time, maxTime);
+ offsetLimit = stats_class_data->dataEnd - stats_class_data->data;
+
+
+ // check to make sure offset isn't out of limits
+ if (offset < 0 || offset >= offsetLimit ) {
+ sql_print_warning("ExtSQL: warning, TIME reset offset of 0x%x (%d,%d,%d,%d,%d) for 0x%x class %s of thd %d/%s exceeds bound of 0x%x(0x%x)",
+ offset, instance, var, maxVar, stats_class_data->time, maxTime,
+ (uint)V, stats_class_data->name, threadID, command_name[command], offsetLimit, *statVerify);
+ Shared->extsql_counter++;
+ extsql_thread_dump(thd, 1);
+#ifdef PGSQL
+ pthread_mutex_unlock(&Shared->LOCK_stats_clock);
+#else
+ VOID(pthread_mutex_unlock(&Shared->LOCK_stats_clock));
+#endif
+ return;
+ }
+
+ stats_class_data->data[offset] = 0;
+
+ // store unix time
+ // we normally round it off to minutes/hours/days depending on units
+ if (!STATS_DEBUG(STATS_TIME_DETAIL)) {
+
+ switch (stats_class_data->timeUnits) {
+ case 'm':
+ log_sec = now_sec - (now_sec % 60);
+ break;
+
+ case 'h':
+ log_sec = now_sec - (now_sec % 3600);
+ break;
+
+ case 'd':
+ log_sec = now_sec - (now_sec % 3600);
+ break;
+
+ default:
+ now_time = now_date.tm_hour;
+ Shared->extsql_active = 0;
+ sql_print_error("ExtSQL: deactivated, got invalid time unit (%d)", stats_class_data->timeUnits);
+ return;
+ }
+
+ } else {
+
+ log_sec = now_sec;
+
+ } // end if/else - don't round off time
+
+ stats_class_data->timeLabels[stats_class_data->time] = log_sec;
+
+ } // end for - vars
+ } // end for - instances
+ } // end if - new time
+
+ } // end for - all classes
+
+#ifdef PGSQL
+ pthread_mutex_unlock(&Shared->LOCK_stats_clock);
+#else
+ VOID(pthread_mutex_unlock(&Shared->LOCK_stats_clock));
+#endif
+ } // end if-else - got the lock
+ } //end if - time rolled over
+
+
+ // ready to increment value
+ if STATS_DEBUG(STATS_INC_VAR) sql_print_information("ExtSQL: 0x%x by %d", (uint)V,(int)C);
+
+ // look for the value being tracked in all classes
+ classCount = 0;
+ for (stats_class_data = Shared->statData;
+ classCount < STATS_MAX_CLASSES && stats_class_data->maxInstance;
+ stats_class_data++, classCount++) {
+
+ var_ptr = stats_class_data->varIndex;
+ for (var = 0, maxVar = stats_class_data->maxVar, maxTime = stats_class_data->maxTime
+ ; var < maxVar ; var_ptr++,var++) {
+ if ((*var_ptr)->addr == (char *)V) { // we are tracking it
+
+ i = indexPtr[(int)classCount];
+
+ if (i < 0 || i >= stats_class_data->maxInstance) { // bound exceeded
+ sql_print_warning("ExtSQL: index (%d) bad value (0x%x/0x%x), V(0x%x), classCount(%d), thd(%d/%s/%s) user(%s), host(%s) db(%s) magic(0x%x)",
+ classCount, i, stats_class_data->maxInstance, (uint) V, classCount,
+ threadID, proc_info, command_name[command],
+ user, host, db, *statVerify);
+ Shared->extsql_counter++;
+ extsql_thread_dump(thd, 1);
+ return;
+ }
+
+ // increment it for the class for the 0 (summary time) and the current time
+ offset = ARRAY_INDEX(i, var, maxVar, stats_class_data->time, maxTime);
+ offsetLimit = stats_class_data->dataEnd - stats_class_data->data;
+
+ if STATS_DEBUG(STATS_INC_VAR) {
+ sql_print_information("ExtSQL, class %s, i (%d) incrementing %s by (%d), offset/Limit(%d/%d)",
+ stats_class_data->name, i, (*var_ptr)->name, (int) *V, offset, offsetLimit);
+ }
+
+ // check to make sure offset isn't out of limits
+ if (offset < 0 || offset >= offsetLimit ) {
+ sql_print_warning("ExtSQL: warning, DATA inc offset of 0x%x (%d,%d,%d,%d,%d) for 0x%x class %s of thd %d/%s exceeds bound of 0x%x(0x%x)",
+ offset, i, var, maxVar, stats_class_data->time, maxTime,
+ (uint)V, stats_class_data->name, threadID, command_name[command], offsetLimit, *statVerify);
+ Shared->extsql_counter++;
+ extsql_thread_dump(thd, 1);
+ return;
+ }
+
+ stats_class_data->data[offset] += C;
+
+ offset = ARRAY_INDEX(i, var, maxVar, 0, maxTime);
+ stats_class_data->data[offset] += C;
+ break; // check the next class
+ } // end if - match
+ } // end for - all vars
+
+ } // end for - all classes
+
+} // end function extsql_inc
+
+#ifdef EXTSQL_BINARY
+#include "../../../../../../../bin/checkParam.h"
+#endif
+
+
+// extsql_prep - invoked to set/confirm start type
+// OUTPUT: startType - STATS_INIT_FAILURE, _NORMAL, _RESET, _RELOAD
+// it will log an error message on _FAILURE
+// INPUT: confFile - may be NULL (startType = NORMAL). If present the files is scanned
+// for new global extsql vars: _class_list, _users, _reload_file, _debug, _license_key
+// and the startType is set to _RESET.
+// reload_file - may be NULL. If present and contents is valid, startType
+// set to _RELOAD, file is scanned to make sure hostname and class list matches.
+// NO global vars are changed!!
+// NOTE: At present only ONE of confFile and realodFile may be set, error otherwise.
+//
+void extsql_prep(int *startType, const char *confFile, char *reloadFile) {
+
+ // if no extsql_reload_file is present:
+ // mysql: reloadFile == 0
+ // pgsql: reloadFile[0] == 0
+
+ extsql_init_globals();
+
+ if (! STATS_DEBUG(STATS_ADMIN) ) { // always normal
+ *startType = STATS_INIT_NORMAL;
+ return;
+ }
+
+#ifdef PGSQL
+ if (!*startType && *reloadFile) *startType = STATS_INIT_RELOAD;
+#else
+ if (!*startType && reloadFile) *startType = STATS_INIT_RELOAD;
+#endif
+
+ if (*startType == STATS_INIT_RELOAD && reloadFile != NULL) {
+
+ // read reload file
+ File reloadHandle;
+ char *memPtr = NULL, *srcPtr = NULL, *tmpPtr = NULL, *tmpPtr2 = NULL;
+ int size, i, fieldLength;
+ char *rfVersion = NULL, *rfClassList = NULL, *rfHostname = NULL;
+ char *extsql_class_listNoWS; char hostname[FN_REFLEN];
+
+ if (gethostname(hostname, FN_REFLEN) < 0) {
+ strcpy(hostname, "localhost");
+ }
+
+ if ((reloadHandle = my_open(reloadFile, O_RDONLY|O_BINARY, MYF(0))) < 0 ) {
+ sql_print_information("ExtSQL: Specified reload_file could not be opened: %s", reloadFile);
+ *startType = STATS_INIT_FAILURE;
+ return;
+ }
+
+ if (!(size = extsql_read_section_bytes(reloadFile, reloadHandle, &memPtr,
+ "RELOAD", 0))) {
+ sql_print_error("ExtSQL: Error reading from file %s", reloadFile);
+ *startType = STATS_INIT_FAILURE;
+ return;
+ }
+
+ // find version, classlist and hostname
+ for (srcPtr = memPtr, i = 0; i < size; srcPtr++, i++) {
+
+ if (!strncmp(srcPtr, "host: ", 6)) { // found hostname
+ tmpPtr = srcPtr + strlen("host: ");
+ fieldLength = 0;
+ while (*tmpPtr != '\n') {
+ ++fieldLength;
+ ++tmpPtr;
+ }
+
+ rfHostname = strndup(srcPtr + strlen("host: "), fieldLength);
+ }
+
+ if (!strncmp(srcPtr, "EXT version: ", 17)) { // found extsql version
+ tmpPtr = srcPtr + strlen("EXT version: ");
+ fieldLength = 0;
+ while (*tmpPtr != '\n') {
+ ++fieldLength;
+ ++tmpPtr;
+ }
+
+ ++fieldLength; // we want the trailing '\n' here
+
+ rfVersion = strndup(srcPtr + strlen("EXT version: "), fieldLength);
+ }
+
+ if (!strncmp(srcPtr, "extsql_class_list: ", 19)) { // found class list
+ tmpPtr = srcPtr + strlen("extsql_class_list: ");
+ fieldLength = 0;
+
+ while (*tmpPtr != '\n') {
+ ++fieldLength;
+ ++tmpPtr;
+ }
+
+ rfClassList = strndup(srcPtr + strlen("extsql_class_list: "),
+ fieldLength);
+ }
+ }
+
+ // by here, all pointers should be set, or we've got a badly
+ // formatted reload file
+ if (!(rfVersion && rfClassList && rfHostname)) {
+ sql_print_error("ExtSQL: Malformatted reload file %s", reloadFile);
+ *startType = STATS_INIT_FAILURE;
+ return;
+ }
+
+ // check for mismatches that are okay, but require confirmation
+ if (strcmp(rfHostname, hostname)) {
+ sql_print_information("ExtSQL: hostname mismatch (file: %s, server: %s)",
+ rfHostname, hostname);
+ *startType = STATS_INIT_CONFIRM;
+ return;
+ }
+
+ if (strcmp(rfVersion, Shared->extsql_version+1)) {
+ sql_print_information("ExtSQL: version mismatch (file: %s, server: %s)",
+ rfVersion, Shared->extsql_version+1);
+ *startType = STATS_INIT_CONFIRM;
+ return;
+ }
+
+
+ // we need a duplicate of extsql_class_list without whitespace
+ extsql_class_listNoWS = extsql_strdup(Shared->extsql_class_list, MYF(MY_WME));
+ for (tmpPtr = Shared->extsql_class_list, tmpPtr2 = extsql_class_listNoWS;
+ *tmpPtr != '\0'; ++tmpPtr) {
+
+ if (!(*tmpPtr == ' ' || *tmpPtr == '\t'))
+ *tmpPtr2++ = *tmpPtr;
+ }
+
+ *tmpPtr2 = '\0';
+
+ if (strcmp(rfClassList, extsql_class_listNoWS)) {
+ sql_print_information("ExtSQL: class list mismatch (file: %s, server: %s)",
+ rfClassList, extsql_class_listNoWS);
+ *startType = STATS_INIT_CONFIRM;
+ return;
+ }
+
+ my_free(extsql_class_listNoWS, MYF(0));
+ my_free(rfHostname, MYF(0));
+ my_free(rfVersion, MYF(0));
+ my_free(rfClassList, MYF(0));
+ my_free(memPtr, MYF(0));
+
+ } else if (*startType == STATS_INIT_RESET && confFile != NULL) {
+
+ char line[2048];
+ FILE* conf = fopen(confFile, "r");
+
+ // parse conf file
+ if (conf == NULL) {
+ sql_print_error("ExtSQL: Unable to open conf file %s", confFile);
+ *startType = STATS_INIT_FAILURE;
+ return;
+ }
+
+ while (fgets(line, 1024, conf) != NULL) {
+
+ // lowercase directive name and strip all whitespace
+ bool foundEqual = false;
+ int equalPos = 0, readPos = 0, writePos = 0;
+ char *directive, *data;
+
+ while (line[readPos] != '\0') {
+
+ if (line[readPos] == '=') {
+ foundEqual = true;
+ equalPos = writePos;
+ }
+
+ if (!foundEqual && line[readPos] > 64 && line[readPos] < 91)
+ line[readPos] += 32;
+
+ if (!(line[readPos] == '\t' || line[readPos] == ' ' ||
+ line[readPos] == '\n' || line[readPos] == '"' ||
+ line[readPos] == '\'')) {
+ line[writePos] = line[readPos];
+ ++writePos;
+ }
+
+ ++readPos;
+ } // end while line
+
+ line[writePos] = '\0';
+
+ if (strncmp(line, "extsql_", 7)) continue;
+
+ directive = strndup(line, equalPos);
+ data = line + (equalPos + 1) * (sizeof(char));
+
+ // set the appropriate data member
+ if (!strcmp(directive, "extsql_class_list"))
+ Shared->extsql_class_list = extsql_strdup(data, MYF(MY_WME));
+ else if (!strcmp(directive, "extsql_users"))
+ extsql_users = extsql_strdup(data, MYF(MY_WME));
+ else if (!strcmp(directive, "extsql_reload_file"))
+ Shared->extsql_reload_file = extsql_strdup(data, MYF(MY_WME));
+ else if (!strcmp(directive, "extsql_debug"))
+ Shared->extsql_debug = atoi(extsql_strdup(data, MYF(MY_WME)));
+ else if (!strcmp(directive, "extsql_key")) {
+ extsql_key = extsql_strdup(data, MYF(MY_WME));
+ sql_print_information("ExtSQL: extsql_key %s", data);
+ }
+ else {
+ sql_print_error("ExtSQL: Unknown configuration directive: %s",
+ directive);
+ *startType = STATS_INIT_FAILURE;
+ }
+
+ } // end while read conf
+
+ } else {
+ *startType = STATS_INIT_NORMAL;
+ }
+}
+
+
+
+// extsql_init_globals - MySQL uses threads for each server, Postgres uses a separate process.
+// Shared memory is used to speed our operations, data seg is shared between threads in MySQL,
+// but separate in postgres. To keep common code we need to alloc the area for our globals,
+// store in section //GLOBALS a top of this file. Not really needed to MySQL, but doesn't hurt.
+// BIG code change, need to switch all refs to pointers!
+// my_malloc is ifdef'd for PGSQL to do the shared memory magic!
+// Return 1 on failure
+int extsql_init_globals() {
+
+#ifdef PGSQL
+ // we allocate a big hunk of shared memory
+ bool memFound = false;
+
+ ShmemPtr = (char *) ShmemInitStruct("ExtSQL Data", SHMEM_SIZE, &memFound);
+
+ if (!ShmemPtr) {
+ sql_print_error("ExtSQL extsql_init_globals: can't init shared mem area of %d bytes", SHMEM_SIZE);
+ return(1);
+ }
+
+ if STATS_DEBUG(STATS_DUMP_START) {
+ sql_print_information("ExtSQL extsql_init_globals: ShmemPtr at 0x%x, memFound(%d)", (uint)ShmemPtr, memFound);
+ }
+
+ if (memFound) { // issue warning, should have only been called once!
+
+ // set our two pointers again
+ MemLock = (slock_t *) ShmemPtr; // already initialized.
+ NextFree = (char **) ShmemPtr + sizeof(slock_t *);
+ return(0); // ZZ - not fatal?
+ }
+
+ // if we get here, FIRST and ONLY time through for original allocation.
+
+ // our MemLock and NextFree live at the start of the region.
+ // this is needed for the PGSQL version of extsql_malloc
+ MemLock = (slock_t *) ShmemPtr;
+ SpinLockInit(MemLock);
+ NextFree = (char **)ShmemPtr + sizeof(slock_t *);
+ *NextFree = (char *)NextFree + sizeof(NextFree);
+
+ // okay alloc the global space
+ Shared = (pGLOBAL) extsql_malloc(sizeof(GLOBAL), MYF(MY_WME | MY_ZEROFILL) );
+ if (!Shared) {
+ sql_print_error("ExtSQL extsql_init_globals: can't alloc shared area.");
+ return(1);
+ }
+
+ Shared->extsql_class_list = extsql_strdup(extsql_class_list, MYF(MY_WME));
+ Shared->extsql_key = extsql_strdup(extsql_key, MYF(MY_WME));
+ Shared->extsql_users = extsql_strdup(extsql_users, MYF(MY_WME));
+ Shared->extsql_version = extsql_strdup(extsql_version, MYF(MY_WME));
+ Shared->extsql_reload_file = extsql_strdup(extsql_reload_file, MYF(MY_WME));
+
+#else
+ static int didInit = 0;
+
+ if (!didInit) {
+ // THIS IS MYSQL, the Shared structure is static and already declared.
+ Shared->extsql_class_list = extsql_class_list;
+ Shared->extsql_key = extsql_key;
+ Shared->extsql_users = extsql_users;
+ Shared->extsql_version = extsql_version;
+ Shared->extsql_reload_file = extsql_reload_file;
+ didInit = 1;
+ } else {
+ return(0);
+ }
+
+ (void) pthread_mutex_init(&Shared->LOCK_stats_clock,MY_MUTEX_INIT_FAST);
+#endif
+
+ // take care of copying some startup global values
+ Shared->extsql_active = extsql_active;
+ Shared->extsql_counter = extsql_counter;
+ Shared->extsql_debug = extsql_debug;
+
+
+ if STATS_DEBUG(STATS_DUMP_START) {
+ sql_print_information("ExtSQL: Globals are active(%d), debug(%d), extsql_class_list(%s), extsql_version(%s), extsql_users(%s), extsql_reload_file(%s)",
+ (int)Shared->extsql_active, (int)Shared->extsql_debug, Shared->extsql_class_list,
+ Shared->extsql_version, Shared->extsql_users, Shared->extsql_reload_file);
+ }
+
+
+ return(0);
+
+} // end extsql_init_globals
+
+
+// Called to initialize tracking data structures, potentially reload with prior data.
+// We may be here from a NORMAL init, first time, or a RESET in the middle of a server run
+// or a RELOAD from a start or the middle of server run.
+// extsql_prep has already been invoked, so global extsql_ vars are set
+int extsql_init(char *extsql_classlist, int startType, char *reloadFile)
+{
+ int extsql_willbe_active = 1; // track state during init process, global extsql_active is
+ // always set to off during init.
+ int reloadFail = 0;
+
+ //extsql_class_list is a comma separated list of which define what we will
+ // be tracking, number of instances, times of data, and which variables
+ // "db,max-5,time-10,units-d(Com_show_tables,Com_show_variables),
+ // user,max-50,time-24,units-m(Bytes_sent,Bytes_received)"
+ // class_name, max-instance_limit, max-time_limit, units-(min|day|hour) (var_list)
+ char *parse_string, *malloc_addr;
+ char *token, *tmpPtr;
+ pSTATS_CLASS_DATA stats_class_data;
+ STAT_VAR **var_ptr = 0;
+ struct show_var_st *status_var_ptr = status_vars;
+ int found = 0, skip = 0, foundParen = 0, num = 0, i = 0, j = 0, size = 0, classCount = 0, totalSize = 0;
+ int thisDebug = 0; char timeUnit;
+
+ sql_print_information("ExtSQL start type(%s): %s", startTypeName[startType], Shared->extsql_version+1);
+
+ // allocate our global area
+ if (extsql_init_globals()) {
+ sql_print_error("ExtSQL: start aborted. Failed to alloc global shared mem.");
+ return(1);
+ }
+
+ // Shared->extsql_class_list = my_strdup(extsql_class_list, MYF(MY_WME));
+ // Shared->extsql_reload_file = my_strdup(extsql_reload_file, MYF(MY_WME));
+
+
+ Shared->extsql_active = 0; // will be set at end of this function if returned to active
+
+
+ if (startType == STATS_INIT_FAILURE) {
+ sql_print_error("ExtSQL: disabled. Start aborted due to prior errors");
+
+ // We do not set Shared->extsql_disabled, may just be a failed reload/reset, okay to try again.
+ return(1);
+ }
+
+
+#ifdef EXTSQL_BINARY
+
+ int status, days;
+
+ // license sequence is ONLY area for returns before end of function!
+ if (!extsql_key) {
+ sql_print_error("ExtSQL: disabled. No license key found for binary build, make sure to define ExtSQL_key in /etc/my.cnf.");
+ Shared->extsql_active = 0; // turn it all off
+ Shared->extsql_disabled = 1;
+ return(1);
+ }
+
+ if (status = checkParam(Shared->extsql_version+1, extsql_key, &days)) {
+ sql_print_error("ExtSQL: disabled. Improper license key found(%s), code %d.",
+ Shared->extsql_version+1, status);
+ Shared->extsql_active = 0; // turn it all off
+ Shared->extsql_disabled = 1; // turn it all off
+ return(1);
+ }
+
+ if (days == 99999) {
+ sql_print_information("ExtSQL: Permanent license verified.");
+
+ } else if (days < 31) {
+
+ // limited time
+ sql_print_warning("ExtSQL: Evaluation license good for server startup only %d more days.", days);
+
+ } else {
+
+ // expired
+ sql_print_error("ExtSQL: disabled. Evaluation period has expired.");
+ Shared->extsql_disabled = 1; // turn it all off
+ Shared->extsql_active = 0; // turn it all off
+ return(1);
+
+ }
+
+#endif
+
+ if (!extsql_users) {
+ sql_print_information("ExtSQL: no defined user list, only root user allowed");
+ extsql_users="root";
+ } else {
+ sql_print_information("ExtSQL: access allowed only to users: %s", extsql_users);
+ }
+
+ if (!extsql_class_list) {
+ extsql_willbe_active = 0; // turn it all off
+ Shared->extsql_disabled = 1; // turn it all off
+ Shared->extsql_active = 0; // turn it all off
+ sql_print_information("ExtSQL: deactivated by user, no extsql_class_list defined");
+ return(1);
+
+ } else {
+ extsql_willbe_active = 1;
+ }
+
+ //init our var addr tracking array to unstored
+ bzero((char *) Shared->varAddrTrackArray, sizeof(Shared->varAddrTrackArray));
+ Shared->varAddrCount = 0;
+
+ // if we are here because of a RESET, there should be something in the statAllocArray
+ // from prior - check to make sure -- if _NORMAL, we zero the whole thing.
+ if (startType != STATS_INIT_NORMAL) { // _RELOAD or _RESET
+
+ // wait just a bit for all threads to know we've stopped
+ Shared->extsql_reload_in_progress = 1;
+ sleep(2);
+
+ if (startType == STATS_INIT_RESET && ( ! Shared->statsAllocArray[0] || ! Shared->allocIndex) ) { // empty!
+ sql_print_error("ExtSQL: deactivated, RESET request failed, no prior data found.");
+ extsql_willbe_active = 0;
+
+ } else { // free up any existing for RELOAD or RESET
+
+ char *addr;
+ for (i = 0, addr = Shared->statsAllocArray[i]; (addr = Shared->statsAllocArray[i]) && i < STATS_MAX_NUM_ALLOC; i++) {
+ my_free(addr, MYF(0));
+ }
+ } // end if - memory clear necessary
+
+ } else {
+
+ Shared->extsql_reload_in_progress = 0;
+
+ } // end if-else startType other than NORMAL
+
+
+ bzero((char *) Shared->statsAllocArray, sizeof(Shared->statsAllocArray));
+ Shared->allocIndex = 0; // for recording mem allocations
+ bzero((char *) &Shared->statData, sizeof(Shared->statData)); // zero out all the contents, support restart - reload
+
+ if STATS_DEBUG(STATS_DUMP_START) sql_print_information("ExtSQL tracking classes: %s", extsql_class_list); //DEBUG
+
+ if (!(parse_string = extsql_strdup(extsql_class_list, MYF(MY_WME)))) {
+ Shared->extsql_active = 0;
+ Shared->extsql_disabled = 1;
+ Shared->extsql_reload_in_progress = 0;
+ sql_print_error( "ExtSQL: disabled, unable to dup extsql_class_list(%s)",
+ extsql_class_list);
+ return(1);
+
+ }
+
+ if (startType == STATS_INIT_NORMAL || startType == STATS_INIT_RESET) { // init structures on RESET/NORMAL starts, skip on RELOAD
+
+ int i = 0; // track our position in statVars
+ Shared->statsAllocArray[Shared->allocIndex++] = parse_string;
+ malloc_addr = parse_string;
+
+
+ while ( (token = strtok(parse_string, ", ")) ) { // start new loop on each class name
+
+ char **tPtr;
+ parse_string = NULL;
+ found = 0; num = 0;
+
+ // should have a class name
+ if (thisDebug) sql_print_information("ExtSQL, Class token is (%s)", token);
+
+
+ // is it allowable
+ for (tPtr = extsqlClassesSupported; *tPtr; tPtr++) {
+ if (!strcmp(token, *tPtr)) {
+ found = 1;
+ break;
+ }
+ } // end for - allowable class names
+
+ if (! found) {
+ extsql_willbe_active = 0;
+ sql_print_error("ExtSQL: deactivated, bad class name (%s)", token);
+ break;
+ }
+
+ // we have a valid class name, make sure it is not a dup
+ found = 0;
+ for (stats_class_data = Shared->statData; stats_class_data->name[0]; stats_class_data++) {
+ if (!strcmp(token, stats_class_data->name)) {
+ found = 1;
+ break;
+ }
+ } // end for - existing classes
+
+ if (found) {
+ extsql_willbe_active = 0;
+ sql_print_error("ExtSQL: deactivated, duplicate class name (%s)", token);
+ break;
+ }
+
+ // okay we have a good name, start loading the structure
+ strncpy(stats_class_data->name, token, STATS_MAX_NAME-1);
+
+ // set our starting time
+ stats_class_data->time = 0;
+
+
+ // get the next token, should be instance limit
+ if (! (token = strtok(parse_string, ", "))) {
+ extsql_willbe_active = 0;
+ sql_print_error("ExtSQL: deactivated, incomplete class definitions, started with (%s)",
+ extsql_class_list);
+ break;
+ }
+
+ // should start with max-
+ if (strncmp(token, "max-", 4)) {
+ extsql_willbe_active = 0;
+ sql_print_error("ExtSQL: deactivated, missing max instance amount, got (%s)",
+ token);
+ break;
+ }
+
+ // get the number for max instances and allocate space
+ num = atoi(token+4);
+ if (!num) {
+ extsql_willbe_active = 0;
+ sql_print_error("ExtSQL: deactivated, missing valid instance amount, got (%s)",
+ token);
+ break;
+ }
+ stats_class_data->maxInstance = num;
+
+ // we null term it by getting one more than required
+ if (!(stats_class_data->instanceIndex = (char **)extsql_malloc( ((num+1) * sizeof(char *)),
+ MYF(MY_WME | MY_ZEROFILL)) )) {
+ extsql_willbe_active = 0;
+ sql_print_error("ExtSQL: deactivated, can't allocate class instance memory");
+ break;
+ }
+ Shared->statsAllocArray[Shared->allocIndex++] = (char *)stats_class_data->instanceIndex;
+
+
+ // good instance limit, now get time limit
+ if (! (token = strtok(parse_string, ", "))) {
+ extsql_willbe_active = 0;
+ sql_print_error("ExtSQL: deactivated, incomplete class definitions, missing time limit, started with (%s)",
+ extsql_class_list);
+ break;
+ }
+
+ // should start with time-
+ if (strncmp(token, "time-", 5)) {
+ extsql_willbe_active = 0;
+ sql_print_error("ExtSQL: deactivated, missing max time amount, got (%s)",
+ token);
+ break;
+ }
+
+ // get the number for max times
+ num = atoi(token+5);
+ num += 1; // always the zero hour - totals
+ if (!num || num < 2) {
+ extsql_willbe_active = 0;
+ sql_print_error("ExtSQL: deactivated, missing valid time amount, got (%s)",
+ token);
+ break;
+ }
+ stats_class_data->maxTime = num;
+
+ // allocate space ZZ add to size count reported
+ if (!(stats_class_data->timeLabels = (my_time_t *)extsql_malloc( ((num+1) * sizeof(my_time_t)),
+ MYF(MY_WME | MY_ZEROFILL)) )) {
+ extsql_willbe_active = 0;
+ sql_print_error("ExtSQL: deactivated, can't allocate class time label memory");
+ break;
+ }
+ Shared->statsAllocArray[Shared->allocIndex++] = (char *) stats_class_data->timeLabels;
+
+ // get the time Units, m - minutes, h - hours, d - days
+ if (! (token = strtok(parse_string, ", "))) {
+ extsql_willbe_active = 0;
+ sql_print_error("ExtSQL: deactivated, incomplete class definitions, missing timeUnits started with (%s)",
+ token);
+ break;
+ }
+
+ // should start with units-
+ if (strncmp(token, "units-", 6)) {
+ extsql_willbe_active = 0;
+ sql_print_error("ExtSQL: deactivated, missing units amount, got (%s)",
+ token);
+ break;
+ }
+
+ // get the time units
+ timeUnit = *(token+6);
+ if (timeUnit == 'm') {
+ stats_class_data->timeUnits = 'm';
+ } else if (timeUnit == 'h') {
+ stats_class_data->timeUnits = 'h';
+ } else if (timeUnit == 'd') {
+ stats_class_data->timeUnits = 'd';
+ } else {
+ extsql_willbe_active = 0;
+ sql_print_error("ExtSQL: deactivated, missing valid time unit amount, got (%c)",
+ timeUnit);
+ break;
+ }
+
+ // set remaining vars
+ stats_class_data->maxVar = 0;
+ stats_class_data->lastTime = 0;
+
+ // okay, next token is var list like: (Com_show_tables,Com_show_variables) OR
+ // ( Com_.... )
+ // first and last token have parens
+ // clear out tmp storage
+ j = 0; // keep track of var tokens
+ i = 0;
+ foundParen = 0; // haven't seen closing paren
+ while (!foundParen && (token = strtok(parse_string, ", ")) ) {
+
+ if (thisDebug) sql_print_information("ExtSQL, variable token is (%s)", token);
+
+ // none of the tokens should be close to MAX_VAR_SIZE
+ if (strlen(token) > MAX_VAR_SIZE - 5) {
+ extsql_willbe_active = 0;
+ sql_print_error("ExtSQL, var name too long in var list (%s)",
+ token);
+ break;
+ }
+
+ // if we got a closing paren by itself, we're done
+ if (!strcmp(")", token)) {
+ break;
+ }
+
+ // should have a paren( on first one, move past that
+ if (!j) {
+ if (token[0] != '(') {
+ extsql_willbe_active = 0;
+ sql_print_error("ExtSQL, bad format in var list, missing ( in %s",
+ token);
+ break;
+ }
+
+ // okay, now we count the , from opening to closing paren, need to know
+ // how many vars there are so we can allocate space for pointer array to them
+ for (i = 0, num = 1, tmpPtr = token; *tmpPtr != ')'; tmpPtr++) {
+ if (*tmpPtr == ',') {
+ num++;
+ }
+ } // end for - token string
+
+ // we null term pointer array by alloc of one extra
+ if (!(stats_class_data->varIndex = (STAT_VAR **)extsql_malloc( ((num+1) * sizeof(pSTAT_VAR)),
+ MYF(MY_WME | MY_ZEROFILL)) )) {
+ extsql_willbe_active = 0;
+ sql_print_error("ExtSQL: deactivated, can't allocate class varIndex memory");
+ break;
+ }
+
+ Shared->statsAllocArray[Shared->allocIndex++] = (char *) stats_class_data->varIndex;
+ var_ptr = stats_class_data->varIndex; // pointer to array of pointer to each var we find below
+
+ // check to see if got just a ')', in that case we get the next
+ if (!strcmp(token, "(")) {
+ token = strtok(parse_string, ", ");
+ } else {
+ token++; // move off paren
+ }
+
+ if (thisDebug) sql_print_information("ExtSQL, FIRST variable token is (%s)", token);
+
+ } // end if - starting paren
+
+ if (token[strlen(token) - 1] == ')') { // last one
+ token[strlen(token) - 1] = '\0'; // null it out early
+ foundParen = 1;
+ if (thisDebug) sql_print_information("ExtSQL, LAST variable token is (%s)", token);
+ }
+
+ // unless the debug flag is set, check to see if allowed
+ if (!STATS_DEBUG(STATS_EXTRA_VARS)) {
+ char **varPtr; extsql_willbe_active = 0;
+ for (varPtr = allowedVars; *varPtr; varPtr++) {
+ if (!strcmp(*varPtr, token)) {
+ extsql_willbe_active = 1;
+ break;
+ } // end if
+ } // end for - all allowed
+
+ if (!extsql_willbe_active) {
+ sql_print_error("ExtSQL, requested tracking var (%s) not on approved list. Please check spelling and documenation. This setting can be overridden via debug flag (STATS_EXTRA_VARS)",
+ token);
+ break;
+ }
+ } // end if - check approved vars
+
+
+ j++; // token count
+
+ // we should now have a token, look for match in status_vars
+ found = 0; skip = 0;
+ for (status_var_ptr = status_vars; status_var_ptr->name; status_var_ptr++) {
+
+ if (thisDebug) sql_print_information("ExtSQL, status_var is %s(0x%x)",
+ status_var_ptr->name, (uint)status_var_ptr->value );
+
+ if (!strcmp(token, status_var_ptr->name)) { // match
+
+ if (!status_var_ptr->value && strcmp(token, "Questions")) { // no address assigned, not tracking this
+ // okay if Questions, we will fix below
+
+
+#ifdef EXTSQL_50
+ // okay if Bytes_received - first item
+ if (strcmp(token,"Bytes_received")) { // only okay for Bytes_received
+#endif
+ sql_print_error("ExtSQL, variable (%s) has no address, can not be tracked",
+ status_var_ptr->name);
+ extsql_willbe_active = 0;
+ break;
+#ifdef EXTSQL_50
+ }
+#endif
+ } // end if - no address
+
+ // allocate room for the structure itself, put the address in the pointer array
+ if (!(*var_ptr = (STAT_VAR *)extsql_malloc( (sizeof(STAT_VAR)),
+ MYF(MY_WME | MY_ZEROFILL)) )) {
+ extsql_willbe_active = 0;
+ skip = 1;
+ sql_print_error("ExtSQL: deactivated, can't allocate class var memory");
+ break;
+ }
+
+ Shared->statsAllocArray[Shared->allocIndex++] = (char *) *var_ptr;
+
+ if (!((*var_ptr)->name = extsql_strdup(status_var_ptr->name, MYF(MY_WME)))) {
+ extsql_willbe_active = 0;
+ sql_print_error( "ExtSQL: deactivated, unable to alloc for (%s)",
+ status_var_ptr->name);
+ break;
+ } // end if - malloc
+
+ Shared->statsAllocArray[Shared->allocIndex++] = (*var_ptr)->name;
+
+#ifdef PGSQL
+ (*var_ptr)->addr = (char *)status_var_ptr->value;
+#else
+
+ // SPECIAL here, if the value is Connections or Questions, we override with our internal
+ // tracking variable address (connects or queries) -- special case for them.
+ if (!strcmp(token, "Questions")) {
+ (*var_ptr)->addr = (char *)&Shared->queries;
+ } else if (!strcmp(token, "Connections")) {
+ (*var_ptr)->addr = (char *)&Shared->connects;
+ } else { // regular
+ (*var_ptr)->addr = (char *)status_var_ptr->value;
+ }
+#endif
+
+ found = 1;
+ if (thisDebug) sql_print_information("ExtSQL: tracking varIndex[%d] var(%s), addr(0x%x)\n",
+ i, (*var_ptr)->name, (uint)(*var_ptr)->addr);
+ (uint)stats_class_data->maxVar++;
+ i++;
+
+ // store the addr in our quick look up array, only if its not already there
+ if (extsql_store_var_addr((*var_ptr)->addr)) {
+ extsql_willbe_active = 0;
+ break;
+ }
+
+ break ; // look for next one
+
+ } // end if - match
+
+ } // end for - more status vars
+
+ if (skip) { // we missed a variable that had a zero address, that is okay.
+ continue;
+ }
+
+ if (! found ) { // could not complete match on one of their variables
+ extsql_willbe_active = 0;
+ sql_print_error( "ExtSQL: deactivated, could not complete variable match for (%s)",
+ token);
+ }
+
+ if (! extsql_willbe_active || ! stats_class_data->maxVar) { // had failure somewhere above
+ extsql_willbe_active = 0;
+ break;
+ }
+ var_ptr++;
+
+ } // end while - more tokens in var list
+
+ if (! extsql_willbe_active) { // had a failure above
+ break;
+ }
+
+ } // end while - more token in class definitions
+
+ } else if (startType == STATS_INIT_RELOAD) {
+
+ // make copy of class list in the event of failure
+ char* oldClassList = extsql_strdup(extsql_class_list, MYF(MY_WME));
+
+ if (extsql_reload(STATS_READ_RELOAD, reloadFile)) { // reload failed
+
+ sql_print_error("ExtSQL: Reload FAIL--resetting class list OLD(%s), NEW(%s)",
+ oldClassList, extsql_class_list);
+
+ // restore old class list
+ extsql_class_list = oldClassList;
+
+ // reset
+ startType = STATS_INIT_RESET;
+ extsql_willbe_active = 1; // reload should log error
+ reloadFail = 1; // flag to return error status
+ }
+
+ } // end if-else startType (NORMAL, RESET or RELOAD
+
+
+ if (extsql_willbe_active) { // calculate data sizes for each class and print what we got
+ size = 0;
+
+ for (stats_class_data = Shared->statData, classCount = 0;
+ classCount < STATS_MAX_CLASSES && stats_class_data->maxInstance;
+ stats_class_data++, classCount++) {
+
+ if STATS_DEBUG(STATS_DUMP_START) sql_print_information("ExtSQL, class %s, max inst %d, max vars %d, max time %d %d",
+ stats_class_data->name, stats_class_data->maxInstance,
+ stats_class_data->maxVar, stats_class_data->maxTime,
+ stats_class_data->timeUnits);
+
+ // loop through all vars
+ for (var_ptr = stats_class_data->varIndex, i = 0; i < stats_class_data->maxVar;
+ var_ptr++, i++) {
+ if STATS_DEBUG(STATS_DUMP_START) sql_print_information(" Var %s at addr 0x%x", (*var_ptr)->name, (uint)(*var_ptr)->addr);
+ } // end for all vars defined
+
+ size = stats_class_data->maxInstance * stats_class_data->maxVar *
+ stats_class_data->maxTime * sizeof(ulong);
+
+ // if NOT RELOAD (a NORMAL OR RESET start) allocate data space
+ if (startType != STATS_INIT_RELOAD) {
+ if (!(stats_class_data->data = (ulong *)extsql_malloc(size, MYF(MY_WME | MY_ZEROFILL)) )) {
+ extsql_willbe_active = 0;
+ sql_print_error("ExtSQL: deactivated, failed to alloc %d bytes for class %s data",
+ size, stats_class_data->name);
+ break;
+ }
+
+ Shared->statsAllocArray[Shared->allocIndex++] = (char *)stats_class_data->data;
+ stats_class_data->dataEnd = stats_class_data->data + size/sizeof(ulong);
+ }
+
+ // set end marker
+ if STATS_DEBUG(STATS_DUMP_START) sql_print_information(" Data allocated %d bytes, data start at 0x%x, end at 0x%x",
+ size, (uint)stats_class_data->data, (uint)stats_class_data->dataEnd);
+ totalSize += size;
+
+#ifdef PGSQL
+ if (totalSize + SHMEM_RESERVE > SHMEM_SIZE) {
+ extsql_willbe_active = 0;
+ sql_print_error("ExtSQL: deactivated, projected mem usage (%d) too close to limit of %d",
+ size, SHMEM_SIZE);
+ }
+#endif
+
+ } // end for - all classes
+ } // end if - stats will be active
+
+ if (startType != STATS_INIT_NORMAL) { // RELOAD or RESET, clear any stored thread instance info
+
+#ifndef PGSQL
+
+ // loop through ALL threads and zero indexes
+ i = 1;
+ while (pthread_mutex_trylock(&LOCK_thread_count)) { // didn't get it, do UNLOCK!!!!
+ sleep(i++);
+ sql_print_information("ExtSQL: Trying to completed RELOAD/RESET, can't get lock, sleeping for (%d)",i);
+ if (i > 5) {
+ extsql_willbe_active = 0;
+
+ VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ break;
+ }
+ } // end while - get thread lock
+
+
+ if (extsql_willbe_active) { // loop through all threads
+
+ I_List_iterator it(threads);
+ THD *tmp;
+ int *indexPtr, *statVerify;
+
+ while ((tmp=it++)) {
+ extsql_thread_init(tmp, STATS_THD_INIT_ALL);
+ } // end while - more threads
+
+ } // end if - extsqlwillbeactive
+
+ VOID(pthread_mutex_unlock(&LOCK_thread_count));
+#endif
+
+#ifdef NEVER__
+ }
+#endif
+
+ } // end if - RELOAD or RESET
+
+ if (extsql_willbe_active) {
+ Shared->extsql_active = 1;
+ sql_print_information("ExtSQL ACTIVE allocated memory: %d bytes for %d classes",
+ totalSize, classCount);
+ } else {
+ sql_print_information("ExtSQL NOT ACTIVE");
+ Shared->extsql_active = 0;
+ }// end if - stats active
+
+ Shared->extsql_reload_in_progress = 0; // in all cases.
+
+ if (Shared->extsql_active && !reloadFail) {
+ return(0);
+ } else {
+ return(1);
+ }
+
+} // end extsql_init
+
+
+// extsql_init_proc_vars - is used to abstract the different locations in which both
+// MySQL and Postgres store process/status info
+// this is always with respect to the current thread -- not all are available for both!
+static void extsql_init_proc_vars(int *threadID, int *command, char **user, char **db, char **host,
+ char **proc_info, int **indexPtr, int **statVerify) {
+
+#ifdef PGSQL
+ THD *thd = MyProc;
+#else
+ THD *thd = current_thd;
+#endif
+
+#ifdef PGSQL
+ *user = MyProcPort->user_name;
+ *statVerify = &thd->statVerify;
+ *indexPtr = thd->statIndex;
+ *proc_info = "n/a";
+ *host = "n/a";
+ *command = 1;
+ *threadID = getpid();
+#else
+
+#ifdef EXTSQL_50
+ *user = thd->security_ctx->user;
+ *host = thd->security_ctx->host;
+ *statVerify = &thd->security_ctx->statVerify;
+ *indexPtr = thd->security_ctx->statIndex;
+#else
+ *user = thd->user;
+ *host = thd->host;
+ *statVerify = &thd->statVerify;
+ *indexPtr = thd->statIndex;
+#endif
+
+ // more just MySQL
+ *command = thd->command;
+ *proc_info = (char *)thd->proc_info;
+ *threadID = thd->thread_id;
+#endif
+
+#ifdef PGSQL
+ *db = MyProcPort->database_name;
+#else
+ *db = thd->db;
+#endif
+
+
+} // end extsql_init_proc_vars
+
+// this function prints hex bytes from the startAddr for size
+static void extsql_print_bytes(char *startAddr, int size) {
+
+ char *tmpPtr;
+ int i;
+ char buff[120] = {'\0'};
+
+ for (i = 0, tmpPtr=startAddr; i < size; tmpPtr++, i++) {
+
+ if (!(i%8) && strlen(buff)) {
+ sql_print_information("ExtSQL: 0x%x - %s", (uint)startAddr+i, buff);
+ buff[0] = '\0';
+ }
+ sprintf(buff+strlen(buff), "0x%x (0x%x) / ", i, *tmpPtr);
+
+ } // end for - entire string
+
+ if (strlen(buff)) {
+ sql_print_information("ExtSQL: 0x%x - %s", (uint)startAddr+i, buff);
+ }
+
+} // end extsql_print_bytes
+
+
+// this utility function reds a section header in a
+// RELOAD file and stores to address of memPtr,
+// as input if memPtr is zero, this function allocates memory and it must be
+// freed by caller. If memPtr is nonzero, we assume caller has allocated space!
+// returns 0 on error, hdr is the name of the header we should be reading
+// num is context for an error.
+int extsql_read_section_bytes(char *fileName, File fileDes, char **memPtr,
+ char *hdr, int num) {
+
+ char buff[80];
+ int size;
+
+
+ // read the section hdr
+ if (my_read(fileDes, buff, STATS_SECTION_HDR, MYF(MY_WME)) != STATS_SECTION_HDR) {
+ sql_print_error("ExtSQL: Section header byte count read failed on file %s, (%s/%d)",
+ fileName, hdr, num);
+ return(0);
+ }
+
+ if (!strstr(buff, hdr)) { // missing
+ sql_print_error("ExtSQL: Section header from file %s unexpected, got %s, wanted %s",
+ fileName, buff, hdr);
+ return(0);
+ }
+
+ size = atoi(buff + STATS_SECTION_BYTES);
+
+ if (size <= 0) {
+ sql_print_error("ExtSQL: Section header bad size (%d) on file %s, (%s/%d)",
+ size, fileName, hdr, num);
+ return(0);
+ }
+
+
+ if (*memPtr == NULL) { // alloc memory for the data
+ if (!(*memPtr = (char *)extsql_malloc(size, MYF(MY_WME | MY_ZEROFILL)))) {
+ sql_print_error("ExtSQL: Could not alloc %d bytes to read data on file %s, (%s/%d)",
+ size, fileName, hdr, num);
+ return(0);
+ }
+ }
+
+ if (my_read(fileDes, *memPtr, size, MYF(MY_WME)) != size) {
+ sql_print_error("ExtSQL: Section header data read failed on file %s for %d bytes, (%s/%d)",
+ fileName, size, hdr, num);
+ return(0);
+ }
+
+ return(size);
+} // end extsql_read_section_bytes
+
+
+// extsql_write_section_hdr - writes the header for a section to the RELOAD
+// file. The hdr should curretnly be 6 chars in length. The size is the
+// size of the data to be stored in that section.
+// Returns non zero on error, puts msg in error log.
+static int extsql_write_section_hdr(char *loadFileName, File reloadFile, char *hdr, int size) {
+
+ char buff[60];
+
+ if (strlen(hdr) != 6) {
+ sql_print_error("ExtSQL: Bad header length for %s", hdr);
+ return(1);
+ }
+
+ sprintf(buff, "\nEXT %s: %09d\n", hdr, size);
+
+ if (strlen(buff) != STATS_SECTION_HDR) { // sanity check
+ sql_print_error("ExtSQL: Bad length for %s section hdr, %d/%d",
+ hdr, STATS_SECTION_HDR, strlen(buff));
+ return(1);
+ }
+
+
+ if (my_write(reloadFile, (byte*) buff, STATS_SECTION_HDR, MYF(MY_WME)) != STATS_SECTION_HDR) {
+ sql_print_error("ExtSQL: Could not write %s header section to RELOAD file %s (%d/%d)",
+ hdr, loadFileName, size, reloadFile);
+ return(1);
+ }
+
+ return(0);
+
+} // end - extsql_write_section_hdr
+
+
+// manage reload data - this function take care of reading/write reload data.
+// WRITE_RELOAD: It creates the file used for the RELOAD command, most of it
+// consists of binary data, but the first few lines are in ASCII so that a 'head' can
+// be done on the file to extract general info
+// READ_RELOAD: Reads in file, sets globa statData and extsql_class_list to what is found
+// in the file.
+// IN: action (STATS_WRITE_RELOAD, STATS_READ_RELOAD)
+// IN: loadFileName (name of reload file to read/write)
+// GLOBALS: will read/write extsql_class_list and statData
+int extsql_reload(int action, char *loadFileName) {
+
+ File reloadFile; // a file descriptor
+ char buff[STATS_TMP_BUFF_SIZE];
+ char *memPtr; // memPtr holds dynamic alloc, needs to be freed!
+ my_time_t now_sec;
+ MYSQL_TIME mysql_time;
+
+#ifndef PGSQL
+ THD *thd = _current_thd();
+#endif
+
+ char dateBuff[80];
+#define CLASS_SIZE 8192
+ char classBuff[CLASS_SIZE];
+ char *srcPtr, *destPtr, *actionStr, *className, **ptrPtr;
+
+ pSTATS_CLASS_DATA stats_class_data;
+ int maxVar, maxTime, maxInstance, var, found, instance, classCount, size, size2, mode, i;
+ int numClasses;
+ char hostname[FN_REFLEN]; // defined in my_global.h
+
+ if (! STATS_DEBUG(STATS_ADMIN)) { // disabled
+ sql_print_information(adminDisabled);
+ return(0);
+ }
+
+ if (gethostname(hostname, FN_REFLEN) < 0) { // failed
+ strcpy(hostname, "localhost");
+ }
+
+ // Shared->extsql_debug |= STATS_RELOAD;
+
+ if (action == STATS_WRITE_RELOAD) { // set some params
+ mode = O_WRONLY|O_CREAT|O_BINARY;
+ actionStr = "write/store";
+ } else if (action == STATS_READ_RELOAD) {
+ mode = O_RDONLY|O_BINARY;
+ actionStr = "read/restore";
+ } else {
+ sql_print_error("ExtSQL: unexpected reload request (%d)", action);
+ return(1);
+ }
+
+ if (strlen(Shared->extsql_class_list) >= CLASS_SIZE) {
+ sql_print_error("ExtSQL: extsql_class_list too long for configured limit, can't save RELOAD data.");
+ return(1);
+ }
+
+ // open the file
+ if ((reloadFile= my_open(loadFileName, mode, MYF(0))) < 0 ) {
+ sql_print_error("ExtSQL: Unable to open %s for %s of RELOAD data, aborted (%d).",
+ loadFileName, actionStr, errno);
+ return(1);
+ }
+ sql_print_information("ExtSQL: (%s) Reload file is %s, Current active extsql_class_list is %s",
+ actionStr, loadFileName, Shared->extsql_class_list);
+
+ // get the current date/time, do any conversions
+ now_sec = time(NULL);
+
+#ifdef PGSQL
+ // need time convesion ZZ
+ mysql_time = now_sec;
+#else
+ thd->variables.time_zone->gmt_sec_to_TIME(&mysql_time, (my_time_t) now_sec);
+#endif
+
+ my_datetime_to_str(&mysql_time, dateBuff);
+
+ // strip ALL blanks from current class_list, count classes
+ numClasses = 0;
+ for (srcPtr=Shared->extsql_class_list, destPtr=classBuff; *srcPtr; srcPtr++) {
+
+ if (*srcPtr != ' ' && *srcPtr != '\t') {
+ *destPtr++ = *srcPtr;
+ }
+
+ if (*srcPtr == ')') {
+ numClasses++;
+ }
+ } // end for - all chars
+
+ // null term
+ *destPtr = '\0';
+
+ // write/read the header lines first, following format, designed to allow grep "EXT" to show
+ // useful info:
+ //
+ // line 0 - "\nEXT RELOAD: 000000025\n (ALL section headers, 12 char prefix, 9 char size
+ // line 1 - "EXT data stored on: date/time\n"
+ // line 2 - "EXT From host: bit.bongo.com\n"
+ // line 3 - "EXT extsql version: version\n"
+ // line 4 - "EXT\n"
+ // line 3 - "EXT ExtSQL extsql_class_list: db,max-130,time-100,units-m,(Binlog_cache_disk_use,Binlog_cache_use...\n"
+ // line 5 - "EXT\n";
+
+ if (action == STATS_WRITE_RELOAD) {
+
+ sprintf(buff, "\
+EXT data stored: %s\n\
+EXT from host: %s\n\
+EXT version: %s\n\
+EXT num classes: %d\n\
+EXT\n\
+EXT extsql_class_list: %s\n\
+EXT\n", dateBuff, hostname, Shared->extsql_version+1, numClasses, classBuff);
+
+
+ if (extsql_write_section_hdr(loadFileName, reloadFile, "RELOAD", strlen(buff))) {
+ return(1);
+ }
+
+ if (my_write(reloadFile, (byte*) buff, strlen(buff), MYF(MY_WME)) != strlen(buff)) {
+ sql_print_error("ExtSQL: Could not %s header data to RELOAD file %s", actionStr, loadFileName);
+ return(1);
+ }
+
+ } else { // READ
+
+ char *classListStart;
+
+ memPtr = NULL; // read_section_bytes will alloc space
+ if (!(size = extsql_read_section_bytes(loadFileName, reloadFile, &memPtr,
+ "RELOAD", 0))) {
+ return(1);
+ }
+
+ // find the stored class list.
+ found = 0;
+ for (srcPtr = memPtr, i = 0; i < size; srcPtr++, i++) {
+ if (!strncmp(srcPtr, "extsql_class_list:", 18)) { // found it
+ found = 1;
+ classListStart = srcPtr + strlen("extsql_class_list: ");
+ break;
+ }
+
+ } // end for - all chars in header
+
+ if (!found) {
+ sql_print_error("ExtSQL: Could not find class info in %s, section size (%d) (%s)",
+ loadFileName, size, memPtr);
+ my_free(memPtr, MYF(0));
+ return(1);
+ }
+
+ // NULL term the class list
+ for (i = 0; *(srcPtr+i) != '\n'; i++) ;
+ *(srcPtr+i) = '\0'; // null term
+
+ // check for new class list
+ if (strncmp(classListStart, classBuff, strlen(classBuff)) ||
+ strlen(classListStart) != strlen(classBuff)) {
+
+ char *numClassPtr;
+
+ if (!(Shared->extsql_class_list = extsql_strdup(srcPtr, MYF(MY_WME)))) {
+ sql_print_error("ExtSQL: reload failed, unable to alloc space for new class list");
+ return(1);
+ }
+
+ // check the number of classes present
+ numClasses = 0;
+ if (!(numClassPtr = strstr(memPtr, "num classes: "))) {
+ sql_print_error("ExtSQL: failed to find class count in reload file");
+ return(1);
+ }
+ numClasses = atoi(numClassPtr + strlen("num classes: "));
+
+
+ sql_print_information("ExtSQL: %d classes found, new class list will be loaded: %s",
+ numClasses, Shared->extsql_class_list);
+
+ } // end if - new class list
+
+ my_free(memPtr, MYF(0));
+
+ } // end if - else (first section)
+
+ if STATS_DEBUG(STATS_RELOAD) {
+ sql_print_information("ExtSQL: during %s on %s, completed section RELOAD", actionStr, hostname);
+ }
+
+ // loop and read/write each class data to/from the RELOAD file
+ classCount = 0;
+ for (stats_class_data = Shared->statData; classCount < STATS_MAX_CLASSES && classCount < numClasses;
+ stats_class_data++, classCount++) {
+
+ STAT_VAR **statPtr;
+
+
+ // -- below section repeated for each class
+ // -- write the binary data in sections, each unique section preceded by byte count that follows
+ // line 6 - "\nEXT CLAS n: 000000000\n" (class number, 0..n
+ // - structure STATS_CLASS_DATA
+
+ sprintf(buff, "CLAS %1d", classCount);
+
+ if (action == STATS_WRITE_RELOAD) {
+
+ if (extsql_write_section_hdr(loadFileName, reloadFile, buff, sizeof(STATS_CLASS_DATA))) {
+ return(1);
+ }
+
+ if (my_write(reloadFile, (byte*) stats_class_data, sizeof(STATS_CLASS_DATA),
+ MYF(MY_WME)) != sizeof(STATS_CLASS_DATA)) {
+ sql_print_error("ExtSQL: Could not %s class %d header data to RELOAD file %s", actionStr,
+ classCount, loadFileName);
+ return(1);
+ }
+
+ } else { // READ
+
+ memPtr = (char *) stats_class_data;
+ if (!extsql_read_section_bytes(loadFileName, reloadFile, &memPtr,
+ buff, classCount)) {
+ return(1);
+ }
+
+ }
+
+ className = stats_class_data->name;
+ maxVar = stats_class_data->maxVar;
+ maxTime = stats_class_data->maxTime;
+ maxInstance = stats_class_data->maxInstance;
+
+
+ // sanity check, make sure the className is on our supported list
+ for (i = 0, ptrPtr=extsqlClassesSupported; i < STATS_MAX_CLASSES; i++, ptrPtr++) {
+
+ found = 0;
+ if (! strcmp(className, *ptrPtr)) {
+ found = 1;
+ break;
+ }
+ } // end for - all supported classes
+
+ if (!found) {
+ sql_print_error("ExtSQL: Bad class name found (%s)", className);
+ return(1);
+ }
+
+ // print out summary info on the CLASS
+ if STATS_DEBUG(STATS_RELOAD) {
+ sql_print_information("ExtSQL: During reload (%s), starting section %s", actionStr, buff);
+ sql_print_information("CLASS is %s, maxInstance(%d), maxVar(%d), maxTime(%d), time(%d), \
+timeUnits(%c), lastTime(%d)",
+ stats_class_data->name, maxInstance, maxVar, maxTime,
+ stats_class_data->time, stats_class_data->timeUnits,
+ stats_class_data->lastTime);
+ }
+
+ // - "\nEXT INSTNC: 3400\n"
+ // - instanceNames 1..n from char **instanceIndex
+
+ if (action == STATS_WRITE_RELOAD) {
+
+ // count the size of what we have
+ size = 0;
+ for (instance = 0; instance < maxInstance && stats_class_data->instanceIndex[instance];
+ instance++) {
+ size += strlen(stats_class_data->instanceIndex[instance]) + 1; // don't forget the NULL!
+ } // end for - all class instances
+
+ if (!(memPtr = (char *)extsql_malloc(size, MYF(MY_WME | MY_ZEROFILL)))) {
+ sql_print_error("ExtSQL: Could not alloc %d bytes to write instance data for class %d",
+ size, classCount);
+ return(1);
+ }
+
+ // write the data to memPtr
+ size = 0;
+ for (instance = 0; instance < maxInstance && stats_class_data->instanceIndex[instance];
+ instance++) {
+ strcpy(memPtr + size, stats_class_data->instanceIndex[instance]);
+
+ if STATS_DEBUG(STATS_RELOAD) sql_print_information("ExtSQL: INSTNC (%d/%s)",
+ instance, memPtr+size);
+
+ size += strlen(stats_class_data->instanceIndex[instance]);
+ size++; // don't forget the NULL!
+
+ } // end for - all class instances
+
+
+ if (extsql_write_section_hdr(loadFileName, reloadFile, "INSTNC", size)) {
+ my_free(memPtr, MYF(0));
+ return(1);
+ }
+
+ if (my_write(reloadFile, (byte*) memPtr, size,
+ MYF(MY_WME)) != size) {
+ sql_print_error("ExtSQL: Could write instance data to RELOAD file %s for %d",
+ loadFileName, classCount);
+ my_free(memPtr, MYF(0));
+ return(1);
+ }
+
+ my_free(memPtr, MYF(0));
+
+ } else { // READ
+
+ memPtr = NULL;
+ if (!(size2 = extsql_read_section_bytes(loadFileName, reloadFile, &memPtr,
+ "INSTNC", classCount))) {
+ return(1);
+ }
+
+ // allocate memory to hold the instanceIndex array!
+ if (!(stats_class_data->instanceIndex = (char **)extsql_malloc( ((maxInstance+1) * sizeof(char *)),
+ MYF(MY_WME | MY_ZEROFILL)) )) {
+ sql_print_error("ExtSQL: Could not alloc space for instanceIndex, RELOAD file %s for %d",
+ loadFileName, classCount);
+ return(1);
+ }
+
+
+ // move the data from buffer to memory, it is a long sequence of null
+ // terminated instance names.
+ // careful, we limit ourselves based not on the max supported, but how many
+ // instances we really had.
+ size = 0;
+ for (instance = 0, srcPtr = memPtr; instance < maxInstance && srcPtr - memPtr < size2; instance++) {
+
+ size = strlen(srcPtr) + 1; // don't forget the NULL
+
+ if (!(destPtr = extsql_malloc(size, MYF(MY_WME | MY_ZEROFILL)))) {
+ sql_print_error("ExtSQL: Memory alloc failed on instance(%d), class(%d)",
+ instance, classCount);
+ my_free(memPtr, MYF(0));
+ return(1);
+ }
+
+ strcpy(destPtr, srcPtr);
+ stats_class_data->instanceIndex[instance] = destPtr;
+ srcPtr += size;
+
+ if STATS_DEBUG(STATS_RELOAD) sql_print_information("ExtSQL: INSTNC (%d/%s)",
+ instance, stats_class_data->instanceIndex[instance]);
+
+ // sql_print_information("ExtSQL: On read, instance(%d) at addr (0x%x), at (%s)", instance, destPtr, destPtr);
+ } // end for - all instance names
+
+ my_free(memPtr, MYF(0));
+
+ }
+
+ if STATS_DEBUG(STATS_RELOAD) {
+ sql_print_information("ExtSQL: during %s, completed section INSTNC, class (%d)",
+ actionStr, classCount);
+ }
+
+ // - "\nEXT VAR___: 2343\n"
+ // - structure STAT_VAR from STAT_VAR **varIndex
+
+ if (action == STATS_WRITE_RELOAD) {
+
+ // count the size of what we have, this is an array of pointers to structures, store
+ // the addr and the string
+ statPtr = stats_class_data->varIndex;
+ size = 0;
+ for (var = 0; var < maxVar; var++, statPtr++) {
+ size += sizeof(char *) + strlen((*statPtr)->name) + 1;
+ }
+
+ if (!(memPtr = (char *)extsql_malloc(size, MYF(MY_WME | MY_ZEROFILL)))) {
+ sql_print_error("ExtSQL: Could not alloc %d bytes to write varIndex data for class %d",
+ size, classCount);
+ return(1);
+ }
+
+ // write the data to buffPtr (address, followed by null term name)
+ statPtr = stats_class_data->varIndex;
+ size = 0;
+ for (var = 0; var < maxVar; var++, statPtr++) {
+ ptrPtr = (char **)(memPtr+size);
+ *ptrPtr = (*statPtr)->addr; // ZZ - var addr may not match on restart?
+ size += sizeof (char *);
+ strcpy(memPtr+size, (*statPtr)->name);
+ size += strlen((*statPtr)->name);
+ if STATS_DEBUG(STATS_RELOAD) sql_print_information("ExtSQL: VAR (%d / %s / 0x%x)",
+ var, (*statPtr)->name, (uint)(*statPtr)->addr);
+ *(memPtr+size++) = '\0'; // don't forget the NULL
+ }
+
+ if (extsql_write_section_hdr(loadFileName, reloadFile, "VAR___", size)) {
+ my_free(memPtr, MYF(0));
+ return(1);
+ }
+
+
+ if (my_write(reloadFile, (byte*) memPtr, size,
+ MYF(MY_WME)) != size) {
+ sql_print_error("ExtSQL: Could write varIndexdata to RELOAD file %s for %d",
+ loadFileName, classCount);
+ my_free(memPtr, MYF(0));
+ return(1);
+ }
+
+ my_free(memPtr, MYF(0));
+
+ } else { // READ
+
+ memPtr = NULL;
+ if (!(size2 = extsql_read_section_bytes(loadFileName, reloadFile, &memPtr,
+ "VAR___", classCount))) {
+ return(1);
+ }
+
+ // alloc memory for the complete var index of pointers
+ if (!(stats_class_data->varIndex = (STAT_VAR **)extsql_malloc( ((maxVar+1) * sizeof(pSTAT_VAR)),
+ MYF(MY_WME | MY_ZEROFILL)) )) {
+ sql_print_error("ExtSQL: Could not alloc space for varIndex, RELOAD file %s for %d",
+ loadFileName, classCount);
+ return(1);
+ }
+
+
+ // move the data from buff to memory, careful that we don't exceed what
+ // we actually read in.
+ statPtr = stats_class_data->varIndex;
+ size = 0;
+ for (var = 0, srcPtr = memPtr; srcPtr - memPtr < size2; var++, statPtr++) {
+
+ // allocate room to the STAT_VAR structure we will point to and that will hold
+ // the name and addr
+ if (!(*statPtr = (STAT_VAR *)extsql_malloc( (sizeof(STAT_VAR)),
+ MYF(MY_WME | MY_ZEROFILL)) )) {
+ sql_print_error("ExtSQL: Could not alloc space for STAT_VAR structure, RELOAD file %s for %d",
+ loadFileName, classCount);
+ return(1);
+ }
+
+ ptrPtr = (char **)srcPtr;
+ (*statPtr)->addr = *ptrPtr;
+
+ // move to name
+ size = sizeof (char *);
+ srcPtr += size; // start of name
+ size = strlen(srcPtr) + 1; // length
+
+ if (!(destPtr = extsql_malloc(size, MYF(MY_WME | MY_ZEROFILL)))) {
+ sql_print_error("ExtSQL: Memory alloc failed on var(%d), class(%d)",
+ var, classCount);
+ my_free(memPtr, MYF(0));
+ return(1);
+ }
+
+ strcpy(destPtr, srcPtr); // copy name to allocated memory
+ (*statPtr)->name = destPtr; // set pointer
+
+ // need to store the address in our quick lookup array, it would have been cleared
+ // prior to the reload
+ if (extsql_store_var_addr((*statPtr)->addr )) {
+ return(1);
+ }
+
+ if STATS_DEBUG(STATS_RELOAD) sql_print_information("ExtSQL: VAR (%d / %s / 0x%x)",
+ var, (*statPtr)->name, (uint)(*statPtr)->addr);
+
+ srcPtr += size; // move to next addr/name pair
+
+ } // end for - all vars
+
+ my_free(memPtr, MYF(0));
+
+ } // end if - else
+
+ if STATS_DEBUG(STATS_RELOAD) {
+ sql_print_information("ExtSQL: during %s, completed section VAR___, class (%d)",
+ actionStr, classCount);
+ }
+
+ // - "\nEXT TIME: 2123\n"
+ // - my_time_t array of time entries for each data entry from my_time_t *timeLabels
+
+
+ if (action == STATS_WRITE_RELOAD) {
+
+ // count the size of what we have, this is an array of time labels,
+ // just items of my_time_t all in a row
+ size = sizeof(my_time_t) * maxTime;
+
+ if (extsql_write_section_hdr(loadFileName, reloadFile, "TIME__", size)) {
+ return(1);
+ }
+
+ if STATS_DEBUG(STATS_RELOAD) {
+
+ strcpy(buff, "ExtSQL: TIME ");
+
+ // only the first 10!
+ for (i = 0; i < maxTime && i < 10; i++) {
+ sprintf(buff+strlen(buff), "(%d / %d)", i, (int)stats_class_data->timeLabels[i]);
+ }
+
+ sql_print_information(buff);
+ }
+
+ if (my_write(reloadFile, (byte*) stats_class_data->timeLabels, size,
+ MYF(MY_WME)) != size) {
+ sql_print_error("ExtSQL: Could write timeLabels data to RELOAD file %s for %d",
+ loadFileName, classCount);
+ return(1);
+ }
+
+ // just to satisfy compiler that extsql_print_bytes could be used!
+ if (!strcmp(loadFileName, "bongongongog")) {
+ extsql_print_bytes((char *)stats_class_data->timeLabels, size);
+ }
+
+ } else { // READ
+
+ // this one is easy, just read it right in, space has already been allocated!
+ memPtr = NULL;
+ if (!extsql_read_section_bytes(loadFileName, reloadFile, &memPtr,
+ "TIME__", classCount)) {
+ return(1);
+ }
+
+ // DON'T FREE memPtr here
+ stats_class_data->timeLabels = (my_time_t *) memPtr;
+
+ if STATS_DEBUG(STATS_RELOAD) {
+
+ strcpy(buff, "ExtSQL: TIME ");
+
+ // only the first 10!
+ for (i = 0; i < maxTime && i < 10; i++) {
+ sprintf(buff+strlen(buff), "(%d / %d)", i, (int)stats_class_data->timeLabels[i]);
+ }
+
+ sql_print_information(buff);
+ }
+
+ } // end if - else
+
+ if STATS_DEBUG(STATS_RELOAD) {
+ sql_print_information("ExtSQL: during %s, completed section TIME__, class (%d)",
+ actionStr, classCount);
+ }
+
+ // - "\nEXT DATA__: 000342432\n"
+ // - ulong data (actual recorded data) from ulong *data
+
+ // get the size of the data section
+ size = stats_class_data->maxInstance * stats_class_data->maxVar *
+ stats_class_data->maxTime * sizeof(ulong);
+
+ if (action == STATS_WRITE_RELOAD) {
+
+
+ // sanity check, size should be same as diff, remember offset in dataEnd
+ // is as an index to ulong entries, size is in bytes!
+ size2 = (stats_class_data->dataEnd - stats_class_data->data) * sizeof(ulong);
+ if (size != size2) {
+ sql_print_error("ExtSQL: data size error (%d/%d) for class %d",
+ size, size2, classCount);
+ return(1);
+ }
+
+ if (extsql_write_section_hdr(loadFileName, reloadFile, "DATA__", size)) {
+ return(1);
+ }
+
+ if (my_write(reloadFile, (byte*) stats_class_data->data, size,
+ MYF(MY_WME)) != size) {
+ sql_print_error("ExtSQL: Could write ext data to RELOAD file %s for %d",
+ loadFileName, classCount);
+ return(1);
+ }
+
+ } else { // READ
+
+ // this one is easy, just read it right in!
+ memPtr = NULL;
+ if (!(size2 = extsql_read_section_bytes(loadFileName, reloadFile, &memPtr,
+ "DATA__", classCount)) ) {
+ return(1);
+ }
+
+ // sanity check, same size?
+ if (size != size2) {
+ sql_print_error("ExtSQL: data size error (%d/%d) for class %d",
+ size, size2, classCount);
+
+ my_free(memPtr, MYF(0));
+ return(1);
+ }
+
+ // DON'T FREE memPtr here
+ stats_class_data->data = (ulong *) memPtr;
+
+ // set dataEnd
+ stats_class_data->dataEnd = stats_class_data->data + size/sizeof(ulong);
+
+ } // end if -else
+
+
+ if STATS_DEBUG(STATS_RELOAD) {
+ sql_print_information("ExtSQL: during %s, completed section DATA__, class (%d)",
+ actionStr, classCount);
+ }
+ // - "\nEXT CLASS n: 2\n" (repeat part for each class)
+
+ } // end for - all classes
+
+ return(0);
+
+} // end extsql_write_reload
+
+// extsql_store_var_addr - stores just the address of a tracked var in a single array
+// this makes increment processing much quicker on the lookup to see if we are
+// tracking a variable
+static int extsql_store_var_addr(char *varAddr) {
+
+ int k, varAddrFound;
+
+ for (k = 0, varAddrFound = 0; k < Shared->varAddrCount; k++) {
+
+ if (Shared->varAddrTrackArray[k] == varAddr) { // it's there
+ varAddrFound = 1;
+ break;
+ }
+ } // end for - existing array values
+
+ if (!varAddrFound && k < MAX_VARS) { // not there and not at end, add it
+ Shared->varAddrTrackArray[k] = varAddr;
+ Shared->varAddrCount++;
+
+ } else if (!varAddrFound && k >= MAX_VARS) {
+ sql_print_error("ExtSQL: disabled, var tracking limit of %d exceeded",
+ MAX_VARS);
+ return(1);
+ }
+
+ return(0);
+
+} // end extsql_store_var_addr
+
+
+// print out all the internal data structures
+static void extsql_stats_dump() {
+
+ pSTATS_CLASS_DATA stats_class_data;
+ STAT_VAR **statPtr;
+ int i, time, maxVar, maxTime, maxInstance, var, value, instance, classCount;
+ char buff[STATS_TMP_BUFF_SIZE];
+
+
+ // DEBUG LOG TO FILE
+ // general info
+ sql_print_information("ExtSQL: DUMP START (%d)", (int)StartTime);
+ sql_print_information(" version (%s)", Shared->extsql_version+17); // don't reprint version
+ sql_print_information(" extsql_class_string (%s)", Shared->extsql_class_list);
+ sql_print_information(" extsql_users (%s)", extsql_users);
+
+
+ // varAddrTrackArray
+ sprintf(buff," varAddrTrackArray (%d): ", Shared->varAddrCount);
+
+ for (i = 0; i < MAX_VARS && i < 40 && i < Shared->varAddrCount; i++) {
+ sprintf(buff + strlen(buff),"(0x%x) ", (uint) Shared->varAddrTrackArray[i]);
+ }
+
+ sql_print_information(buff);
+
+
+ // loop and print everything to the log file
+ classCount = 0;
+ for (stats_class_data = Shared->statData; classCount < STATS_MAX_CLASSES && stats_class_data->maxInstance;
+ stats_class_data++, classCount++) {
+
+ maxVar = stats_class_data->maxVar;
+ maxTime = stats_class_data->maxTime;
+ maxInstance = stats_class_data->maxInstance;
+
+
+ // print out summary info on the CLASS
+ sql_print_information("CLASS is %s, maxInstance(%d), maxVar(%d), maxTime(%d), time(%d), \
+timeUnits(%c), lastTime(%d)",
+ stats_class_data->name, maxInstance, maxVar, maxTime,
+ stats_class_data->time, stats_class_data->timeUnits,
+ stats_class_data->lastTime);
+
+ sql_print_information(" Base of instanceIndex array at 0x%x", (uint)stats_class_data->instanceIndex);
+
+ for (instance=0;
+ instance < maxInstance && stats_class_data->instanceIndex[instance];
+ instance++) {
+ sql_print_information(" instanceIndex[%d] = (%s), addr(0x%x)",
+ instance, stats_class_data->instanceIndex[instance],
+ (uint) stats_class_data->instanceIndex[instance]);
+ } // end for -- all instances
+
+ for (var=0, statPtr = stats_class_data->varIndex;
+ (var < stats_class_data->maxVar) && *statPtr ;
+ var++, statPtr++) {
+ sql_print_information(" varIndex[0x%x] = name(%s), addr(0x%x)",
+ var, (*statPtr)->name, (uint)(*statPtr)->addr);
+
+ } // end for - all vars
+
+ for (instance=0; instance < maxInstance; instance++) {
+ for (var=0, statPtr = stats_class_data->varIndex;
+ var < stats_class_data->maxVar && *statPtr;
+ var++, statPtr++) {
+ for (time = 0; time < maxTime; time++) {
+
+ value = stats_class_data->data [ARRAY_INDEX(instance, var, maxVar,
+ time, maxTime)];
+ if (value) {
+ sql_print_information(" statData[%d-%s][%d-%s][%d](%d) = %d",
+ instance, stats_class_data->instanceIndex[instance],
+ var, (*statPtr)->name,
+ time, (int)stats_class_data->timeLabels[time],
+ value);
+
+ } // end if - value
+ } // end for - all times
+ } // end for - all variables
+ } // end for - all instances
+
+ } // end for - log file write all classes
+ sql_print_information("ExtSQL: DUMP END ");
+
+} // end extsql_stats_dump
+
+
+
+// dump/check thread info
+// if doDump is set, then go ahead and dump all info on all threads;
+// otherwise just create output if there is an error.
+static void extsql_thread_dump(THD *thd, int doDump)
+{
+
+#ifndef PGSQL
+ if (pthread_mutex_trylock(&LOCK_thread_count)) { // forget about it!
+ return;
+ }; // For unlink from list
+#endif
+
+#ifdef PGSQL
+
+ // NEED this ZZ
+#else
+
+ I_List_iterator it(threads);
+ THD *tmp;
+ pthread_t my_id = pthread_self();
+ int foundError = 0;
+ char *user, *host;
+ int *statVerify, *indexPtr;
+
+ if (doDump) {
+ sql_print_information("ExtSQL: this thread id is %d", thd->thread_id);
+ }
+
+
+
+ while ((tmp=it++))
+ {
+#ifdef EXTSQL_50
+ user = tmp->security_ctx->user;
+ host = tmp->security_ctx->host;
+ statVerify = &tmp->security_ctx->statVerify;
+ indexPtr = tmp->security_ctx->statIndex;
+#else
+ user = tmp->user;
+ host = tmp->host;
+ statVerify = &tmp->statVerify;
+ indexPtr = tmp->statIndex;
+#endif
+
+ if (doDump) {
+ sql_print_information("ExtSQL: thd(%d/%s/%s) user(%s) host(%s) db(%s) magic(0x%x)",
+ tmp->thread_id, tmp->proc_info, command_name[tmp->command], user,
+ host, tmp->db, *statVerify);
+ }
+
+ for (int i = 0; i < STATS_MAX_CLASSES; i++) {
+ if (indexPtr[i]) {
+
+ if ( (*statVerify == STATS_MAGIC_NUM) &&
+ ( (indexPtr[i] >= Shared->statData[i].maxInstance) || indexPtr[i] < 0) ) {
+ sql_print_warning("ExtSQL: special check failed: thd(%d/%s/%s) user(%s) host(%s) db(%s) magic(0x%x)",
+ tmp->thread_id, tmp->proc_info, command_name[tmp->command], user,
+ host, tmp->db, *statVerify);
+ sql_print_warning(" index(%d), value(0x%x/0x%x)",
+ i, indexPtr[i], Shared->statData[i].maxInstance);
+ }
+
+ if (doDump) {
+ sql_print_information(" index(%d), value(0x%x/0x%x)",
+ i, indexPtr[i], Shared->statData[i].maxInstance);
+ } // end if - dump value
+
+ } // end if - has value
+ } // end for - all entries in this thread
+ } // end while - still more threads
+
+#endif
+
+#ifndef PGSQL
+ VOID(pthread_mutex_unlock(&LOCK_thread_count));
+#endif
+
+} // end extsql_thread_dump
+
+
+
+// a new thread has been created, init the stat tracking structures
+// if (initType == STATS_THD_INIT_DB) is set, the thread exists, but a 'use' command was given to change DB's
+// we need to update the db index ONLY!
+// if initType == STATS_THD_INIT_ALL -- we reset everybody no matter what!
+int extsql_thread_init(THD *thd, int initType)
+
+{
+ int i, threadID, command;
+ char *tmpName = "", classStart;
+ int classCount = 0;
+ int found = 0;
+ pSTATS_CLASS_DATA stats_class_data;
+ char *user = "", *host = "", *db = "", *proc_info = "", **instPtr;
+ int *statVerify = 0, *indexPtr = 0;
+ char tmpBuff[STATS_TMP_BUFF_SIZE];
+
+
+ if (initType != STATS_THD_INIT_ALL) { // normal
+
+ if (!Shared->extsql_active || Shared->extsql_disabled) { // shouldn't normally even get here
+ return(0);
+ }
+
+ if (initType == STATS_THD_INIT_DB) { // ZZ -return
+ // return(0);
+ }
+ }
+
+ // attach to our shared memory!
+ if (extsql_init_globals()) {
+ return(1);
+ }
+
+ if STATS_DEBUG(STATS_DUMP_START) {
+ extsql_stats_dump();
+ }
+
+ StartTime = time(NULL);
+
+ extsql_init_proc_vars(&threadID, &command, &user, &db, &host, &proc_info, &indexPtr, &statVerify);
+
+
+ // OKAY, if (initType == STATS_THD_INIT_DB) is set we should already be tracking data on this thread, just
+ // need to change the db index - SO, we should have already done an init on this
+ // thread and the MAGIC should have been set, if not, get out!
+ if (initType == STATS_THD_INIT_DB) {
+ if (*statVerify != STATS_MAGIC_NUM) {
+ return(1);
+ }
+ }
+
+ // check that we have a host and db
+ if (! host || ! *host || *host == '\0') {
+ host = "(null)";
+ }
+
+ if (! db || ! *db || *db == '\0') {
+ db = "(null)";
+ }
+
+ // we don't do thread zero -- ZZ disabled temp
+ if (0 && ! threadID) {
+ sql_print_warning("ExtSQL: thread_init called for thread zero in error");
+ return(0);
+ }
+
+ // for root stuff, i.e. phpMyAdmin, the db is (null).
+ if STATS_DEBUG(STATS_THD_INIT_PARAMS) {
+ sql_print_information("ExtSQL: thread (%d) started for db %s(0x%x), user %s(0x%x), host %s(0x%x)",
+ threadID, db, (uint)db, user, (uint)user, host, (uint)host);
+ }
+
+ // for quick indexing on increment, we store the index values used to track the instances
+ // used by this thread within the thread itself
+ // see if user and db names are there already
+
+ // check/add indexes depending on classes that we are collecting for;
+
+ // init the class index array to zero if this is a first time init or for ALL
+ if (!(initType == STATS_THD_INIT_DB)) {
+ for (i = 0; i< STATS_MAX_CLASSES; i++) {
+ indexPtr[i] = 0;
+ }
+ *statVerify = 0; // will be set below when we find match on anything.
+ }
+
+ for (stats_class_data = Shared->statData, classCount = 0;
+ classCount < STATS_MAX_CLASSES && stats_class_data->maxInstance;
+ stats_class_data++, classCount++) {
+
+ classStart = stats_class_data->name[0];
+
+ if ((initType == STATS_THD_INIT_DB) && classStart != 'd' && classStart != 'c') { // we just want the db or condb
+ continue;
+ }
+
+ switch (classStart) { // LIST should match extsqlClassesSupported[]
+
+ case 'c': // connect
+
+ if ((initType == STATS_THD_INIT_DB) && stats_class_data->name[3] != 'd') { // not condb
+ continue;
+ }
+
+ if (stats_class_data->name[3] == 'd') { // condb - db@host
+
+ strcpy(tmpBuff, db);
+
+ } else if (stats_class_data->name[3] == 'u') { // conuser- user@host
+ strcpy(tmpBuff, user);
+
+ } else {
+
+ sql_print_error("ExtSQL: disabled, unexpected connection class name %s",
+ stats_class_data->name);
+ Shared->extsql_disabled = 1;
+ break;
+ }
+
+ strcat(tmpBuff, "@");
+ strcat(tmpBuff, host);
+ tmpName = tmpBuff;
+
+ break;
+
+ case 'd': // db
+
+ tmpName = db;
+ break;
+
+ case 'h': // host
+
+ tmpName = host;
+ break;
+
+ case 's': // server -- cum total of all on this server
+
+ tmpName = "server";
+ break;
+
+ case 'u': // user
+
+ tmpName = user;
+ break;
+
+ default:
+
+ sql_print_error("ExtSQL: disabled, unexpected class name %s",
+ stats_class_data->name);
+ Shared->extsql_disabled = 1;
+
+ } // end switch
+
+ // make sure we got something in tmpName
+ if (!tmpName) {
+ // sql_print_warning("ExtSQL: got null tmpName instance value for class %s, forcing to (null)", stats_class_data->name);
+ tmpName = "(null)";
+ }
+
+ if (Shared->extsql_disabled) {
+ break;
+ }
+
+ // now try to match or insert
+ // check for new or existing instance, LOCK statData during the search
+ found = 0;
+
+ i = 0;
+ for (instPtr = (char **)stats_class_data->instanceIndex;
+ i < stats_class_data->maxInstance; instPtr++, i++) {
+
+ if STATS_DEBUG(STATS_THD_INIT_LOOP) {
+ sql_print_information(" Looking for match of %s with %s", *instPtr, tmpName);
+ }
+
+ if (!*instPtr) { // at a blank, fill it
+
+ // Grab the lock now, check to see if it is still NULL
+ // if it is, fill it in
+ (void) pthread_mutex_lock(&Shared->LOCK_stats_load); // IMPORTANT, don't leave with unlock!
+
+ if (!*instPtr) { // at a blank, fill it
+ if (!(*instPtr = extsql_strdup(tmpName, MYF(MY_WME)))) {
+ Shared->extsql_disabled = 1;
+ sql_print_error("ExtSQL: disabled, unable to alloc instance item %s",
+ tmpName);
+ // UNLOCK statData
+ (void) pthread_mutex_unlock(&Shared->LOCK_stats_load); // IMPORTANT!
+ break;
+ }
+ found = 1;
+
+ Shared->statsAllocArray[Shared->allocIndex] = *instPtr;
+
+ // UNLOCK statData
+ (void) pthread_mutex_unlock(&Shared->LOCK_stats_load); // IMPORTANT!
+
+ indexPtr[classCount] = i;
+
+ if STATS_DEBUG(STATS_THD_INIT_LOOP) {
+ sql_print_information("ExtSQL, NEW statIndex[%d]->%d for %s in class %s stored as (%s)",
+ classCount, i, tmpName, stats_class_data->name,
+ Shared->statsAllocArray[Shared->allocIndex]);
+ }
+
+ Shared->allocIndex++;
+ break;
+
+ } // end if - was emtpy - fill in new values
+
+ // if we get here it is not null, someone else got it ahead of us. May have filled in another value,
+ // or what we want. -- we release lock, we will fall throught to below and check for a match, and keep looping
+ // UNLOCK statData
+ (void) pthread_mutex_unlock(&Shared->LOCK_stats_load); // IMPORTANT!
+
+ } // end if - was empty - fill in new value
+
+ if (!strcmp(*instPtr, tmpName)) { // match
+ indexPtr[classCount] = i;
+ found = 1;
+ break;
+ }
+
+ } // end for - each instance
+
+
+ // check for max instances exceeded
+ if (! found) {
+ sql_print_warning("ExtSQL: disabled, instances exceed tracking limit for class %s with %s",
+ stats_class_data->name, tmpName);
+ Shared->extsql_disabled = 1;
+ break;
+ }
+
+ // if we get here, something was found.
+ *statVerify = STATS_MAGIC_NUM;
+
+ if (Shared->extsql_disabled) {
+ break;
+ }
+
+ } // end for - each class
+ if STATS_DEBUG(STATS_DUMP_START) {
+ extsql_stats_dump();
+ }
+
+#ifdef PGSQL
+ extsql_inc(&Connections, 1);
+#endif
+
+ return(0);
+
+} // end extsql_thread_init
+
+#ifdef EXTSQL_50
+// only for information schema, in 5 and above
+
+extern bool schema_table_store_record(THD *thd, TABLE *table);
+
+// We fill the table with our tracking data, depending on the name
+int fill_schema_extsql(THD *thd, TABLE_LIST *tables, COND *cond)
+{
+
+ const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS;
+ TABLE *table= tables->table;
+ CHARSET_INFO *scs= system_charset_info;
+
+ pSTATS_CLASS_DATA stats_class_data;
+ STAT_VAR **statPtr;
+ ST_SCHEMA_TABLE *schema_table= tables->schema_table;
+ int found, var, maxVar, maxTime, maxInstance, currentTime, timeLimit, timeCount, indexTime,
+ varNameLength, varIndex, value, stats_hourly, fieldIndex=0, rowHasData, doZeroTime=1;
+ char listBuff[MAX_BUFF_SIZE], className[STATS_MAX_NAME];
+ char *varListPtr = listBuff, *stats_class = className, *varName, *termChar;
+ MYSQL_TIME now_mysql_time;
+ char *user;
+
+ DBUG_ENTER("schema_table_store_record");
+
+ // are they authorized
+#ifdef EXTSQL_50
+ user = thd->security_ctx->user;
+#else
+ user = thd->user;
+#endif
+
+ // return if not authorized, disabled or inactive
+ if (extsql_check(thd, 0)) DBUG_RETURN(1);
+
+ // lets make sure this is one of our tables
+ if (!strncmp(schema_table->table_name, "EXTSTATS_", 9)) { // one of ours
+
+ int found, var, maxVar;
+
+ // lets get the class name off the end
+ strcpy(stats_class, schema_table->table_name + 9);
+
+ // find the data for the Class we are interested in
+ found = 0;
+ for (stats_class_data = Shared->statData; stats_class_data->maxInstance;
+ stats_class_data++) {
+ if (!strcmp(stats_class_data->name, stats_class)) { // found it
+ found = 1;
+ break;
+ }
+ } // end for
+
+ if (!found) {
+ net_printf_error(thd, 0, "ExtSQL: can't fill requested SCHEMA TABLE %s(%s). The \
+most likely reason is that no data is being recorded for that class. Use SHOW STATISTICS to see \
+what is being tracked.",
+
+ schema_table->table_name, stats_class);
+ DBUG_RETURN(0);
+ }
+
+ } else { // called by mistake
+ net_printf_error(thd, 0, "ExtSQL: schema called in error to fill table %s.",
+ schema_table->table_name);
+ DBUG_RETURN(0);
+ }
+
+ // AT THIS POINT stats_class_data points to the requested class
+ maxVar = stats_class_data->maxVar;
+ maxTime = stats_class_data->maxTime;
+ maxInstance = stats_class_data->maxInstance; // needed for array indexing
+
+ currentTime = stats_class_data->time; // time we are currently logging to.
+ timeLimit = maxTime-1; // they get it all
+
+ // we need to now load the columns in the PROPER order, data is recorded as it
+ // occurs, but the Var name order is controlled by the user, we try to use
+ // that order.
+ strcpy(varListPtr,","); // init the list
+ // loop through the instance variables for names
+ for (var=0, statPtr = stats_class_data->varIndex;
+ (var < stats_class_data->maxVar) && *statPtr ;
+ var++, statPtr++) {
+
+ strcat(varListPtr,(*statPtr)->name );
+ strcat(varListPtr, ",");
+
+ if (strlen(varListPtr) > MAX_BUFF_SIZE - 50) {
+ net_printf(thd, 0, "ExtSQL: List of Vars too long");
+ DBUG_RETURN(1);
+ }
+ } // end for - all instances
+
+
+ // Remember, zero index holds the total. The
+ // currentTime has our present location and it is circular, i.e. if
+ // maxTime for the class is 4, we store current + 3. If the currentTime
+ // is 2, the proper way to display all hours, starting with the most
+ // recent is to show 2(current),1(prior),3(more prior) - skip 0
+ // we now go through all the instances, we will be loading the table
+ // with instance_name, time, var, var, .....
+ for (int instance=0; instance < maxInstance && stats_class_data->instanceIndex[instance] ;
+ instance++) {
+
+ // loop through the times,
+ // if stats_timely is set we will output a number of hours starting at currentTime, and
+ // limited by timeLimit (starts at 1).
+ // For INFORMATION_SCHEMA we have a real problem, need to print the totals under
+ // time heading of 0000-00-00 00:00 (zero time), but then pick up and print the rest,
+ // have to really hack the loop control to make first output special, zero time.
+ // If doZeroTime = 1 -- we need to output the zero values(totals)
+ doZeroTime = 1;
+
+ for (timeCount = 0, indexTime = currentTime ;
+ timeCount < timeLimit && indexTime >= 0;
+ timeCount++, indexTime-- ) {
+
+ // we have to adjust our indexTime for wrap
+ if (!indexTime && !doZeroTime) { // we have gone back to the total
+ indexTime = maxTime - 1; // pick the last
+ }
+
+ restore_record(table, s->default_values);
+ fieldIndex = 0;
+ rowHasData = 0; // do we have anything to show, only on HISTORY
+
+ // first column - instance name - always
+ table->field[fieldIndex++]->store(stats_class_data->instanceIndex[instance],
+ strlen(stats_class_data->instanceIndex[instance]), scs);
+
+ // second column is times - always, we use indexTime
+ if (indexTime) {
+ thd->variables.time_zone->gmt_sec_to_TIME(&now_mysql_time,
+ (my_time_t) stats_class_data->timeLabels[indexTime]);
+ } else {
+ char *zeroTime = "0000-00-00 00:00:00";
+ str_to_time_with_warn(zeroTime, strlen(zeroTime), &now_mysql_time);
+ }
+
+ table->field[fieldIndex++]->store_time(&now_mysql_time, MYSQL_TIMESTAMP_DATETIME);
+
+ // loop through varListPtr, output the vars in the DEFAULT order
+ // varListPtr = ",bytes,selects," varName will move through this
+ for ( varName = varListPtr; *varName ; varName = strstr(varName, ",")) {
+
+ varName++; // move off the comma
+ //sql_print_information(" target varName is (%s)", varName);
+
+ if (! *varName) { // we are done
+ break;
+ }
+
+ varNameLength = strchr(varName, ',') - varName;
+
+ // okay, varName points at a name, lets go through the index looking
+ // for a match
+ found = 0;
+ for (varIndex=0, statPtr = stats_class_data->varIndex;
+ varIndex < maxVar && *statPtr;
+ varIndex++, statPtr++) {
+
+ termChar = (*statPtr)->name + varNameLength +1; // point to end of the current var name
+
+ if (!strncmp( (*statPtr)->name, varName, varNameLength)) {
+ found = 1;
+ value = stats_class_data->data [ARRAY_INDEX(instance, varIndex, maxVar,
+ indexTime, maxTime)];
+
+ table->field[fieldIndex++]->store((longlong) value, TRUE);
+ if (value) { // is it non-zero
+ rowHasData = 1;
+ }
+ break;
+ }
+
+ } // end for - a matching var in the list
+
+ if (!found) {
+ table->field[fieldIndex++]->store((longlong) 9999999, TRUE);
+ }
+
+ } // end for - requested values
+
+ if (!indexTime) { // it had hit zero and we have outputed totals
+ doZeroTime = 0; // don't do it again for this class
+ indexTime = 1; // we want it to zero again for wrap.
+ timeCount--; // this row doesn't count
+ }
+
+ if (!rowHasData) {
+ continue;
+ }
+
+ // record the row
+ if (schema_table_store_record(thd, table)) {
+ sql_print_error("ExtSQL, protocol write failed");
+ DBUG_RETURN(1);
+ }
+ } // end for - all requested times
+
+ } // end for - all instances
+
+ return 0;
+}
+
+#endif
--- /export/mysql/extsql-build/builds/8.4.1/rhel4/x86/1/0.0/src/include/utils/extsql.h 1969-12-31 19:00:00.000000000 -0500
+++ src/include/utils/extsql.h 2009-11-29 17:01:47.000000000 -0500
@@ -0,0 +1,217 @@
+
+/* Copyright (C) 2006, 2007, 2008, 2009, 2010 Software Workshop Incorporated
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef EXTSQL_H
+#define EXTSQL_H 1
+#endif
+
+#define PGSQL 1
+
+#ifdef PGSQL
+#define PGSQL_8 1 // only for 8.4 builds and above.
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "postgres.h"
+#include "libpq/pqcomm.h"
+#include "miscadmin.h"
+#include "catalog/pg_type.h"
+#include "tcop/dest.h"
+#include "executor/executor.h"
+#include "storage/proc.h"
+#include "storage/backendid.h"
+#include "libpq/libpq-be.h"
+#include "libpq/hba.h"
+#include "storage/pg_shmem.h" // ZZ shared memory
+#include "storage/spin.h"
+#include "storage/fd.h"
+#include "nodes/pg_list.h"
+
+// need this stuff for PGSQL, mimics where stored in mysql (mysqld.cc)
+// by default we are set for MySQL 4.x, PGSQL set for any PGSQL build where
+// MySQL stuff will NOT apply
+
+// NOTE variadic macros below, invoke with either '...' or '__VA_ARGS__' compiler dependent!
+#define sql_print_information(str, ...) elog(LOG, str, ##__VA_ARGS__)
+#define sql_print_warning(str, ...) elog(LOG, str, ##__VA_ARGS__)
+#define sql_print_error(str, ...) elog(LOG, str, ##__VA_ARGS__)
+
+#define net_printf(thd, x, str, ...) elog(ERROR, str, ##__VA_ARGS__)
+
+#define DBUG_RETURN(x) return(x)
+#define DBUG_ENTER(x)
+
+#define NullS (char *) 0
+
+#define MY_MUTEX_INIT_FAST &my_fast_mutexattr
+
+#define pthread_mutex_trylock(lock) pthread_mutex_lock(lock)
+#define my_free(x, y) free(x)
+#define my_malloc(a,b) malloc(a)
+#define my_open(fileName, mode, flag) open(fileName, mode)
+#define my_read(fd, buff, bytes, flags) read(fd, buff, bytes)
+#define my_write(fd, buff, bytes, flags) write(fd, buff, bytes)
+#define MY_WME 1
+#define MY_NABP 1
+#define MY_ZEROFILL 1
+#define MYF(a) a
+#define O_BINARY 0 // ZZ
+
+
+#define FN_REFLEN 255
+
+
+typedef char byte;
+typedef time_t my_time_t;
+typedef time_t MYSQL_TIME;
+typedef struct PGPROC THD;
+typedef struct show_var_st {
+ char *name;
+ ulong *value;
+} SHOW_VAR;
+
+extern ulong Com_begin, Com_commit, Com_create_table, Com_delete,
+ Com_drop_table, Com_insert, Com_rollback, Com_select, Com_update, Connections, Questions;
+extern int mysql_show_extsql(THD *thd, const char *stats_class, DestReceiver *dest, List *stats_vars,
+ uint stats_hourly, const char *wild, char *stats_order_var,
+ const uint stats_order_dir, const uint stats_order_limit, char *stats_where_var,
+ const uint stats_where_num, const uint stats_where_char);
+
+#endif // PGSQL
+
+
+
+#define STATS_DEBUG(Val) (Val & extsql_debug ? 1 : 0)
+
+#define STATS_MAGIC_NUM 0x12345678
+#define STATS_MAX_NAME 20 // max size of a Class name
+#define STATS_TMP_BUFF_SIZE 2048
+
+#define STATS_INIT_FAILURE 0 // define the start types
+#define STATS_INIT_NORMAL 1
+#define STATS_INIT_RESET 2
+#define STATS_INIT_RELOAD 3
+#define STATS_INIT_CONFIRM 4
+
+#define STATS_THD_INIT_NORM 5
+#define STATS_THD_INIT_DB 6
+#define STATS_THD_INIT_ALL 7
+
+#define STATS_WRITE_RELOAD 1 // actions taken with reload file
+#define STATS_READ_RELOAD 2
+#define STATS_SECTION_HDR 23 // characters in reload file section hdrs
+#define STATS_SECTION_BYTES 13 // offset to first digit in byte count
+
+#define STATS_MAX_NUM_ALLOC 200 // max independent mem allocs we track
+#define MAX_VARS 50 // the most unique Vars we can track
+
+// #define EXTSQL_50 1 -- external define set during build during configure.
+// #define EXTSQL_BINARY 1 -- external define set during build during configure.
+
+typedef struct stat_vars {
+ char *name;
+ char *addr;
+} STAT_VAR, *pSTAT_VAR;
+
+typedef struct stats_class_data { // keep this padded to 4 byte borders
+ char name[STATS_MAX_NAME]; // class name
+ ulong *data; // pointer to data area for that class
+ // 3-d array [instance index][var index][hour index, 0-summary]
+ ulong *dataEnd; // where does data end - limit check
+ int maxInstance;
+ char **instanceIndex; // indexes for named instances
+ int maxVar;
+ STAT_VAR **varIndex; // indexes for vars, name & addr
+ int maxTime;
+ char *varList; // NOT USED ZZ
+ int time; // the current time unit we are logging to
+ char timeUnits; // d - day, h - hour, m - minute
+ char padd[3];
+ my_time_t *timeLabels; // absolute time for each time entry in data
+ int lastTime; // the last clock time we saw for data logged
+} STATS_CLASS_DATA, *pSTATS_CLASS_DATA;
+
+
+// SHARED MEM -- all our global data
+typedef struct global_alloc { // in same order as declared in extsql.cc
+ STATS_CLASS_DATA statData[STATS_MAX_CLASSES];
+ char *extsql_class_list, *extsql_reload_file;
+
+ char *statsAllocArray[STATS_MAX_NUM_ALLOC]; // used to track mem allocs for reset/free
+ int allocIndex; // our position in the global statsAllocArray
+
+ pthread_mutex_t LOCK_stats_load, LOCK_stats_clock;
+
+ char *extsql_key, *extsql_users, *extsql_version;
+
+ ulong extsql_active, // temp stop extsql activity, can be user re-activated by SET
+ extsql_disabled, // perm stop extsql activity, severe/license error, only cleared by server restart
+ extsql_reload_in_progress, // temp stop extsql activity during startType reset/reload
+ extsql_debug, queries, connects, extsql_counter;
+
+ char *varAddrTrackArray[MAX_VARS]; // tracks addr of vars of interest, speeds up increment search.
+ int varAddrCount; // how many do we have being tracked
+ int lastMin; // top of minute processing
+
+} GLOBAL, *pGLOBAL;
+
+extern pGLOBAL Shared;
+
+// FOLLOWING only used at startup, then referenced from Shared.
+extern ulong extsql_active, extsql_counter, extsql_debug, queries, connects;
+extern char *extsql_class_list, *extsql_key, *extsql_users, *extsql_reload_file;
+
+
+// FUNCTION PROTOTYPES
+int extsql_init_globals();
+int extsql_check(THD*, int);
+
+#ifdef PGSQL
+int extsql_output_prep_2_col(char *col1, char *col2,DestReceiver *dest,
+ TupOutputState **tstate, TupleDesc tupdesc);
+int extsql_output_2_col(char *col1Str, int col1Int, char *col2Str, int col2Int,
+ DestReceiver *dest, TupOutputState *tstate);
+void extsql_output_complete(THD *, TupOutputState *);
+int extsql_usage(THD*, DestReceiver*);
+int extsql_enable(THD*, DestReceiver*, uint);
+int extsql_reset(THD* thd, DestReceiver*, const char* conf);
+int extsql_file_io(THD* thd, DestReceiver*, uint io_mode, uint confirm, char* reload_file);
+#else
+static int extsql_output_prep_2_col(char *col1, char *col2, Protocol *protocol,
+ List
- field_list, char *fill2);
+static int extsql_output_2_col(char *col1Str, int col1Int, char *col2Str,
+ int col2Int, Protocol *protocol, char *fill1);
+void extsql_output_complete(THD* thd, char *tstate);
+int extsql_usage(THD*);
+int extsql_enable(THD*, uint);
+int extsql_reset(THD* thd, const char* conf);
+int extsql_file_io(THD* thd, uint io_mode, uint confirm, char* reload_file);
+#endif
+
+extern void extsql_inc(ulong *V, ulong C);
+extern int extsql_init(char *, int, char *);
+extern void extsql_prep(int *, const char *, char *);
+int extsql_thread_init(THD *thd, int initType);
+int extsql_read_section_bytes(char*, File, char**, char*, int);
+int extsql_reload(int action, char *loadFileName);
|