Files
mars-flaim/flaim/src/imonsel.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

1389 lines
33 KiB
C++

//-------------------------------------------------------------------------
// Desc: Class for doing queries via web pages - for monitoring stuff.
// Tabs: 3
//
// Copyright (c) 2002-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: imonsel.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $
//-------------------------------------------------------------------------
#include "flaimsys.h"
#define MAX_RECORDS_TO_OUTPUT 100
#define DRN_LIST_INCREASE_SIZE 4096
#define SELECT_FORM_NAME "SelectForm"
#define QUERY_CRITERIA_LEN "querycriterialen"
#define QUERY_CRITERIA "querycriteria"
FSTATIC RCODE queryStatusCB(
FLMUINT uiStatusType,
void * pvParm1,
void * pvParm2,
void * pvUserData);
FSTATIC RCODE imonDoQuery(
IF_Thread * pThread);
/****************************************************************************
Desc: Prints the web page for running queries.
****************************************************************************/
RCODE F_SelectPage::display(
FLMUINT uiNumParams,
const char ** ppszParams)
{
RCODE rc = FERR_OK;
const char * pszErrType = NULL;
RCODE runRc = FERR_OK;
F_Session * pFlmSession = m_pFlmSession;
HFDB hDb;
FLMUINT uiContainer;
FLMUINT uiIndex;
char szTmp [32];
char * pszTmp;
char * pszQueryCriteria = NULL;
char szQueryCriteria [100];
char * pszOperation = NULL;
F_NameTable * pNameTable = NULL;
FLMBOOL bPerformQuery = FALSE;
FLMBOOL bDoDelete = FALSE;
FLMBOOL bStopQuery = FALSE;
FLMBOOL bAbortQuery = FALSE;
HFCURSOR hCursor = HFCURSOR_NULL;
FLMUINT uiQueryThreadId;
QUERY_STATUS QueryStatus;
void * pvHttpSession;
FLMUINT uiCriteriaLen;
FLMUINT uiSize;
char szDbKey[ F_SESSION_DB_KEY_LEN];
QueryStatus.bQueryRunning = FALSE;
QueryStatus.bHaveQueryStatus = FALSE;
// Check the FLAIM session
if( !pFlmSession)
{
rc = RC_SET( m_uiSessionRC);
goto ReportErrorExit;
}
// Get the database handle, if any
if( RC_BAD( rc = getDatabaseHandleParam( uiNumParams,
ppszParams, pFlmSession, &hDb, szDbKey)))
{
goto ReportErrorExit;
}
if (RC_BAD( rc = pFlmSession->getNameTable( hDb, &pNameTable)))
{
goto ReportErrorExit;
}
// Get the container, if any - look in the form first - because
// it can be in both the form and the header. The one in the form
// takes precedence over the one in the header.
szTmp [0] = '\0';
uiContainer = 0;
pszTmp = &szTmp [0];
if (RC_BAD( getFormValueByName( "container",
&pszTmp, sizeof( szTmp), NULL)))
{
if( RC_BAD( ExtractParameter( uiNumParams, ppszParams,
"container", sizeof( szTmp), szTmp)))
{
szTmp [0] = 0;
}
}
if (szTmp[ 0])
{
uiContainer = f_atoud( szTmp);
}
// Get the index, if any - look in the form first - because
// it can be in both the form and the header. The one in the form
// takes precedence over the one in the header.
szTmp [0] = '\0';
uiIndex = FLM_SELECT_INDEX;
pszTmp = &szTmp [0];
if (RC_BAD( getFormValueByName( "index",
&pszTmp, sizeof( szTmp), NULL)))
{
if( RC_BAD( ExtractParameter( uiNumParams, ppszParams,
"index", sizeof( szTmp), szTmp)))
{
szTmp [0] = 0;
}
}
if (szTmp[ 0])
{
uiIndex = f_atoud( szTmp);
}
// Get the query, as constructed so far.
if (getFormValueByName( "querycriteria",
&pszQueryCriteria, 0, NULL) == 0)
{
if( pszQueryCriteria && *pszQueryCriteria)
{
fcsDecodeHttpString( pszQueryCriteria);
}
else if (!pszQueryCriteria)
{
szQueryCriteria[0] = 0;
pszQueryCriteria = &szQueryCriteria [0];
}
// Store the criteria into the session.
if (gv_FlmSysData.HttpConfigParms.fnAcquireSession &&
((pvHttpSession = fnAcquireSession()) != NULL))
{
uiCriteriaLen = f_strlen( pszQueryCriteria) + 1;
fnSetSessionValue( pvHttpSession,
QUERY_CRITERIA_LEN, &uiCriteriaLen, sizeof( uiCriteriaLen));
fnSetSessionValue( pvHttpSession,
QUERY_CRITERIA, pszQueryCriteria, (FLMSIZET)uiCriteriaLen);
fnReleaseSession( pvHttpSession);
}
}
else
{
// See if the query criteria was stored in the http session.
if (gv_FlmSysData.HttpConfigParms.fnAcquireSession &&
((pvHttpSession = fnAcquireSession()) != NULL))
{
uiSize = sizeof( uiCriteriaLen);
if (fnGetSessionValue( pvHttpSession,
QUERY_CRITERIA_LEN, (void *)&uiCriteriaLen,
(FLMSIZET *)&uiSize) == 0)
{
if (uiCriteriaLen <= sizeof( szQueryCriteria))
{
pszQueryCriteria = &szQueryCriteria [0];
}
else
{
if (RC_BAD( f_alloc( uiCriteriaLen, &pszQueryCriteria)))
{
pszQueryCriteria = NULL;
}
}
if (pszQueryCriteria)
{
if (fnGetSessionValue( pvHttpSession, QUERY_CRITERIA,
pszQueryCriteria,
(FLMSIZET *)&uiCriteriaLen) != 0)
{
if (pszQueryCriteria != &szQueryCriteria [0])
{
f_free( &pszQueryCriteria);
}
}
}
}
fnReleaseSession( pvHttpSession);
}
}
// Get the value of the Operation field, if present.
getFormValueByName( "Operation",
&pszOperation, 0, NULL);
if( pszOperation)
{
if (f_stricmp( pszOperation, OPERATION_QUERY) == 0)
{
bPerformQuery = TRUE;
}
else if (f_stricmp( pszOperation, OPERATION_DELETE) == 0)
{
bPerformQuery = TRUE;
bDoDelete = TRUE;
}
else if (f_stricmp( pszOperation, OPERATION_STOP) == 0)
{
bStopQuery = TRUE;
}
else if (f_stricmp( pszOperation, OPERATION_ABORT) == 0)
{
bStopQuery = TRUE;
bAbortQuery = TRUE;
}
}
// See if we had a query running. Get the query object ID
// if any.
szTmp [0] = '\0';
uiQueryThreadId = 0;
if (RC_OK( ExtractParameter( uiNumParams, ppszParams,
"Running", sizeof( szTmp), szTmp)))
{
if (szTmp [0])
{
uiQueryThreadId = f_atoud( szTmp);
QueryStatus.bQueryRunning = TRUE;
}
}
if (bPerformQuery)
{
// Better not have both bQueryRunning and bPerformQuery set!
flmAssert( !QueryStatus.bQueryRunning);
// Parse the query.
if (RC_BAD( runRc = parseQuery( hDb, uiContainer, uiIndex,
pNameTable,
pszQueryCriteria, &hCursor)))
{
pszErrType = "PARSING QUERY";
}
else if (RC_BAD( runRc = runQuery( hDb, uiContainer, uiIndex,
hCursor, bDoDelete, &uiQueryThreadId)))
{
pszErrType = "RUNNING QUERY";
}
else
{
QueryStatus.bQueryRunning = TRUE;
// Set hCursor to null because the query thread will destroy it when
// it finishes.
hCursor = HFCURSOR_NULL;
}
}
// Stop the query, if requested, or get the query data.
if (QueryStatus.bQueryRunning)
{
// Give query a fifth of a second to complete. If it doesn't complet
// in this amount of time, we will catch it on a refresh.
f_sleep( 200);
// getQueryStatus could change QueryStatus.bQueryRunning
// to FALSE.
getQueryStatus( uiQueryThreadId, bStopQuery, bAbortQuery, &QueryStatus);
}
// Output the web page.
if (!QueryStatus.bQueryRunning && QueryStatus.bHaveQueryStatus)
{
// If we have query results, output a page for viewing/editing them.
printDocStart( "Query Results");
}
else if (!QueryStatus.bQueryRunning)
{
printDocStart( "Run Query");
if (pszErrType)
{
fnPrintf( m_pHRequest,
"<br><font color=\"Red\">ERROR %04X (%s) %s</font><br><br>\n",
(unsigned)runRc, FlmErrorString( runRc), pszErrType);
}
}
else
{
stdHdr();
fnPrintf( m_pHRequest, HTML_DOCTYPE);
fnPrintf( m_pHRequest, "<html>\n"
"<head>\n");
printRecordStyle();
printStyle();
// Output html that will cause a refresh to occur.
fnPrintf( m_pHRequest,
"<META http-equiv=\"refresh\" content=\"1; "
"url=%s/select?Running=%u&dbhandle=%s&container=%u&index=%u\">"
"<TITLE>Query Status</TITLE>\n",
m_pszURLString,
(unsigned)uiQueryThreadId, szDbKey, (unsigned)uiContainer,
(unsigned)uiIndex);
fnPrintf( m_pHRequest, "</head>\n"
"<body>\n");
}
// Output the form for entering the query criteria
// and query status, if the query is currently running.
outputSelectForm( hDb, szDbKey, uiContainer, uiIndex,
QueryStatus.bQueryRunning,
uiQueryThreadId, pNameTable,
pszQueryCriteria, &QueryStatus);
// Output any query status information we have.
if (QueryStatus.bHaveQueryStatus)
{
outputQueryStatus( hDb, szDbKey, uiContainer,
pNameTable, &QueryStatus);
}
// End the document
printDocEnd();
Exit:
fnEmit();
if (pszQueryCriteria && pszQueryCriteria != &szQueryCriteria [0])
{
f_free( &pszQueryCriteria);
}
if (pszOperation)
{
f_free( &pszOperation);
}
if (hCursor != HFCURSOR_NULL)
{
FlmCursorFree( &hCursor);
}
return( FERR_OK);
ReportErrorExit:
printErrorPage( rc);
goto Exit;
}
/****************************************************************************
Desc: Output the form for the user to input a query. If the query is
running, the query criteria cannot be changed.
****************************************************************************/
void F_SelectPage::outputSelectForm(
HFDB hDb,
const char * pszDbKey,
FLMUINT uiContainer,
FLMUINT uiIndex,
FLMBOOL bQueryRunning,
FLMUINT uiQueryThreadId,
F_NameTable * pNameTable,
const char * pszQueryCriteria,
QUERY_STATUS * pQueryStatus)
{
char * pszName;
char szName [128];
fnPrintf( m_pHRequest, "<form name=\""
SELECT_FORM_NAME "\" type=\"submit\" "
"method=\"post\" action=\"%s/select", m_pszURLString);
if (bQueryRunning)
{
fnPrintf( m_pHRequest, "?Running=%u&",
(unsigned)uiQueryThreadId);
}
else
{
fnPrintf( m_pHRequest, "?");
}
fnPrintf( m_pHRequest, "dbhandle=%s&container=%u&index=%u\">\n",
pszDbKey, (unsigned)uiContainer, (unsigned)uiIndex);
// Output the database name
printStartCenter();
fnPrintf( m_pHRequest, "Database&nbsp;");
printEncodedString( ((FDB *)hDb)->pFile->pszDbPath, HTML_ENCODING);
printEndCenter( FALSE);
fnPrintf( m_pHRequest, "<br>\n");
// Output container name or a pulldown list to select a container.
printStartCenter();
fnPrintf( m_pHRequest, "Container&#%u;&nbsp;", (unsigned)':');
if (pQueryStatus->bQueryRunning)
{
switch (uiContainer)
{
case FLM_DATA_CONTAINER:
pszName = (char *)"Data";
break;
case FLM_DICT_CONTAINER:
pszName = (char *)"Dictionary";
break;
case FLM_TRACKER_CONTAINER:
pszName = (char *)"Tracker";
break;
default:
if (!pNameTable ||
!pNameTable->getFromTagNum( uiContainer, NULL,
szName,
sizeof( szName)))
{
f_sprintf( szName, "Cont_%u", (unsigned)uiContainer);
}
pszName = &szName [0];
break;
}
printEncodedString( pszName, HTML_ENCODING);
fnPrintf( m_pHRequest, " (%u)", (unsigned)uiContainer);
}
else
{
printContainerPulldown( pNameTable, uiContainer);
}
printEndCenter( FALSE);
fnPrintf( m_pHRequest, "<br>\n");
// Output pulldown list to select an index
if (!pQueryStatus->bQueryRunning)
{
printStartCenter();
fnPrintf( m_pHRequest, "Index&#%u;&nbsp;", (unsigned)':');
printIndexPulldown( pNameTable, uiIndex, TRUE, TRUE);
printEndCenter( FALSE);
fnPrintf( m_pHRequest, "<br>\n");
}
// Output text box for query.
printStartCenter();
fnPrintf( m_pHRequest, "<textarea name=\"querycriteria\" wrap=off rows=4 cols=80");
if (pQueryStatus->bQueryRunning)
{
fnPrintf( m_pHRequest, " readonly");
}
fnPrintf( m_pHRequest, ">\n");
if (pszQueryCriteria && *pszQueryCriteria)
{
printEncodedString( pszQueryCriteria, HTML_ENCODING);
}
fnPrintf( m_pHRequest, "</textarea>");
printEndCenter( FALSE);
fnPrintf( m_pHRequest, "<br>\n");
// Output a text box for the field list - so user can copy and paste
// field names into the query box.
if (!pQueryStatus->bQueryRunning && pNameTable)
{
FLMUINT uiNextPos;
FLMUINT uiFieldNum;
FLMUINT uiType;
printStartCenter();
fnPrintf( m_pHRequest,
"<textarea name=\"ListOfFields\" wrap=off rows=8 cols=80>\n");
uiNextPos = 0;
while (pNameTable->getNextTagNameOrder( &uiNextPos, NULL,
szName, sizeof( szName), &uiFieldNum, &uiType))
{
if( uiType != FLM_FIELD_TAG)
{
continue;
}
printEncodedString( szName, HTML_ENCODING);
fnPrintf( m_pHRequest, " (%u)\n", (unsigned)uiFieldNum);
}
fnPrintf( m_pHRequest, "</textarea>");
printEndCenter();
fnPrintf( m_pHRequest, "<br>\n");
}
// Output the setOperation function
printSetOperationScript();
printStartCenter();
if (!pQueryStatus->bQueryRunning)
{
// If we are not running a query, add a Perform Query button
// and a Query & Delete button.
printOperationButton( SELECT_FORM_NAME,
"Perform Query", OPERATION_QUERY);
printSpaces( 1);
printOperationButton( SELECT_FORM_NAME,
"Query & Delete", OPERATION_DELETE);
}
else
{
// Output a stop button if not doing a delete. Otherwise, output a stop and
// commit button and a stop and abort button.
if (!pQueryStatus->bDoDelete)
{
printOperationButton( SELECT_FORM_NAME,
"Stop Query", OPERATION_STOP);
}
else
{
printOperationButton( SELECT_FORM_NAME,
"Stop Query & Commit Transaction", OPERATION_STOP);
printSpaces( 1);
printOperationButton( SELECT_FORM_NAME,
"Stop Query & Abort Transaction", OPERATION_ABORT);
}
}
printEndCenter( TRUE);
// Close the form
fnPrintf( m_pHRequest, "</form>\n");
}
/****************************************************************************
Desc: Outputs information on a query that is either running or has
finished.
****************************************************************************/
void F_SelectPage::outputQueryStatus(
HFDB hDb,
const char * pszDbKey,
FLMUINT uiContainer,
F_NameTable * pNameTable,
QUERY_STATUS * pQueryStatus)
{
RCODE rc;
char szName[ 128];
FLMUINT uiMax;
FLMUINT uiLoop;
FlmRecord * pRec = NULL;
FLMUINT uiContext;
fnPrintf( m_pHRequest, "<br>\n");
// Output index optimization information
printStartCenter();
fnPrintf( m_pHRequest, "Index ");
if (pQueryStatus->uiIndex == FLM_SELECT_INDEX)
{
fnPrintf( m_pHRequest, "(Selected by DB)&#%u; ", (unsigned)':');
}
else
{
fnPrintf( m_pHRequest, "(Set by User)&#%u; ", (unsigned)':');
}
if (pQueryStatus->uiIndexInfo == HAVE_NO_INDEX)
{
fnPrintf( m_pHRequest, "None");
}
else
{
if (!pNameTable ||
!pNameTable->getFromTagNum( pQueryStatus->uiOptIndex, NULL,
szName, sizeof( szName)))
{
f_sprintf( (char *)szName, "Index_%u",
(unsigned)pQueryStatus->uiOptIndex);
}
printEncodedString( szName, HTML_ENCODING);
fnPrintf( m_pHRequest, " (%u)", (unsigned)pQueryStatus->uiOptIndex);
if (pQueryStatus->uiIndexInfo == HAVE_MULTIPLE_INDEXES)
{
fnPrintf( m_pHRequest, " (Using multiple indexes)");
}
else if (pQueryStatus->uiIndexInfo == HAVE_ONE_INDEX_MULT_PARTS)
{
fnPrintf( m_pHRequest, " (Multiple subqueries use index)");
}
}
printEndCenter( FALSE);
fnPrintf( m_pHRequest, "<br>\n");
printStartCenter();
if (pQueryStatus->bQueryRunning)
{
printTableStart( "QUERY PROGRESS", 2, 50);
}
else
{
printTableStart( "QUERY RESULTS", 2, 50);
}
// Column headers
printTableRowStart();
if (pQueryStatus->bDoDelete)
{
printColumnHeading( "Records Deleted", JUSTIFY_RIGHT);
}
else
{
printColumnHeading( "Records Matched", JUSTIFY_RIGHT);
}
printColumnHeading( "Processed Count", JUSTIFY_RIGHT);
printTableRowEnd();
if (pQueryStatus->uiProcessedCnt < pQueryStatus->uiDrnCount)
{
pQueryStatus->uiProcessedCnt = pQueryStatus->uiDrnCount;
}
printTableRowStart( TRUE);
printTableDataStart( TRUE, JUSTIFY_RIGHT);
fnPrintf( m_pHRequest, "%u", (unsigned)pQueryStatus->uiDrnCount);
printTableDataEnd();
printTableDataStart( TRUE, JUSTIFY_RIGHT);
fnPrintf( m_pHRequest, "%u", (unsigned)pQueryStatus->uiProcessedCnt);
printTableDataEnd();
printTableRowEnd();
printTableEnd();
printEndCenter( FALSE);
fnPrintf( m_pHRequest, "<br>\n");
// Output the records if the query is done. Then free the data.
if (!pQueryStatus->bQueryRunning && pQueryStatus->puiDrnList)
{
// List the retrieved records. They should not be in there if
// we are doing a delete.
flmAssert( !pQueryStatus->bDoDelete);
// Output up to the first 100 records retrieved. We
// don't output more than one hundred because it will just
// take too long.
printTableStart( "RECORDS RETRIEVED", 1, 100);
printTableEnd();
fnPrintf( m_pHRequest, "<br>\n");
if ((uiMax = pQueryStatus->uiDrnCount) > MAX_RECORDS_TO_OUTPUT)
{
uiMax = MAX_RECORDS_TO_OUTPUT;
}
uiContext = 0;
for (uiLoop = 0; uiLoop < uiMax; uiLoop++)
{
if (RC_BAD( rc = FlmRecordRetrieve( hDb, uiContainer,
pQueryStatus->puiDrnList [uiLoop],
FO_EXACT, &pRec, NULL)))
{
if (rc != FERR_NOT_FOUND)
{
fnPrintf( m_pHRequest,
"<br><font color=\"Red\">ERROR %04X (%s) retrieving "
"record #%u</font><br><br>\n",
(unsigned)rc, FlmErrorString( rc),
(unsigned)pQueryStatus->puiDrnList [uiLoop]);
}
}
else
{
printRecord( pszDbKey, pRec, pNameTable, &uiContext,
TRUE, 0, FO_EXACT);
}
}
f_free( &pQueryStatus->puiDrnList);
}
//Exit:
if (pRec)
{
pRec->Release();
}
}
/****************************************************************************
Desc: Set up a query from a query criteria string.
****************************************************************************/
RCODE F_SelectPage::parseQuery(
HFDB hDb,
FLMUINT uiContainer,
FLMUINT uiIndex,
F_NameTable * pNameTable,
const char * pszQueryCriteria,
HFCURSOR * phCursor)
{
RCODE rc = FERR_OK;
*phCursor = HFCURSOR_NULL;
if (RC_BAD( rc = FlmCursorInit( hDb, uiContainer, phCursor)))
{
goto Exit;
}
if (RC_BAD( rc = FlmCursorConfig( *phCursor, FCURSOR_SET_FLM_IX,
(void *)uiIndex, 0)))
{
goto Exit;
}
if (RC_BAD( rc = FlmParseQuery( *phCursor, pNameTable, pszQueryCriteria)))
{
goto Exit;
}
// Do a final validation on the cursor to ensure it is valid.
if (RC_BAD( rc = FlmCursorValidate( *phCursor)))
{
goto Exit;
}
Exit:
if (RC_BAD( rc) && *phCursor != HFCURSOR_NULL)
{
FlmCursorFree( phCursor);
}
return( rc);
}
/****************************************************************************
Desc: Run a query.
****************************************************************************/
RCODE F_SelectPage::runQuery(
HFDB hDb,
FLMUINT uiContainer,
FLMUINT uiIndex,
HFCURSOR hCursor,
FLMBOOL bDoDelete,
FLMUINT * puiQueryThreadId
)
{
RCODE rc = FERR_OK;
QUERY_STATUS * pQueryStatus = NULL;
IF_Thread * pThread;
FDB * pDb = NULL;
// Open the database for the thread - so it doesn't have
// to worry about the handle going away. The thread will close the
// new handle when it exits.
if (RC_BAD( rc = flmOpenFile( ((FDB *)hDb)->pFile, NULL, NULL, NULL,
0, TRUE, NULL, NULL,
(((FDB *)hDb)->pFile)->pszDbPassword, &pDb)))
{
goto Exit;
}
// Create an object to track the query.
if (RC_BAD( rc = f_calloc( sizeof( QUERY_STATUS), &pQueryStatus)))
{
goto Exit;
}
pQueryStatus->hDb = (HFDB)pDb;
pQueryStatus->uiContainer = uiContainer;
pQueryStatus->uiIndex = uiIndex;
pQueryStatus->hCursor = hCursor;
pQueryStatus->bDoDelete = bDoDelete;
pQueryStatus->bQueryRunning = TRUE;
pQueryStatus->uiLastTimeChecked = FLM_GET_TIMER();
FlmCursorGetConfig( hCursor, FCURSOR_GET_FLM_IX, &pQueryStatus->uiOptIndex,
&pQueryStatus->uiIndexInfo);
// If browser does not check query status at least every 15 seconds, we will
// assume it has gone away and the thread will terminate itself.
pQueryStatus->uiQueryTimeout = FLM_SECS_TO_TIMER_UNITS( 15);
// Start a thread to do the query.
if( RC_BAD( rc = f_threadCreate( &pThread, imonDoQuery,
"WEB QUERY",
gv_uiDbThrdGrp, 1,
(void *)pQueryStatus, (void *)hDb)))
{
goto Exit;
}
*puiQueryThreadId = pThread->getThreadId();
// Set pQueryStatus to NULL so it won't be freed below. The thread
// will free it when it stops.
pQueryStatus = NULL;
// Set pDb to NULL so it won't be closed below. The thread will
// close it when it stops.
pDb = NULL;
Exit:
if (pThread)
{
pThread->Release();
}
if (pQueryStatus)
{
f_free( &pQueryStatus);
}
if (pDb)
{
FlmDbClose( (HFDB *)&pDb);
}
return( rc);
}
/****************************************************************************
Desc: Output the current thread status to the web page.
****************************************************************************/
void F_SelectPage::getQueryStatus(
FLMUINT uiQueryThreadId,
FLMBOOL bStopQuery,
FLMBOOL bAbortQuery,
QUERY_STATUS * pQueryStatus
)
{
FLMUINT uiThreadId;
IF_Thread * pThread = NULL;
QUERY_STATUS * pThreadQueryStatus;
FLMBOOL bMutexLocked = FALSE;
// pQueryStatus->bHaveQueryStatus should be set to FALSE by the caller.
flmAssert( !pQueryStatus->bHaveQueryStatus);
// See if the thread is still running.
f_mutexLock( gv_FlmSysData.hShareMutex);
bMutexLocked = TRUE;
uiThreadId = 0;
for (;;)
{
if (RC_BAD( gv_FlmSysData.pThreadMgr->getNextGroupThread( &pThread,
gv_uiDbThrdGrp, &uiThreadId)))
{
pQueryStatus->bQueryRunning = FALSE;
goto Exit;
}
if (uiThreadId == uiQueryThreadId)
{
// If the app ID is zero, the thread is on its way out or already
// out. Can no longer get thread status.
if (!pThread->getThreadAppId())
{
pQueryStatus->bQueryRunning = FALSE;
goto Exit;
}
// Found thread, get its query data
pThreadQueryStatus = (QUERY_STATUS *)pThread->getParm1();
pThreadQueryStatus->uiLastTimeChecked = FLM_GET_TIMER();
// Tell the thread to stop the query before telling it
// to stop. This is so we can get partial results.
if (bStopQuery)
{
pThreadQueryStatus->bStopQuery = TRUE;
pThreadQueryStatus->bAbortQuery = bAbortQuery;
// Go into a while loop, waiting for the thread
// to finish its query.
while (pThreadQueryStatus->bQueryRunning)
{
f_mutexUnlock( gv_FlmSysData.hShareMutex);
bMutexLocked = FALSE;
f_sleep( 200);
f_mutexLock( gv_FlmSysData.hShareMutex);
bMutexLocked = TRUE;
// If the thread app ID goes to zero, it has been
// told to shut down, and has either already gone
// away or is in the process of doing so, in which
// case pThreadQueryStatus has either already been
// deleted, or will be - so it is not safe to access
// it any more!
if (!pThread->getThreadAppId())
{
pQueryStatus->bQueryRunning = FALSE;
goto Exit;
}
}
}
break;
}
pThread->Release();
pThread = NULL;
}
// Mutex better still be locked at this point.
flmAssert( bMutexLocked);
// If the query is not done, return everything except the DRN list.
// Note that we test pThreadQueryStatus->bQueryRunning BEFORE
// doing the memcpy. This is because puiDrnList is not guaranteed
// to be set until bQueryRunning is FALSE. If bQueryRunning is TRUE,
// we will NULL out whatever got copied into puiDrnList.
if (!pThreadQueryStatus->bQueryRunning)
{
f_memcpy( pQueryStatus, pThreadQueryStatus, sizeof( QUERY_STATUS));
// NULL out the puiDrnList member in the thread's copy of
// the query data so it won't free the list when it exits.
// The caller of this routine must be sure to do it instead!
pThreadQueryStatus->puiDrnList = NULL;
// Need to unlock the mutex so that the thread can stop.
f_mutexUnlock( gv_FlmSysData.hShareMutex);
bMutexLocked = FALSE;
pThread->stopThread();
}
else
{
f_memcpy( pQueryStatus, pThreadQueryStatus, sizeof( QUERY_STATUS));
// NULL out the DRN list and set bQueryRunning to TRUE. This takes
// care of a race condition of pThreadQueryStatus->bQueryRunning getting
// set to FALSE by the query thread after we test it above.
// we make the test on pThreadQueryStatus->bQueryRunning. We will
// simply get that fact next time we get status.
pQueryStatus->bQueryRunning = TRUE;
pQueryStatus->puiDrnList = NULL;
}
pQueryStatus->bHaveQueryStatus = TRUE;
Exit:
if (bMutexLocked)
{
f_mutexUnlock( gv_FlmSysData.hShareMutex);
bMutexLocked = FALSE;
}
if (pThread)
{
pThread->Release();
}
}
/***************************************************************************
Desc: Query status callback.
***************************************************************************/
FSTATIC RCODE queryStatusCB(
FLMUINT uiStatusType,
void * pvParm1,
void *, // pvParm2,
void * pvUserData)
{
RCODE rc = FERR_OK;
FLMUINT uiCurrTime;
switch (uiStatusType)
{
case FLM_SUBQUERY_STATUS:
{
FCURSOR_SUBQUERY_STATUS * pQueryInfo = (FCURSOR_SUBQUERY_STATUS *)pvParm1;
QUERY_STATUS * pQueryStatus = (QUERY_STATUS *)pvUserData;
pQueryStatus->uiProcessedCnt = pQueryInfo->uiProcessedCnt;
uiCurrTime = FLM_GET_TIMER();
if (pQueryStatus->bStopQuery)
{
rc = RC_SET( FERR_USER_ABORT);
goto Exit;
}
else if (FLM_ELAPSED_TIME( uiCurrTime,
pQueryStatus->uiLastTimeChecked) >=
pQueryStatus->uiQueryTimeout)
{
rc = RC_SET( FERR_TIMEOUT);
goto Exit;
}
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Thread to perform a query for a web page.
****************************************************************************/
FSTATIC RCODE imonDoQuery(
IF_Thread * pThread)
{
RCODE rc;
QUERY_STATUS * pQueryStatus = (QUERY_STATUS *)pThread->getParm1();
HFCURSOR hCursor = pQueryStatus->hCursor;
HFDB hDb = pQueryStatus->hDb;
FLMUINT uiContainer = pQueryStatus->uiContainer;
FLMUINT uiDrn;
FLMUINT uiCurrTime;
FLMUINT uiLastTimeSetStatus = 0;
FLMUINT ui5SecsTime;
FLMBOOL bTransStarted = FALSE;
pThread->setThreadStatus( FLM_THREAD_STATUS_INITIALIZING);
ui5SecsTime = FLM_SECS_TO_TIMER_UNITS( 5);
// Start a transaction.
if (pQueryStatus->bDoDelete)
{
rc = FlmDbTransBegin( hDb, FLM_UPDATE_TRANS, FLM_NO_TIMEOUT);
}
else
{
rc = FlmDbTransBegin( hDb, FLM_READ_TRANS, 0);
}
if (RC_BAD( rc))
{
pThread->setThreadStatus( "Trans Error %04X", (unsigned)rc);
pQueryStatus->bQueryRunning = FALSE;
}
else
{
bTransStarted = TRUE;
// Set the cursor callback
(void)FlmCursorConfig( hCursor, FCURSOR_SET_STATUS_HOOK,
(void *)(FLMUINT)queryStatusCB,
(void *)pQueryStatus);
// Disconnect the cursor from its current database handle.
(void)FlmCursorConfig( hCursor, FCURSOR_DISCONNECT, 0, 0);
// Connect the cursor to the database handle passed in.
(void)FlmCursorConfig( hCursor, FCURSOR_SET_HDB, (void *)hDb, 0);
pThread->setThreadStatus( FLM_THREAD_STATUS_RUNNING);
}
for (;;)
{
// See if we should shut down.
if (pThread->getShutdownFlag())
{
pQueryStatus->bQueryRunning = FALSE;
// Transaction will be aborted below
pThread->setThreadStatus( FLM_THREAD_STATUS_TERMINATING);
goto Exit;
}
// See if the browser quit asking for status.
uiCurrTime = FLM_GET_TIMER();
if (FLM_ELAPSED_TIME( uiCurrTime, pQueryStatus->uiLastTimeChecked) >=
pQueryStatus->uiQueryTimeout)
{
if (pQueryStatus->bQueryRunning)
{
pThread->setThreadStatus( "Timed out, Cnt=%u",
(unsigned)pQueryStatus->uiDrnCount);
pQueryStatus->bQueryRunning = FALSE;
}
// Transaction will be aborted below
goto Exit;
}
// If the query is not running, just pause one second at a time
// until we are told to shut down or until we time out.
if (!pQueryStatus->bQueryRunning)
{
f_sleep( 1000);
continue;
}
// See if we should stop the query.
if (pQueryStatus->bStopQuery)
{
Stop_Query:
if (pQueryStatus->bAbortQuery)
{
pThread->setThreadStatus( "User aborted, Cnt=%u",
(unsigned)pQueryStatus->uiDrnCount);
goto Abort_Query;
}
else
{
pThread->setThreadStatus( "User halted, Cnt=%u",
(unsigned)pQueryStatus->uiDrnCount);
goto Commit_Query;
}
}
// Get the next record.
if (RC_BAD( rc = FlmCursorNextDRN( hCursor, &uiDrn)))
{
if (rc == FERR_EOF_HIT || rc == FERR_BOF_HIT || rc == FERR_NOT_FOUND)
{
pThread->setThreadStatus( "Query done, Cnt=%u",
(unsigned)pQueryStatus->uiDrnCount);
// Finish the query. If doing a delete, the transaction
// will be committed.
goto Commit_Query;
}
else if (rc == FERR_USER_ABORT)
{
// Callback forced us to quit.
goto Stop_Query;
}
else if (rc == FERR_TIMEOUT)
{
pThread->setThreadStatus( "Timed out, Cnt=%u",
(unsigned)pQueryStatus->uiDrnCount);
goto Abort_Query;
}
else if (rc == FERR_OLD_VIEW)
{
// Better not happen if we are in an update transaction!
flmAssert( !pQueryStatus->bDoDelete);
// Start a new read transaction.
FlmDbTransAbort( hDb);
bTransStarted = FALSE;
if (RC_BAD( rc = FlmDbTransBegin( hDb, FLM_READ_TRANS, 0)))
{
pThread->setThreadStatus( "Trans Error %04X, Cnt=%u",
(unsigned)rc, (unsigned)pQueryStatus->uiDrnCount);
goto Abort_Query;
}
bTransStarted = TRUE;
}
else
{
pThread->setThreadStatus( "Read Error %04X, Cnt=%u",
(unsigned)rc, (unsigned)pQueryStatus->uiDrnCount);
goto Abort_Query;
}
}
else // FERR_OK
{
// If we are not deleting, add DRN to the list of DRNs.
if (!pQueryStatus->bDoDelete)
{
if (pQueryStatus->uiDrnCount == pQueryStatus->uiDrnListSize)
{
FLMUINT * puiTmp;
if( RC_BAD( rc = f_alloc( sizeof( FLMUINT) *
(pQueryStatus->uiDrnListSize + DRN_LIST_INCREASE_SIZE),
&puiTmp)))
{
pThread->setThreadStatus( "Mem Alloc Error, Cnt=%u",
(unsigned)pQueryStatus->uiDrnCount);
goto Abort_Query;
}
if (pQueryStatus->puiDrnList)
{
f_memcpy( puiTmp, pQueryStatus->puiDrnList,
sizeof( FLMUINT) * pQueryStatus->uiDrnCount);
f_free( &pQueryStatus->puiDrnList);
}
pQueryStatus->puiDrnList = puiTmp;
pQueryStatus->uiDrnListSize += DRN_LIST_INCREASE_SIZE;
}
pQueryStatus->puiDrnList [pQueryStatus->uiDrnCount] = uiDrn;
pQueryStatus->uiDrnCount++;
}
else
{
pQueryStatus->uiDrnCount++;
// Delete the record.
if (RC_BAD( rc = FlmRecordDelete( hDb, uiContainer, uiDrn, 0)))
{
if (rc != FERR_NOT_FOUND)
{
pThread->setThreadStatus( "Delete Error %04X, Cnt=%u",
(unsigned)rc, (unsigned)pQueryStatus->uiDrnCount);
goto Abort_Query;
}
}
}
// Update thread status every 5 seconds
uiCurrTime = FLM_GET_TIMER();
if (FLM_ELAPSED_TIME( uiCurrTime, uiLastTimeSetStatus) >=
ui5SecsTime)
{
if (pQueryStatus->uiProcessedCnt < pQueryStatus->uiDrnCount)
{
pQueryStatus->uiProcessedCnt = pQueryStatus->uiDrnCount;
}
pThread->setThreadStatus( "Found %u, Processed %u",
(unsigned)pQueryStatus->uiDrnCount,
(unsigned)pQueryStatus->uiProcessedCnt);
uiLastTimeSetStatus = uiCurrTime;
}
}
continue;
Abort_Query:
// This label is jumped to whenever a condition occurs which
// causes the query to be stopped - error, user stops, etc.
// Free the cursor.
FlmCursorFree( &hCursor);
// Abort the transaction if we still have one going.
if (bTransStarted)
{
(void)FlmDbTransAbort( hDb);
bTransStarted = FALSE;
}
// Close the database.
FlmDbClose( &hDb);
// Continue until told to shut down or until we
// timeout.
pQueryStatus->bQueryRunning = FALSE;
continue;
Commit_Query:
// Free the cursor.
FlmCursorFree( &hCursor);
bTransStarted = FALSE;
if (pQueryStatus->bDoDelete)
{
if (RC_BAD( rc = FlmDbTransCommit( hDb)))
{
pThread->setThreadStatus( "Commit Error %04X, Cnt=%u",
(unsigned)rc, (unsigned)pQueryStatus->uiDrnCount);
}
}
else
{
// Only a read transaction - don't care if committed or aborted
(void)FlmDbTransCommit( hDb);
}
// Close the database.
FlmDbClose( &hDb);
pQueryStatus->bQueryRunning = FALSE;
// Continue until told to shut down or until we
// timeout.
continue;
}
Exit:
// Free the cursor.
if (hCursor != HFCURSOR_NULL)
{
FlmCursorFree( &hCursor);
}
// Abort the transaction if we still have one going.
if (bTransStarted)
{
(void)FlmDbTransAbort( hDb);
}
// Close the database.
if (hDb != HFDB_NULL)
{
FlmDbClose( &hDb);
}
// Set the thread's app ID to 0, so that it will not
// be found now that the thread is terminating (we don't
// want getQueryStatus() to find the thread).
pThread->setThreadAppId( 0);
// Free the query status. Must do inside mutex lock so
// that it doesn't go away after getQueryStatus finds the
// thread.
f_mutexLock( gv_FlmSysData.hShareMutex);
if (pQueryStatus->puiDrnList)
{
f_free( &pQueryStatus->puiDrnList);
}
f_free( &pQueryStatus);
f_mutexUnlock( gv_FlmSysData.hShareMutex);
return( FERR_OK);
}