", 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);
}
}