Files
mars-flaim/flaim/src/imonqury.cpp
ahodgkinson 7de8b6be39 Ported FLAIM to FTK.
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@509 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2006-06-05 22:59:36 +00:00

1915 lines
41 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_INT32_VAL:
f_sprintf( szBuf, "%d", (int)pQNode->pQAtom->val.iVal);
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);
}
}