git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@805 0109f412-320b-0410-ab79-c3e0c5ffbbe6
1923 lines
42 KiB
C++
1923 lines
42 KiB
C++
//-------------------------------------------------------------------------
|
|
// Desc: Class for displaying the query page for monitoring via HTTP.
|
|
// Tabs: 3
|
|
//
|
|
// Copyright (c) 2002-2003,2005-2006 Novell, Inc. All Rights Reserved.
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of version 2 of the GNU General Public
|
|
// License as published by the Free Software Foundation.
|
|
//
|
|
// 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, contact Novell, Inc.
|
|
//
|
|
// To contact Novell about this file by physical or electronic mail,
|
|
// you may find current contact information at www.novell.com
|
|
//
|
|
// $Id: imonqury.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $
|
|
//-------------------------------------------------------------------------
|
|
|
|
#include "flaimsys.h"
|
|
|
|
#define Q_FIELD_COLOR FLM_MAGENTA
|
|
#define Q_LABEL_COLOR FLM_BLACK
|
|
#define Q_VALUE_COLOR FLM_GREEN
|
|
#define Q_PARAM_COLOR FLM_BLUE
|
|
#define Q_OPERATOR_COLOR FLM_BLUE
|
|
#define Q_YES_COLOR FLM_GREEN
|
|
#define Q_NO_COLOR FLM_RED
|
|
#define Q_DOT_COLOR FLM_BLACK
|
|
|
|
FSTATIC FLMBOOL findSubQuery(
|
|
CURSOR * pCursor,
|
|
SUBQUERY * pSubQuery);
|
|
|
|
/****************************************************************************
|
|
Desc: Class for formatting a query into HTML
|
|
*****************************************************************************/
|
|
class F_QueryFormatter
|
|
{
|
|
public:
|
|
F_QueryFormatter();
|
|
~F_QueryFormatter();
|
|
|
|
void formatQuery(
|
|
HRequest * pHRequest,
|
|
F_WebPage * pWebPage,
|
|
CURSOR * pCursor,
|
|
FLMBOOL bSingleLineOnly,
|
|
FLMUINT uiMaxChars);
|
|
|
|
void outputSubqueryStats(
|
|
HRequest * pHRequest,
|
|
F_WebPage * pWebPage,
|
|
SUBQUERY * pSubQuery);
|
|
|
|
private:
|
|
|
|
void outputStr(
|
|
const char * pszStr);
|
|
|
|
void changeColor(
|
|
eColorType eColor,
|
|
FLMBOOL bForceColor = FALSE);
|
|
|
|
void appendString(
|
|
const char * pszStr,
|
|
eColorType eColor = FLM_CURRENT_COLOR,
|
|
FLMBOOL bForceColor = FALSE);
|
|
|
|
void newline( void);
|
|
|
|
void outputIndent(
|
|
FLMUINT uiIndent);
|
|
|
|
void outputOperator(
|
|
QTYPES eOperator,
|
|
FLMBOOL bEndLine);
|
|
|
|
FINLINE void formatHex(
|
|
char * pszBuf,
|
|
FLMBYTE ucChar)
|
|
{
|
|
FLMUINT uiNibble = ((FLMUINT)ucChar >> 4) & 0xF;
|
|
|
|
*pszBuf++ = (char)(uiNibble <= 9
|
|
? (char)(uiNibble + '0')
|
|
: (char)(uiNibble - 10 + 'A'));
|
|
uiNibble = (FLMUINT)ucChar & 0xF;
|
|
*pszBuf = (char)(uiNibble <= 9
|
|
? (char)(uiNibble + '0')
|
|
: (char)(uiNibble - 10 + 'A'));
|
|
}
|
|
|
|
void outputBinary(
|
|
FLMBYTE * pucBuf,
|
|
FLMUINT uiBufLen,
|
|
eColorType eValueColor = Q_VALUE_COLOR);
|
|
|
|
void outputText(
|
|
FLMBYTE * pucBuf,
|
|
FLMUINT uiBufLen,
|
|
eColorType eColor = Q_VALUE_COLOR);
|
|
|
|
void outputPredicate(
|
|
FLMUINT uiIndent,
|
|
FQNODE * pQNode);
|
|
|
|
void outputSubQuery(
|
|
FLMUINT uiIndent,
|
|
QTYPES eParentOp,
|
|
CURSOR * pReferenceCursor,
|
|
SUBQUERY * pSubQuery);
|
|
|
|
void outputQuery(
|
|
FLMUINT uiIndent,
|
|
CURSOR * pReferenceCursor,
|
|
CURSOR * pCursor);
|
|
|
|
void outputLabel(
|
|
const char * pszLabel,
|
|
eColorType eLabelColor = Q_LABEL_COLOR);
|
|
|
|
void outputStringRow(
|
|
const char * pszLabel,
|
|
const char * pszValue,
|
|
eColorType eLabelColor = Q_LABEL_COLOR,
|
|
eColorType eValueColor = Q_VALUE_COLOR);
|
|
|
|
void outputYesNoRow(
|
|
const char * pszLabel,
|
|
FLMBOOL bYesNo,
|
|
eColorType eLabelColor = Q_LABEL_COLOR,
|
|
eColorType eYesColor = Q_YES_COLOR,
|
|
eColorType eNoColor = Q_NO_COLOR);
|
|
|
|
void outputUINTRow(
|
|
const char * pszLabel,
|
|
FLMUINT uiValue,
|
|
eColorType eLabelColor = Q_LABEL_COLOR,
|
|
eColorType eValueColor = Q_VALUE_COLOR);
|
|
|
|
void outputBinaryRow(
|
|
const char * pszLabel,
|
|
FLMBYTE * pucValue,
|
|
FLMUINT uiValueLen,
|
|
eColorType eLabelColor,
|
|
eColorType eValueColor);
|
|
|
|
HRequest * m_pHRequest;
|
|
F_WebPage * m_pWebPage;
|
|
eColorType m_eCurrColor;
|
|
FLMBOOL m_bSingleLineOnly;
|
|
FLMUINT m_uiMaxChars;
|
|
FLMUINT m_uiVisibleChars;
|
|
FLMUINT m_uiRowCount;
|
|
};
|
|
|
|
|
|
/****************************************************************************
|
|
Desc: Prints the web page for the list of queries.
|
|
****************************************************************************/
|
|
RCODE F_QueriesPage::display(
|
|
FLMUINT uiNumParams,
|
|
const char ** ppszParams)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
HFCURSOR hCursor;
|
|
const char * pszTitle = "Queries";
|
|
char szTemp [100];
|
|
QUERY_HDR * pQueryHdr;
|
|
F_QueryFormatter qf;
|
|
FLMBOOL bMutexLocked = FALSE;
|
|
FLMUINT uiRows;
|
|
|
|
F_UNREFERENCED_PARM( uiNumParams);
|
|
F_UNREFERENCED_PARM( ppszParams);
|
|
|
|
// Start the document
|
|
|
|
printDocStart( pszTitle, FALSE);
|
|
|
|
// Begin the table
|
|
|
|
printTableStart( pszTitle, 3);
|
|
|
|
// Write out the table headings
|
|
|
|
printTableRowStart();
|
|
printColumnHeading( "Query Criteria");
|
|
printColumnHeading( "Terminate Status");
|
|
printColumnHeading( "Record Count");
|
|
printTableRowEnd();
|
|
|
|
// Lock the mutex on the queries
|
|
|
|
f_mutexLock( gv_FlmSysData.hQueryMutex);
|
|
bMutexLocked = TRUE;
|
|
|
|
// Position to the most recent query and display in order from
|
|
// newest to oldest.
|
|
|
|
uiRows = 0;
|
|
pQueryHdr = gv_FlmSysData.pNewestQuery;
|
|
while (pQueryHdr)
|
|
{
|
|
char szAddress [20];
|
|
CURSOR * pCursor;
|
|
SUBQUERY * pSubQuery;
|
|
FLMUINT uiRecCount;
|
|
|
|
hCursor = pQueryHdr->hCursor;
|
|
pCursor = (CURSOR *)hCursor;
|
|
|
|
// Setup a hyperlink for the query.
|
|
|
|
printAddress( (void *)hCursor, szAddress);
|
|
f_sprintf((char *)szTemp,
|
|
"%s/Query?QueryHandle=%s",
|
|
m_pszURLString,
|
|
szAddress);
|
|
|
|
// Output a row for the query
|
|
|
|
printTableRowStart( (++uiRows) & 0x00000001 ? TRUE : FALSE);
|
|
|
|
// Start a cell for the data
|
|
|
|
printTableDataStart();
|
|
|
|
// Hyperlink and query criteria
|
|
|
|
fnPrintf( m_pHRequest, "<a href=\"%s\">", szTemp);
|
|
|
|
// Format query - up to 80 viewable characters.
|
|
|
|
qf.formatQuery( m_pHRequest, this, pCursor, TRUE, 80);
|
|
|
|
// Close the hyperlink
|
|
|
|
fnPrintf( m_pHRequest, "</a>");
|
|
|
|
// Close the cell
|
|
|
|
printTableDataEnd();
|
|
|
|
// Query status
|
|
|
|
if (pCursor->rc == FERR_EOF_HIT)
|
|
{
|
|
f_strcpy( szTemp, "EOF");
|
|
}
|
|
else if (pCursor->rc == FERR_BOF_HIT)
|
|
{
|
|
f_strcpy( szTemp, "BOF");
|
|
}
|
|
else if (pCursor->rc != FERR_OK)
|
|
{
|
|
f_sprintf( (char *)szTemp, "Error: %04X", (unsigned)pCursor->rc);
|
|
}
|
|
else
|
|
{
|
|
f_strcpy( szTemp, "App Ended");
|
|
}
|
|
|
|
printTableDataStart();
|
|
fnPrintf( m_pHRequest, "%s", szTemp);
|
|
printTableDataEnd();
|
|
|
|
// Count of records returned
|
|
|
|
uiRecCount = 0;
|
|
pSubQuery = pCursor->pSubQueryList;
|
|
while (pSubQuery)
|
|
{
|
|
uiRecCount += pSubQuery->SQStatus.uiMatchedCnt;
|
|
pSubQuery = pSubQuery->pNext;
|
|
}
|
|
fnPrintf( m_pHRequest, TD_ui, (unsigned)uiRecCount);
|
|
|
|
printTableRowEnd();
|
|
pQueryHdr = pQueryHdr->pNext;
|
|
}
|
|
|
|
// Unlock the mutex on the queries
|
|
|
|
f_mutexUnlock( gv_FlmSysData.hQueryMutex);
|
|
bMutexLocked = FALSE;
|
|
|
|
printTableEnd();
|
|
printDocEnd();
|
|
|
|
if (bMutexLocked)
|
|
{
|
|
f_mutexUnlock( gv_FlmSysData.hQueryMutex);
|
|
}
|
|
|
|
fnEmit();
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Constructor
|
|
*****************************************************************************/
|
|
F_QueryFormatter::F_QueryFormatter()
|
|
{
|
|
m_eCurrColor = FLM_CURRENT_COLOR;
|
|
m_uiRowCount = 0;
|
|
m_bSingleLineOnly = 0;
|
|
m_uiMaxChars = 0;
|
|
m_uiVisibleChars = 0;
|
|
m_pWebPage = NULL;
|
|
m_pHRequest = NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Destuctor
|
|
*****************************************************************************/
|
|
F_QueryFormatter::~F_QueryFormatter()
|
|
{
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Output a null-terminated string to the buffer.
|
|
****************************************************************************/
|
|
void F_QueryFormatter::outputStr(
|
|
const char * pszStr)
|
|
{
|
|
gv_FlmSysData.HttpConfigParms.fnPrintf( m_pHRequest, pszStr);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Output a new color code, but only if the color has changed.
|
|
****************************************************************************/
|
|
void F_QueryFormatter::changeColor(
|
|
eColorType eColor,
|
|
FLMBOOL bForceColor)
|
|
{
|
|
const char * pszColor;
|
|
|
|
// If we are doing a single line, and we have filled our buffer
|
|
// with the maximum visible characters, we are not going to add
|
|
// anything else to the buffer.
|
|
|
|
if (m_bSingleLineOnly && m_uiVisibleChars == m_uiMaxChars)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// If no color has yet been set, and none was passed in,
|
|
// set to a default of light gray.
|
|
|
|
if (eColor == FLM_CURRENT_COLOR && m_eCurrColor == FLM_CURRENT_COLOR)
|
|
{
|
|
eColor = FLM_LIGHTGRAY;
|
|
}
|
|
|
|
// Change colors if necessary
|
|
|
|
if (bForceColor || (eColor != FLM_CURRENT_COLOR && eColor != m_eCurrColor))
|
|
{
|
|
|
|
m_eCurrColor = eColor;
|
|
|
|
switch (eColor)
|
|
{
|
|
case FLM_BLACK:
|
|
pszColor = "<font color=\"Black\">";
|
|
break;
|
|
case FLM_BLUE:
|
|
pszColor = "<font color=\"Blue\">";
|
|
break;
|
|
case FLM_GREEN:
|
|
pszColor = "<font color=\"Green\">";
|
|
break;
|
|
case FLM_CYAN:
|
|
//VISIT: Redo this one
|
|
pszColor = "<font color=\"Teal\">";
|
|
break;
|
|
case FLM_RED:
|
|
pszColor = "<font color=\"Red\">";
|
|
break;
|
|
case FLM_MAGENTA:
|
|
pszColor = "<font color=\"Purple\">";
|
|
break;
|
|
case FLM_BROWN:
|
|
//VISIT: Redo this one
|
|
pszColor = "<font color=\"Maroon\">";
|
|
break;
|
|
case FLM_LIGHTGRAY:
|
|
pszColor = "<font color=\"Gray\">";
|
|
break;
|
|
case FLM_DARKGRAY:
|
|
//VISIT: Redo this one
|
|
pszColor = "<font color=\"Gray\">";
|
|
break;
|
|
case FLM_LIGHTBLUE:
|
|
//VISIT: Redo this one
|
|
pszColor = "<font color=\"Navy\">";
|
|
break;
|
|
case FLM_LIGHTGREEN:
|
|
//VISIT: Redo this one
|
|
pszColor = "<font color=\"Lime\">";
|
|
break;
|
|
case FLM_LIGHTCYAN:
|
|
//VISIT: Redo this one
|
|
pszColor = "<font color=\"Teal\">";
|
|
break;
|
|
case FLM_LIGHTRED:
|
|
//VISIT: Redo this one
|
|
pszColor = "<font color=\"Red\">";
|
|
break;
|
|
case FLM_LIGHTMAGENTA:
|
|
//VISIT: Redo this one
|
|
pszColor = "<font color=\"Fuchsia\">";
|
|
break;
|
|
case FLM_YELLOW:
|
|
pszColor = "<font color=\"Yellow\">";
|
|
break;
|
|
case FLM_WHITE:
|
|
pszColor = "<font color=\"White\">";
|
|
break;
|
|
default:
|
|
flmAssert( 0);
|
|
goto Exit;
|
|
}
|
|
|
|
// Output the color buffer
|
|
|
|
outputStr( pszColor);
|
|
}
|
|
|
|
Exit:
|
|
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Append a string to the output buffer.
|
|
****************************************************************************/
|
|
void F_QueryFormatter::appendString(
|
|
const char * pszStr,
|
|
eColorType eColor,
|
|
FLMBOOL bForceColor)
|
|
{
|
|
char szTmpBuf [80];
|
|
FLMUINT uiLen;
|
|
|
|
// If we are doing a single line, and we have filled our buffer
|
|
// with the maximum visible characters, we are done.
|
|
|
|
if (m_bSingleLineOnly && m_uiVisibleChars == m_uiMaxChars)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Output a color change, if any
|
|
|
|
changeColor( eColor, bForceColor);
|
|
|
|
// Check for a NULL pointer - treat like empty string.
|
|
|
|
if (pszStr)
|
|
{
|
|
|
|
// Output each character to the buffer. Use the special encoding
|
|
// format for reserved HTML characters.
|
|
|
|
uiLen = 0;
|
|
while (*pszStr)
|
|
{
|
|
|
|
// Can format any character in six characters in HTML - "&#xxx;"
|
|
// xxx is the decimal digit for the character.
|
|
// Therefore, if we have room for less than six characters in
|
|
// the temporary buffer, output what we currently have in the
|
|
// buffer.
|
|
|
|
if (uiLen + 6 >= sizeof( szTmpBuf))
|
|
{
|
|
szTmpBuf [uiLen] = 0;
|
|
outputStr( szTmpBuf);
|
|
|
|
// Start filling the temporary buffer again.
|
|
|
|
uiLen = 0;
|
|
}
|
|
|
|
// Check for reserved characters that need to be encoded.
|
|
|
|
if ((int)(*pszStr) > 126 ||
|
|
*pszStr == '\"' ||
|
|
*pszStr == '&' ||
|
|
*pszStr == '<' ||
|
|
*pszStr == '>')
|
|
{
|
|
f_sprintf( &szTmpBuf [uiLen], "&#%d;", (int)(*pszStr));
|
|
while (szTmpBuf [uiLen])
|
|
{
|
|
uiLen++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
szTmpBuf [uiLen++] = *pszStr;
|
|
}
|
|
|
|
if (m_bSingleLineOnly)
|
|
{
|
|
|
|
// If we have reached the maximum visible characters,
|
|
// quit outputting.
|
|
|
|
if (++m_uiVisibleChars == m_uiMaxChars)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Go to the next character
|
|
|
|
pszStr++;
|
|
}
|
|
|
|
// If there is something in the temporary buffer, output it.
|
|
|
|
if (uiLen)
|
|
{
|
|
szTmpBuf [uiLen] = 0;
|
|
outputStr( szTmpBuf);
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Output a newline.
|
|
****************************************************************************/
|
|
void F_QueryFormatter::newline( void)
|
|
{
|
|
if (!m_bSingleLineOnly)
|
|
{
|
|
outputStr( "\n");
|
|
|
|
// Reset so we start again.
|
|
|
|
m_uiVisibleChars = 0;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine indents the output.
|
|
****************************************************************************/
|
|
void F_QueryFormatter::outputIndent(
|
|
FLMUINT uiIndent
|
|
)
|
|
{
|
|
if (!m_bSingleLineOnly)
|
|
{
|
|
|
|
// Note: just use current color here. It doesn't matter because
|
|
// we are outputting spaces, and we cannot change the background
|
|
// color.
|
|
|
|
while (uiIndent)
|
|
{
|
|
outputStr( " ");
|
|
uiIndent--;
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine outputs an operator
|
|
****************************************************************************/
|
|
void F_QueryFormatter::outputOperator(
|
|
QTYPES eOperator,
|
|
FLMBOOL bEndLine
|
|
)
|
|
{
|
|
const char * pszOperator;
|
|
|
|
switch (eOperator)
|
|
{
|
|
case FLM_AND_OP:
|
|
pszOperator = "AND";
|
|
break;
|
|
case FLM_OR_OP:
|
|
pszOperator = "OR";
|
|
break;
|
|
case FLM_NOT_OP:
|
|
pszOperator = "!";
|
|
break;
|
|
case FLM_EQ_OP:
|
|
pszOperator = "==";
|
|
break;
|
|
case FLM_MATCH_OP:
|
|
pszOperator = "MATCH";
|
|
break;
|
|
case FLM_MATCH_BEGIN_OP:
|
|
pszOperator = "MATCHBEGIN";
|
|
break;
|
|
case FLM_MATCH_END_OP:
|
|
pszOperator = "MATCHEND";
|
|
break;
|
|
case FLM_CONTAINS_OP:
|
|
pszOperator = "CONTAINS";
|
|
break;
|
|
case FLM_NE_OP:
|
|
pszOperator = "!=";
|
|
break;
|
|
case FLM_LT_OP:
|
|
pszOperator = "<";
|
|
break;
|
|
case FLM_LE_OP:
|
|
pszOperator = "<=";
|
|
break;
|
|
case FLM_GT_OP:
|
|
pszOperator = ">";
|
|
break;
|
|
case FLM_GE_OP:
|
|
pszOperator = ">=";
|
|
break;
|
|
case FLM_BITAND_OP:
|
|
pszOperator = "&";
|
|
break;
|
|
case FLM_BITOR_OP:
|
|
pszOperator = "|";
|
|
break;
|
|
case FLM_BITXOR_OP:
|
|
pszOperator = "^";
|
|
break;
|
|
case FLM_MULT_OP:
|
|
pszOperator = "*";
|
|
break;
|
|
case FLM_DIV_OP:
|
|
pszOperator = "/";
|
|
break;
|
|
case FLM_MOD_OP:
|
|
pszOperator = "%";
|
|
break;
|
|
case FLM_PLUS_OP:
|
|
pszOperator = "+";
|
|
break;
|
|
case FLM_MINUS_OP:
|
|
case FLM_NEG_OP:
|
|
pszOperator = "-";
|
|
break;
|
|
case FLM_LPAREN_OP:
|
|
pszOperator = "(";
|
|
break;
|
|
case FLM_RPAREN_OP:
|
|
pszOperator = ")";
|
|
break;
|
|
default:
|
|
pszOperator = "UNKNOWN";
|
|
break;
|
|
}
|
|
|
|
appendString( pszOperator, Q_OPERATOR_COLOR);
|
|
|
|
if (bEndLine && !m_bSingleLineOnly)
|
|
{
|
|
newline();
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine outputs a buffer of binary bytes as ASCII hex.
|
|
****************************************************************************/
|
|
void F_QueryFormatter::outputBinary(
|
|
FLMBYTE * pucBuf,
|
|
FLMUINT uiBufLen,
|
|
eColorType eValueColor)
|
|
{
|
|
FLMUINT uiLoop;
|
|
FLMUINT uiOffset;
|
|
char szBuf [128];
|
|
FLMBYTE ucChar;
|
|
FLMUINT uiRepeatCnt;
|
|
FLMUINT uiCharsNeeded;
|
|
FLMBOOL bFirstChar = TRUE;
|
|
|
|
// Opening paren
|
|
|
|
appendString( "(", eValueColor);
|
|
|
|
// Output the word "<empty>" if the binary value is empty.
|
|
// Otherwise output as hex values.
|
|
|
|
if (!uiBufLen)
|
|
{
|
|
appendString( "<empty>", eValueColor);
|
|
}
|
|
else
|
|
{
|
|
|
|
uiLoop = 0;
|
|
uiOffset = 0;
|
|
while (uiLoop < uiBufLen)
|
|
{
|
|
|
|
// See how many there are of this byte.
|
|
|
|
ucChar = *pucBuf;
|
|
uiRepeatCnt = 1;
|
|
uiLoop++;
|
|
pucBuf++;
|
|
while (uiLoop < uiBufLen && *pucBuf == ucChar)
|
|
{
|
|
uiLoop++;
|
|
pucBuf++;
|
|
uiRepeatCnt++;
|
|
}
|
|
|
|
// If this is the first character, we need 2 characters
|
|
// for the HEX digits. If not the first character,
|
|
// we need 3 character - one leading space and 2 for hex digits.
|
|
|
|
uiCharsNeeded = (FLMUINT)(bFirstChar
|
|
? (FLMUINT)2
|
|
: (FLMUINT)3);
|
|
|
|
// Calculate number of characters needed for
|
|
// the ":cnt" after the hex digits if we have
|
|
// the character repeated more than once.
|
|
|
|
if (uiRepeatCnt > 1)
|
|
{
|
|
FLMUINT uiTmp = uiRepeatCnt;
|
|
while (uiTmp)
|
|
{
|
|
uiCharsNeeded++;
|
|
uiTmp /= 10;
|
|
}
|
|
uiCharsNeeded++; // Add one for the colon.
|
|
}
|
|
|
|
// Don't overflow the buffer.
|
|
|
|
if (uiOffset >= sizeof( szBuf) - uiCharsNeeded)
|
|
{
|
|
szBuf [uiOffset] = 0;
|
|
appendString( szBuf, eValueColor);
|
|
uiOffset = 0;
|
|
}
|
|
|
|
// Put a space between bytes.
|
|
|
|
if (!bFirstChar)
|
|
{
|
|
szBuf [uiOffset++] = ' ';
|
|
}
|
|
else
|
|
{
|
|
bFirstChar = FALSE;
|
|
}
|
|
formatHex( &szBuf [uiOffset], ucChar);
|
|
uiOffset += 2;
|
|
if (uiRepeatCnt > 1)
|
|
{
|
|
szBuf [uiOffset++] = ':';
|
|
f_sprintf( &szBuf [uiOffset], "%u", (unsigned)uiRepeatCnt);
|
|
while (szBuf [uiOffset])
|
|
{
|
|
uiOffset++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Output whatever has not been output so far.
|
|
|
|
if (uiOffset)
|
|
{
|
|
szBuf [uiOffset] = 0;
|
|
appendString( szBuf, eValueColor);
|
|
}
|
|
}
|
|
|
|
// Closing paren
|
|
|
|
appendString( ")", eValueColor);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine outputs text data.
|
|
****************************************************************************/
|
|
void F_QueryFormatter::outputText(
|
|
FLMBYTE * pucBuf,
|
|
FLMUINT uiBufLen,
|
|
eColorType eColor)
|
|
{
|
|
FLMUINT uiLoop;
|
|
FLMUINT uiOffset;
|
|
FLMBYTE ucChar;
|
|
FLMUINT uiObjType;
|
|
FLMUINT uiObjLength = 0;
|
|
char szBuf [128];
|
|
|
|
uiOffset = 0;
|
|
for (uiLoop = 0, uiOffset = 0;
|
|
uiLoop < uiBufLen;
|
|
pucBuf += uiObjLength, uiLoop += uiObjLength)
|
|
{
|
|
|
|
// Don't overflow the buffer - must have room for maximum bytes needed
|
|
// to represent a character.
|
|
|
|
if (uiOffset >= sizeof( szBuf) - 14)
|
|
{
|
|
szBuf [uiOffset] = 0;
|
|
appendString( szBuf, eColor);
|
|
uiOffset = 0;
|
|
}
|
|
|
|
ucChar = *pucBuf;
|
|
uiObjType = flmTextObjType( ucChar);
|
|
switch (uiObjType)
|
|
{
|
|
case ASCII_CHAR_CODE: // 0nnnnnnn
|
|
uiObjLength = 1;
|
|
|
|
// Character set zero is assumed.
|
|
|
|
szBuf [uiOffset++] = (char)ucChar;
|
|
break;
|
|
case CHAR_SET_CODE: // 10nnnnnn
|
|
uiObjLength = 2;
|
|
|
|
// Character set followed by character
|
|
|
|
f_strcpy( &szBuf [uiOffset], "~[UC-0x");
|
|
uiOffset += 7;
|
|
formatHex( &szBuf [uiOffset], ucChar & (~CHAR_SET_MASK));
|
|
formatHex( &szBuf [uiOffset + 2], *(pucBuf + 1));
|
|
szBuf [uiOffset + 4] = ']';
|
|
uiOffset += 5;
|
|
break;
|
|
case WHITE_SPACE_CODE: // 110nnnnn
|
|
uiObjLength = 1;
|
|
szBuf [uiOffset++] = ' ';
|
|
break;
|
|
case EXT_CHAR_CODE:
|
|
uiObjLength = 3;
|
|
|
|
// Character set followed by character
|
|
|
|
f_strcpy( &szBuf [uiOffset], "~[WP-0x");
|
|
uiOffset += 7;
|
|
formatHex( &szBuf [uiOffset], *(pucBuf + 1));
|
|
formatHex( &szBuf [uiOffset + 2], *(pucBuf + 2));
|
|
szBuf [uiOffset + 4] = ']';
|
|
uiOffset += 5;
|
|
break;
|
|
case OEM_CODE:
|
|
|
|
// OEM characters are always >= 128
|
|
// Use character set zero to process them.
|
|
|
|
uiObjLength = 2;
|
|
szBuf [uiOffset++] = (char)(*(pucBuf + 1));
|
|
break;
|
|
case UNICODE_CODE: // Unconvertable UNICODE code
|
|
uiObjLength = 3;
|
|
|
|
// Unicode character followed by unicode character set
|
|
|
|
f_strcpy( &szBuf [uiOffset], "~[UC-0x");
|
|
uiOffset += 7;
|
|
formatHex( &szBuf [uiOffset], *(pucBuf + 1));
|
|
formatHex( &szBuf [uiOffset + 2], *(pucBuf + 2));
|
|
szBuf [uiOffset + 4] = ']';
|
|
uiOffset += 5;
|
|
break;
|
|
default:
|
|
|
|
// Should not happen
|
|
|
|
flmAssert( 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Log whatever has not yet been logged.
|
|
|
|
if (uiOffset)
|
|
{
|
|
szBuf [uiOffset] = 0;
|
|
appendString( szBuf, eColor);
|
|
uiOffset = 0;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine logs the query criteria for a cursor.
|
|
****************************************************************************/
|
|
void F_QueryFormatter::outputPredicate(
|
|
FLMUINT uiIndent,
|
|
FQNODE * pQNode
|
|
)
|
|
{
|
|
FLMUINT uiNestLevel = 0;
|
|
QTYPES eCurrentOp;
|
|
char szBuf [80];
|
|
|
|
if (!m_bSingleLineOnly)
|
|
{
|
|
outputIndent( uiIndent);
|
|
}
|
|
else
|
|
{
|
|
appendString( " ");
|
|
}
|
|
|
|
// Traverse the tree.
|
|
|
|
for (;;)
|
|
{
|
|
eColorType eSaveCurrColor;
|
|
|
|
eCurrentOp = GET_QNODE_TYPE( pQNode);
|
|
if (IS_OP( eCurrentOp))
|
|
{
|
|
if (uiNestLevel)
|
|
{
|
|
outputOperator( FLM_LPAREN_OP, FALSE);
|
|
}
|
|
pQNode = pQNode->pChild;
|
|
eCurrentOp = GET_QNODE_TYPE( pQNode);
|
|
uiNestLevel++;
|
|
continue;
|
|
}
|
|
|
|
eSaveCurrColor = m_eCurrColor;
|
|
if (IS_VAL( eCurrentOp))
|
|
{
|
|
switch (eCurrentOp)
|
|
{
|
|
case FLM_BOOL_VAL:
|
|
f_sprintf( szBuf, "%u", (unsigned)pQNode->pQAtom->val.uiBool);
|
|
appendString( szBuf, Q_VALUE_COLOR);
|
|
break;
|
|
case FLM_REC_PTR_VAL:
|
|
case FLM_UINT32_VAL:
|
|
f_sprintf( szBuf, "%u", (unsigned)pQNode->pQAtom->val.uiVal);
|
|
appendString( szBuf, Q_VALUE_COLOR);
|
|
break;
|
|
case FLM_UINT64_VAL:
|
|
f_sprintf( szBuf, "%I64u", pQNode->pQAtom->val.ui64Val);
|
|
appendString( szBuf, Q_VALUE_COLOR);
|
|
break;
|
|
case FLM_INT32_VAL:
|
|
f_sprintf( szBuf, "%d", (int)pQNode->pQAtom->val.iVal);
|
|
appendString( szBuf, Q_VALUE_COLOR);
|
|
break;
|
|
case FLM_INT64_VAL:
|
|
f_sprintf( szBuf, "%I64d", pQNode->pQAtom->val.i64Val);
|
|
appendString( szBuf, Q_VALUE_COLOR);
|
|
break;
|
|
case FLM_BINARY_VAL:
|
|
appendString( "BINARY", Q_LABEL_COLOR);
|
|
outputBinary( pQNode->pQAtom->val.pucBuf,
|
|
pQNode->pQAtom->uiBufLen,
|
|
Q_VALUE_COLOR);
|
|
break;
|
|
case FLM_TEXT_VAL:
|
|
appendString( "\"", Q_VALUE_COLOR);
|
|
outputText( pQNode->pQAtom->val.pucBuf,
|
|
pQNode->pQAtom->uiBufLen, Q_VALUE_COLOR);
|
|
appendString( "\"", Q_VALUE_COLOR);
|
|
break;
|
|
default:
|
|
flmAssert( 0);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FLMUINT * puiFldPath = pQNode->pQAtom->val.QueryFld.puiFldPath;
|
|
FLMUINT uiCnt;
|
|
|
|
flmAssert( IS_FIELD( eCurrentOp));
|
|
appendString( "FLD:", Q_FIELD_COLOR);
|
|
|
|
// Fields are from child to parent order - must count fields
|
|
// and print out in parent to child order.
|
|
|
|
uiCnt = 0;
|
|
while (puiFldPath [uiCnt])
|
|
{
|
|
uiCnt++;
|
|
}
|
|
while (uiCnt)
|
|
{
|
|
uiCnt--;
|
|
if (uiCnt)
|
|
{
|
|
f_sprintf( szBuf, "%u.", (unsigned)puiFldPath [uiCnt]);
|
|
}
|
|
else
|
|
{
|
|
f_sprintf( szBuf, "%u", (unsigned)puiFldPath [uiCnt]);
|
|
}
|
|
appendString( szBuf, Q_FIELD_COLOR);
|
|
}
|
|
}
|
|
|
|
if (!uiNestLevel)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Go to the sibling, if any.
|
|
|
|
while (!pQNode->pNextSib)
|
|
{
|
|
pQNode = pQNode->pParent;
|
|
uiNestLevel--;
|
|
if (!uiNestLevel)
|
|
{
|
|
goto Exit; // Done with this predicate.
|
|
}
|
|
outputOperator( FLM_RPAREN_OP, FALSE);
|
|
}
|
|
|
|
// Have a sibling, log the operator.
|
|
|
|
eCurrentOp = GET_QNODE_TYPE( pQNode->pParent);
|
|
appendString( " ");
|
|
outputOperator( eCurrentOp, FALSE);
|
|
appendString( " ");
|
|
pQNode = pQNode->pNextSib;
|
|
}
|
|
|
|
Exit:
|
|
|
|
if( !m_bSingleLineOnly)
|
|
{
|
|
newline();
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine outputs a subquery.
|
|
****************************************************************************/
|
|
void F_QueryFormatter::outputSubQuery(
|
|
FLMUINT uiIndent,
|
|
QTYPES eParentOp,
|
|
CURSOR * pReferenceCursor,
|
|
SUBQUERY * pSubQuery
|
|
)
|
|
{
|
|
char * pszURL = NULL;
|
|
FQNODE * pQNode;
|
|
QTYPES eCurrentOp;
|
|
QTYPES eTmpParentOp;
|
|
FLMBOOL bIndentOptInfo = TRUE;
|
|
|
|
if ((pQNode = pSubQuery->pTree) == NULL)
|
|
{
|
|
if (!m_bSingleLineOnly)
|
|
{
|
|
outputIndent( uiIndent);
|
|
}
|
|
|
|
outputOperator( FLM_LPAREN_OP, FALSE);
|
|
appendString( "<empty>", Q_VALUE_COLOR);
|
|
outputOperator( FLM_RPAREN_OP, TRUE);
|
|
goto Output_Opt_Info;
|
|
}
|
|
|
|
// Traverse the tree.
|
|
|
|
for (;;)
|
|
{
|
|
eCurrentOp = GET_QNODE_TYPE( pQNode);
|
|
eTmpParentOp = (QTYPES)(pQNode->pParent
|
|
? GET_QNODE_TYPE( pQNode->pParent)
|
|
: eParentOp);
|
|
if (eCurrentOp == FLM_AND_OP)
|
|
{
|
|
if (eTmpParentOp == FLM_OR_OP)
|
|
{
|
|
if (!m_bSingleLineOnly)
|
|
{
|
|
outputIndent( uiIndent);
|
|
}
|
|
|
|
outputOperator( FLM_LPAREN_OP, TRUE);
|
|
uiIndent += 2;
|
|
bIndentOptInfo = FALSE;
|
|
}
|
|
pQNode = pQNode->pChild;
|
|
}
|
|
else if (eCurrentOp == FLM_OR_OP)
|
|
{
|
|
if (eTmpParentOp == FLM_AND_OP)
|
|
{
|
|
if (!m_bSingleLineOnly)
|
|
{
|
|
outputIndent( uiIndent);
|
|
}
|
|
|
|
outputOperator( FLM_LPAREN_OP, TRUE);
|
|
uiIndent += 2;
|
|
}
|
|
pQNode = pQNode->pChild;
|
|
}
|
|
else if (eCurrentOp == FLM_USER_PREDICATE)
|
|
{
|
|
HFCURSOR hCursor = pQNode->pQAtom->val.pPredicate->getCursor();
|
|
|
|
if (!m_bSingleLineOnly)
|
|
{
|
|
outputIndent( uiIndent);
|
|
}
|
|
|
|
outputOperator( FLM_LPAREN_OP, FALSE);
|
|
|
|
if (hCursor == HFCURSOR_NULL)
|
|
{
|
|
appendString( " [EmbeddedPredicate] ", Q_LABEL_COLOR);
|
|
outputOperator( FLM_RPAREN_OP, TRUE);
|
|
}
|
|
else
|
|
{
|
|
appendString( " [BeginEmbedded", Q_LABEL_COLOR);
|
|
if (pSubQuery->OptInfo.eOptType == QOPT_USING_PREDICATE &&
|
|
pSubQuery->pPredicate == pQNode->pQAtom->val.pPredicate)
|
|
{
|
|
appendString( ", Optimized]", Q_LABEL_COLOR);
|
|
}
|
|
else
|
|
{
|
|
appendString( "]", Q_LABEL_COLOR);
|
|
}
|
|
bIndentOptInfo = FALSE;
|
|
if (!m_bSingleLineOnly)
|
|
{
|
|
newline();
|
|
}
|
|
uiIndent += 2;
|
|
outputQuery( uiIndent, pReferenceCursor, (CURSOR *)hCursor);
|
|
uiIndent -= 2;
|
|
if (!m_bSingleLineOnly)
|
|
{
|
|
outputIndent( uiIndent);
|
|
}
|
|
outputOperator( FLM_RPAREN_OP, FALSE);
|
|
appendString( " [EndEmbedded]", Q_LABEL_COLOR);
|
|
if (!m_bSingleLineOnly)
|
|
{
|
|
newline();
|
|
}
|
|
}
|
|
goto Traverse_Up;
|
|
}
|
|
else
|
|
{
|
|
flmAssert( eCurrentOp != FLM_NOT_OP);
|
|
if (!pQNode->pNextSib && !pQNode->pParent)
|
|
{
|
|
outputPredicate( uiIndent, pQNode);
|
|
}
|
|
else
|
|
{
|
|
outputPredicate( uiIndent + 2, pQNode);
|
|
bIndentOptInfo = FALSE;
|
|
}
|
|
Traverse_Up:
|
|
while (!pQNode->pNextSib)
|
|
{
|
|
if ((pQNode = pQNode->pParent) == NULL)
|
|
{
|
|
goto Output_Opt_Info;
|
|
}
|
|
eCurrentOp = GET_QNODE_TYPE( pQNode);
|
|
eTmpParentOp = (QTYPES)(pQNode->pParent
|
|
? GET_QNODE_TYPE( pQNode->pParent)
|
|
: eParentOp);
|
|
if ((eCurrentOp == FLM_AND_OP && eTmpParentOp == FLM_OR_OP) ||
|
|
(eCurrentOp == FLM_OR_OP && eTmpParentOp == FLM_AND_OP))
|
|
{
|
|
flmAssert( uiIndent >= 2);
|
|
uiIndent -= 2;
|
|
if (!m_bSingleLineOnly)
|
|
{
|
|
outputIndent( uiIndent);
|
|
}
|
|
outputOperator( FLM_RPAREN_OP, TRUE);
|
|
}
|
|
}
|
|
|
|
// Have a sibling.
|
|
|
|
if (!m_bSingleLineOnly)
|
|
{
|
|
outputIndent( uiIndent);
|
|
}
|
|
outputOperator( eTmpParentOp, TRUE);
|
|
pQNode = pQNode->pNextSib;
|
|
}
|
|
}
|
|
|
|
Output_Opt_Info:
|
|
if (m_bSingleLineOnly)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (bIndentOptInfo)
|
|
{
|
|
uiIndent += 2;
|
|
}
|
|
|
|
outputIndent( uiIndent);
|
|
|
|
// Create a URL for the sub-query statistics.
|
|
|
|
if( RC_BAD( f_alloc( 340, &pszURL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
printAddress( (void *)pReferenceCursor, pszURL);
|
|
printAddress( (void *)pSubQuery, &pszURL [20]);
|
|
f_sprintf( &pszURL [40], "<A href=\"javascript:openPopup('%s/QueryStats?"
|
|
"QueryHandle=%s&SubQuery=%s')\">",
|
|
gv_FlmSysData.HttpConfigParms.pszURLString,
|
|
pszURL, &pszURL [20]);
|
|
|
|
outputStr( &pszURL [40]);
|
|
appendString( "{OptInfo & Stats}", FLM_RED, TRUE);
|
|
outputStr( "</A>");
|
|
newline();
|
|
|
|
if (bIndentOptInfo)
|
|
{
|
|
flmAssert( uiIndent >= 2);
|
|
uiIndent -= 2;
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (pszURL)
|
|
{
|
|
f_free( &pszURL);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine formats the query criteria for a cursor and optionally
|
|
outputs it.
|
|
****************************************************************************/
|
|
void F_QueryFormatter::formatQuery(
|
|
HRequest * pHRequest,
|
|
F_WebPage * pWebPage,
|
|
CURSOR * pCursor,
|
|
FLMBOOL bSingleLineOnly,
|
|
FLMUINT uiMaxChars
|
|
)
|
|
{
|
|
m_pHRequest = pHRequest;
|
|
m_pWebPage = pWebPage;
|
|
m_bSingleLineOnly = bSingleLineOnly;
|
|
m_uiMaxChars = uiMaxChars;
|
|
m_uiVisibleChars = 0;
|
|
m_eCurrColor = FLM_CURRENT_COLOR;
|
|
|
|
outputQuery( 0, pCursor, pCursor);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine formats the query criteria for a cursor and optionally
|
|
outputs it. This routine may be called recursively.
|
|
****************************************************************************/
|
|
void F_QueryFormatter::outputQuery(
|
|
FLMUINT uiIndent,
|
|
CURSOR * pReferenceCursor,
|
|
CURSOR * pCursor
|
|
)
|
|
{
|
|
SUBQUERY * pSubQuery;
|
|
QTYPES eParentOp = (pCursor->pSubQueryList &&
|
|
pCursor->pSubQueryList->pNext)
|
|
? FLM_OR_OP
|
|
: NO_TYPE;
|
|
|
|
if (!uiIndent)
|
|
{
|
|
outputStr( "<PRE>");
|
|
|
|
if (!m_bSingleLineOnly)
|
|
{
|
|
appendString( "Query Criteria: ", Q_LABEL_COLOR);
|
|
}
|
|
if (!pCursor->pSubQueryList)
|
|
{
|
|
appendString( "<Empty>", Q_VALUE_COLOR);
|
|
}
|
|
|
|
if (!m_bSingleLineOnly)
|
|
{
|
|
newline();
|
|
}
|
|
uiIndent += 2;
|
|
}
|
|
|
|
// Output each sub-query.
|
|
|
|
pSubQuery = pCursor->pSubQueryList;
|
|
while (pSubQuery)
|
|
{
|
|
outputSubQuery( uiIndent, eParentOp, pReferenceCursor, pSubQuery);
|
|
if ((pSubQuery = pSubQuery->pNext) != NULL)
|
|
{
|
|
if (!m_bSingleLineOnly)
|
|
{
|
|
outputIndent( uiIndent);
|
|
}
|
|
else
|
|
{
|
|
appendString( " ");
|
|
}
|
|
|
|
outputOperator( FLM_OR_OP, TRUE);
|
|
|
|
if (m_bSingleLineOnly)
|
|
{
|
|
appendString( " ");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Output the last line of the query.
|
|
|
|
if (!uiIndent)
|
|
{
|
|
if (!m_bSingleLineOnly)
|
|
{
|
|
newline();
|
|
}
|
|
|
|
outputStr( "</PRE>");
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Prints the web page for a single query.
|
|
****************************************************************************/
|
|
RCODE F_QueryPage::display(
|
|
FLMUINT uiNumParams,
|
|
const char ** ppszParams)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
HFCURSOR hCursor;
|
|
QUERY_HDR * pQueryHdr;
|
|
char szHandle [100];
|
|
F_QueryFormatter qf;
|
|
FLMBOOL bMutexLocked = FALSE;
|
|
|
|
printDocStart( "Query");
|
|
popupFrame();
|
|
|
|
if (RC_BAD(rc = ExtractParameter( uiNumParams, ppszParams,
|
|
"QueryHandle", sizeof( szHandle),
|
|
szHandle)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
hCursor = (HFCURSOR)f_atoud( szHandle);
|
|
|
|
// Lock the mutex on the queries
|
|
|
|
f_mutexLock( gv_FlmSysData.hQueryMutex);
|
|
bMutexLocked = TRUE;
|
|
|
|
// See if the hCursor is in the list.
|
|
|
|
pQueryHdr = gv_FlmSysData.pNewestQuery;
|
|
while (pQueryHdr && pQueryHdr->hCursor != hCursor)
|
|
{
|
|
pQueryHdr = pQueryHdr->pNext;
|
|
}
|
|
|
|
if (pQueryHdr)
|
|
{
|
|
// Output query
|
|
|
|
qf.formatQuery( m_pHRequest, this, (CURSOR *)hCursor, FALSE, 0);
|
|
}
|
|
else
|
|
{
|
|
fnPrintf( m_pHRequest,
|
|
"<center>Query is no longer in the table</center>\n");
|
|
}
|
|
|
|
// Unlock the mutex on the queries
|
|
|
|
f_mutexUnlock( gv_FlmSysData.hQueryMutex);
|
|
bMutexLocked = FALSE;
|
|
|
|
printDocEnd();
|
|
|
|
Exit:
|
|
|
|
if (bMutexLocked)
|
|
{
|
|
f_mutexUnlock( gv_FlmSysData.hQueryMutex);
|
|
}
|
|
|
|
fnEmit();
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Find a sub-query within a cursor.
|
|
****************************************************************************/
|
|
FSTATIC FLMBOOL findSubQuery(
|
|
CURSOR * pCursor,
|
|
SUBQUERY * pSubQuery
|
|
)
|
|
{
|
|
SUBQUERY * pTmpSubQuery;
|
|
FLMBOOL bFound = FALSE;
|
|
HFCURSOR hTmpCursor;
|
|
FLMUINT uiLoop;
|
|
|
|
// First check the sub-query list.
|
|
|
|
pTmpSubQuery = pCursor->pSubQueryList;
|
|
while (pTmpSubQuery)
|
|
{
|
|
if (pTmpSubQuery == pSubQuery)
|
|
{
|
|
bFound = TRUE;
|
|
goto Exit;
|
|
}
|
|
pTmpSubQuery = pTmpSubQuery->pNext;
|
|
}
|
|
|
|
// Search through the query's embedded predicates
|
|
|
|
for (uiLoop = 0; uiLoop < pCursor->QTInfo.uiNumPredicates; uiLoop++)
|
|
{
|
|
if ((hTmpCursor = pCursor->QTInfo.ppPredicates [uiLoop]->getCursor()) !=
|
|
HFCURSOR_NULL)
|
|
{
|
|
if (findSubQuery( (CURSOR *)hTmpCursor, pSubQuery))
|
|
{
|
|
bFound = TRUE;
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( bFound);
|
|
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Prints the web page for statistics of a query/subquery
|
|
****************************************************************************/
|
|
RCODE F_QueryStatsPage::display(
|
|
FLMUINT uiNumParams,
|
|
const char ** ppszParams)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
QUERY_HDR * pQueryHdr;
|
|
HFCURSOR hCursor;
|
|
SUBQUERY * pSubQuery;
|
|
char szPtr [100];
|
|
F_QueryFormatter qf;
|
|
FLMBOOL bMutexLocked = FALSE;
|
|
|
|
printDocStart( "Query Statistics", FALSE);
|
|
|
|
// Get the query handle
|
|
|
|
if (RC_BAD(rc = ExtractParameter( uiNumParams, ppszParams,
|
|
"QueryHandle", sizeof( szPtr), szPtr)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
hCursor = (HFCURSOR)f_atoud( szPtr);
|
|
|
|
// Get the sub-query pointer
|
|
|
|
if (RC_BAD(rc = ExtractParameter( uiNumParams, ppszParams,
|
|
"SubQuery", sizeof( szPtr), szPtr)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pSubQuery = (SUBQUERY *)f_atoud( szPtr);
|
|
|
|
// Lock the mutex on the queries
|
|
|
|
f_mutexLock( gv_FlmSysData.hQueryMutex);
|
|
bMutexLocked = TRUE;
|
|
|
|
// See if the hCursor is in the list.
|
|
|
|
pQueryHdr = gv_FlmSysData.pNewestQuery;
|
|
while (pQueryHdr && pQueryHdr->hCursor != hCursor)
|
|
{
|
|
pQueryHdr = pQueryHdr->pNext;
|
|
}
|
|
|
|
if (pQueryHdr)
|
|
{
|
|
|
|
// Make sure we can find the sub-query.
|
|
|
|
if (!findSubQuery( (CURSOR *)hCursor, pSubQuery))
|
|
{
|
|
fnPrintf( m_pHRequest,
|
|
"<center>SubQuery is no longer in the query!</center>\n");
|
|
}
|
|
else
|
|
{
|
|
|
|
// Output subquery statistics
|
|
|
|
qf.outputSubqueryStats( m_pHRequest, this, pSubQuery);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fnPrintf( m_pHRequest,
|
|
"<center>Query is no longer in the table</center>\n");
|
|
}
|
|
|
|
// Unlock the mutex on the queries
|
|
|
|
f_mutexUnlock( gv_FlmSysData.hQueryMutex);
|
|
bMutexLocked = FALSE;
|
|
|
|
printDocEnd();
|
|
|
|
Exit:
|
|
|
|
if (bMutexLocked)
|
|
{
|
|
f_mutexUnlock( gv_FlmSysData.hQueryMutex);
|
|
}
|
|
|
|
fnEmit();
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine outputs a label in its own column.
|
|
****************************************************************************/
|
|
void F_QueryFormatter::outputLabel(
|
|
const char * pszLabel,
|
|
eColorType eLabelColor)
|
|
{
|
|
// Label goes in a column by itself
|
|
|
|
m_pWebPage->printTableDataStart();
|
|
|
|
// Output the label
|
|
|
|
appendString( pszLabel, eLabelColor, TRUE);
|
|
|
|
// End the data column
|
|
|
|
m_pWebPage->printTableDataEnd();
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine outputs a row that has a label in column one and a
|
|
string value in column two.
|
|
****************************************************************************/
|
|
void F_QueryFormatter::outputStringRow(
|
|
const char * pszLabel,
|
|
const char * pszValue,
|
|
eColorType eLabelColor,
|
|
eColorType eValueColor)
|
|
{
|
|
// Start a new row in the table
|
|
|
|
m_pWebPage->printTableRowStart( (++m_uiRowCount) & 0x00000001 ? TRUE : FALSE);
|
|
|
|
// Label the row
|
|
|
|
outputLabel( pszLabel, eLabelColor);
|
|
|
|
// Start a new column for the string value
|
|
|
|
m_pWebPage->printTableDataStart();
|
|
|
|
// Output the value
|
|
|
|
if (pszValue)
|
|
{
|
|
appendString( pszValue, eValueColor, TRUE);
|
|
}
|
|
|
|
// End the data column
|
|
|
|
m_pWebPage->printTableDataEnd();
|
|
|
|
// End the row
|
|
|
|
m_pWebPage->printTableRowEnd();
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine outputs a row that has a label in column one and a
|
|
YES/NO value in column two.
|
|
****************************************************************************/
|
|
void F_QueryFormatter::outputYesNoRow(
|
|
const char * pszLabel,
|
|
FLMBOOL bYesNo,
|
|
eColorType eLabelColor,
|
|
eColorType eYesColor,
|
|
eColorType eNoColor)
|
|
{
|
|
// Start a new row in the table
|
|
|
|
m_pWebPage->printTableRowStart( (++m_uiRowCount) & 0x00000001 ? TRUE : FALSE);
|
|
|
|
outputLabel( pszLabel, eLabelColor);
|
|
|
|
// Start a new column for the YES/NO value
|
|
|
|
m_pWebPage->printTableDataStart();
|
|
|
|
if (bYesNo)
|
|
{
|
|
appendString( "YES", eYesColor, TRUE);
|
|
}
|
|
else
|
|
{
|
|
appendString( "NO", eNoColor, TRUE);
|
|
}
|
|
|
|
// End the data column
|
|
|
|
m_pWebPage->printTableDataEnd();
|
|
|
|
// End the row
|
|
|
|
m_pWebPage->printTableRowEnd();
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine outputs a row that has a label in column one and a
|
|
UINT value in column two.
|
|
****************************************************************************/
|
|
void F_QueryFormatter::outputUINTRow(
|
|
const char * pszLabel,
|
|
FLMUINT uiValue,
|
|
eColorType eLabelColor,
|
|
eColorType eValueColor)
|
|
{
|
|
char szTmp [20];
|
|
|
|
// Start a new row in the table
|
|
|
|
m_pWebPage->printTableRowStart( (++m_uiRowCount) & 0x00000001 ? TRUE : FALSE);
|
|
|
|
outputLabel( pszLabel, eLabelColor);
|
|
|
|
// Start a new column for the YES/NO value
|
|
|
|
m_pWebPage->printTableDataStart();
|
|
|
|
f_sprintf( szTmp, "%u", (unsigned)uiValue);
|
|
appendString( szTmp, eValueColor, TRUE);
|
|
|
|
// End the data column
|
|
|
|
m_pWebPage->printTableDataEnd();
|
|
|
|
// End the row in the table.
|
|
|
|
m_pWebPage->printTableRowEnd();
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine outputs a row that has a label in column one and a
|
|
binary value in column two.
|
|
****************************************************************************/
|
|
void F_QueryFormatter::outputBinaryRow(
|
|
const char * pszLabel,
|
|
FLMBYTE * pucValue,
|
|
FLMUINT uiValueLen,
|
|
eColorType eLabelColor,
|
|
eColorType eValueColor)
|
|
{
|
|
// Start a new row in the table
|
|
|
|
m_pWebPage->printTableRowStart();
|
|
|
|
outputLabel( pszLabel, eLabelColor);
|
|
|
|
// Start a new column for the binary value
|
|
|
|
m_pWebPage->printTableDataStart();
|
|
|
|
// Force the color to be output
|
|
|
|
changeColor( eValueColor, TRUE);
|
|
|
|
outputBinary( pucValue, uiValueLen, eValueColor);
|
|
|
|
// End the data column
|
|
|
|
m_pWebPage->printTableDataEnd();
|
|
|
|
// End the row in the table.
|
|
|
|
m_pWebPage->printTableRowEnd();
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine formats the sub-query statistics.
|
|
****************************************************************************/
|
|
void F_QueryFormatter::outputSubqueryStats(
|
|
HRequest * pHRequest,
|
|
F_WebPage * pWebPage,
|
|
SUBQUERY * pSubQuery
|
|
)
|
|
{
|
|
FLMBYTE * pucFromKey = NULL;
|
|
FLMUINT uiFromKeyLen;
|
|
FLMBYTE * pucUntilKey = NULL;
|
|
FLMUINT uiUntilKeyLen;
|
|
FLMBOOL bUntilKeyExclusive;
|
|
|
|
m_pWebPage = pWebPage;
|
|
m_pHRequest = pHRequest;
|
|
m_bSingleLineOnly = FALSE;
|
|
m_eCurrColor = FLM_CURRENT_COLOR;
|
|
|
|
// Begin the table
|
|
|
|
m_pWebPage->printTableStart( "Subquery Statistics", 2);
|
|
|
|
// Output the statistics
|
|
|
|
switch (pSubQuery->OptInfo.eOptType)
|
|
{
|
|
case QOPT_USING_INDEX:
|
|
outputStringRow( "OPTIMIZATION",
|
|
"Using Index",
|
|
Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
|
|
outputUINTRow( "Index",
|
|
pSubQuery->OptInfo.uiIxNum,
|
|
Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
|
|
outputYesNoRow( "Key Match",
|
|
pSubQuery->OptInfo.bDoKeyMatch);
|
|
|
|
outputYesNoRow( "Record Match",
|
|
pSubQuery->OptInfo.bDoRecMatch);
|
|
|
|
pucFromKey = NULL;
|
|
pucUntilKey = NULL;
|
|
if (RC_OK( pSubQuery->pFSIndexCursor->getFirstLastKeys(
|
|
&pucFromKey, &uiFromKeyLen,
|
|
&pucUntilKey, &uiUntilKeyLen,
|
|
&bUntilKeyExclusive)))
|
|
{
|
|
// Show the from key
|
|
|
|
outputUINTRow( "From Key Length", uiFromKeyLen,
|
|
Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
|
|
outputBinaryRow( "From Key", pucFromKey,
|
|
uiFromKeyLen, Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
|
|
// Show the until key.
|
|
|
|
outputUINTRow( "Until Key Length",
|
|
uiUntilKeyLen,
|
|
Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
|
|
outputYesNoRow( "Until Key Exclusive",
|
|
bUntilKeyExclusive);
|
|
|
|
outputBinaryRow( "Until Key", pucUntilKey,
|
|
uiUntilKeyLen, Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
|
|
f_free( &pucFromKey);
|
|
f_free( &pucUntilKey);
|
|
}
|
|
break;
|
|
|
|
case QOPT_USING_PREDICATE:
|
|
outputStringRow( "OPTIMIZATION",
|
|
"Using Embedded Predicate",
|
|
Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
break;
|
|
|
|
case QOPT_SINGLE_RECORD_READ:
|
|
outputStringRow( "OPTIMIZATION",
|
|
"Using Single Record Read",
|
|
Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
|
|
outputUINTRow( "DRN To Read",
|
|
pSubQuery->OptInfo.uiDrn,
|
|
Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
break;
|
|
|
|
case QOPT_PARTIAL_CONTAINER_SCAN:
|
|
outputStringRow( "OPTIMIZATION",
|
|
"Using Partial Container Scan",
|
|
Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
|
|
//VISIT: Output from and until DRNs - need a method from
|
|
//pSubQuery->pFSDataCursor to return them.
|
|
break;
|
|
|
|
case QOPT_FULL_CONTAINER_SCAN:
|
|
outputStringRow( "OPTIMIZATION",
|
|
"Using Full Container Scan",
|
|
Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
break;
|
|
|
|
default:
|
|
outputStringRow( "OPTIMIZATION",
|
|
"Using Unknown",
|
|
Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
break;
|
|
}
|
|
|
|
outputStringRow( "STATISTICS", "",
|
|
Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
|
|
outputUINTRow( "Container",
|
|
pSubQuery->SQStatus.uiContainerNum,
|
|
Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
|
|
outputUINTRow( "Records Matched",
|
|
pSubQuery->SQStatus.uiMatchedCnt,
|
|
Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
|
|
if (pSubQuery->SQStatus.uiNumRejectedByCallback)
|
|
{
|
|
outputUINTRow( "Rejected By Callback",
|
|
pSubQuery->SQStatus.uiNumRejectedByCallback,
|
|
Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
}
|
|
|
|
if (pSubQuery->SQStatus.uiDupsEliminated)
|
|
{
|
|
outputUINTRow( "Duplicates Eliminated",
|
|
pSubQuery->SQStatus.uiDupsEliminated,
|
|
Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
}
|
|
|
|
if (pSubQuery->SQStatus.uiKeysTraversed ||
|
|
pSubQuery->SQStatus.uiKeysRejected)
|
|
{
|
|
outputUINTRow( "Keys Traversed",
|
|
pSubQuery->SQStatus.uiKeysTraversed,
|
|
Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
|
|
outputUINTRow( "Keys Rejected",
|
|
pSubQuery->SQStatus.uiKeysRejected,
|
|
Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
}
|
|
|
|
if (pSubQuery->SQStatus.uiRefsTraversed ||
|
|
pSubQuery->SQStatus.uiRefsRejected)
|
|
{
|
|
outputUINTRow( "References Traversed",
|
|
pSubQuery->SQStatus.uiRefsTraversed,
|
|
Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
|
|
outputUINTRow( "References Rejected",
|
|
pSubQuery->SQStatus.uiRefsRejected,
|
|
Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
}
|
|
|
|
if (pSubQuery->SQStatus.uiRecsFetchedForEval ||
|
|
pSubQuery->SQStatus.uiRecsRejected ||
|
|
pSubQuery->SQStatus.uiRecsNotFound)
|
|
{
|
|
outputUINTRow( "Records Fetched",
|
|
pSubQuery->SQStatus.uiRecsFetchedForEval,
|
|
Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
|
|
outputUINTRow( "Records Rejected",
|
|
pSubQuery->SQStatus.uiRecsRejected,
|
|
Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
|
|
outputUINTRow( "Records Not Found",
|
|
pSubQuery->SQStatus.uiRecsNotFound,
|
|
Q_LABEL_COLOR, Q_PARAM_COLOR);
|
|
}
|
|
|
|
// End the table
|
|
|
|
m_uiRowCount = 0;
|
|
m_pWebPage->printTableEnd();
|
|
|
|
// Free allocated memory
|
|
|
|
if( pucFromKey)
|
|
{
|
|
f_free( &pucFromKey);
|
|
}
|
|
|
|
if( pucUntilKey)
|
|
{
|
|
f_free( &pucUntilKey);
|
|
}
|
|
}
|