git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@509 0109f412-320b-0410-ab79-c3e0c5ffbbe6
1389 lines
33 KiB
C++
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 ");
|
|
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; ", (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; ", (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);
|
|
}
|