//------------------------------------------------------------------------- // 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, "", szTemp); // Format query - up to 80 viewable characters. qf.formatQuery( m_pHRequest, this, pCursor, TRUE, 80); // Close the hyperlink fnPrintf( m_pHRequest, ""); // 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 = ""; break; case FLM_BLUE: pszColor = ""; break; case FLM_GREEN: pszColor = ""; break; case FLM_CYAN: //VISIT: Redo this one pszColor = ""; break; case FLM_RED: pszColor = ""; break; case FLM_MAGENTA: pszColor = ""; break; case FLM_BROWN: //VISIT: Redo this one pszColor = ""; break; case FLM_LIGHTGRAY: pszColor = ""; break; case FLM_DARKGRAY: //VISIT: Redo this one pszColor = ""; break; case FLM_LIGHTBLUE: //VISIT: Redo this one pszColor = ""; break; case FLM_LIGHTGREEN: //VISIT: Redo this one pszColor = ""; break; case FLM_LIGHTCYAN: //VISIT: Redo this one pszColor = ""; break; case FLM_LIGHTRED: //VISIT: Redo this one pszColor = ""; break; case FLM_LIGHTMAGENTA: //VISIT: Redo this one pszColor = ""; break; case FLM_YELLOW: pszColor = ""; break; case FLM_WHITE: pszColor = ""; 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 "" if the binary value is empty. // Otherwise output as hex values. if (!uiBufLen) { appendString( "", 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.ui32Val); 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.i32Val); 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( "", 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], "", gv_FlmSysData.HttpConfigParms.pszURLString, pszURL, &pszURL [20]); outputStr( &pszURL [40]); appendString( "{OptInfo & Stats}", FLM_RED, TRUE); outputStr( ""); 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( "
");

		if (!m_bSingleLineOnly)
		{
			appendString( "Query Criteria: ", Q_LABEL_COLOR);
		}
		if (!pCursor->pSubQueryList)
		{
			appendString( "", 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( "
"); } } /**************************************************************************** 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, "
Query is no longer in the table
\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, "
SubQuery is no longer in the query!
\n"); } else { // Output subquery statistics qf.outputSubqueryStats( m_pHRequest, this, pSubQuery); } } else { fnPrintf( m_pHRequest, "
Query is no longer in the table
\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); } }