/* r4relate.c (c)Copyright Sequiter Software Inc., 1988-1996. All rights reserved. */ #include "d4all.h" #ifdef __TURBOC__ #pragma hdrstop #endif #ifdef S4CLIENT static int relate4clientInit( RELATE4 * ) ; #else static int relate4currentIsChild( RELATE4 * ) ; static int relate4parent( RELATE4 *, RELATE4 * ) ; static int relate4nextRelationList( RELATION4 *, int ) ; static int relate4prevRecordInScan( RELATE4 * ) ; static int relate4prevRelationList( RELATION4 *, int ) ; #endif static int relate4initRelate( RELATE4 *, RELATION4 *, DATA4 *, CODE4 *, int ) ; static int relate4lookup( RELATE4 *, const char ) ; static int relate4readRest( RELATE4 *, char ) ; static void relate4setNotRead( RELATE4 * ) ; static void relate4sortFree( RELATION4 *, const int ) ; static int relate4sortGetRecord( RELATION4 *, const long ) ; static int relate4sortNextRecord( RELATION4 * ) ; static int relate4sortPrevRecord( RELATION4 * ) ; #ifndef S4CLIENT static int relate4topInit( RELATE4 * ) ; static int f4flagIsSetFlip( F4FLAG *flagPtr, const unsigned long r ) { if ( flagPtr->flags == 0 ) return 1 ; if ( flagPtr->isFlip ) return ! f4flagIsSet( flagPtr, r ) ; else return f4flagIsSet( flagPtr, r ) ; } /* returns the position of the next flipped flag in the flag set - start at r */ static unsigned long f4flagGetNextFlip( F4FLAG *f4, const unsigned long r, const char direction ) { unsigned char cFlag ; unsigned long lowVal, onVal, highVal ; char i ; #ifdef E4PARM_LOW if ( direction != -1 && direction != 1 ) return error4( 0, e4parm, E90812 ) ; #endif onVal = r ; if ( f4->flags == 0 || r > f4->numFlags ) return 0 ; lowVal = (unsigned long)( r & 0x7 ) ; highVal = (unsigned long)( r >> 3 ) ; if ( (int)direction == -1 ) { cFlag = (unsigned char)( f4->flags[highVal] ) ; if ( f4->isFlip ) cFlag = (unsigned char) ~cFlag ; cFlag = (unsigned char)( (unsigned char)( cFlag << ( 7 - lowVal ) ) >> ( 7 - lowVal )) ; onVal += ( 7 - lowVal ) ; if ( cFlag == 0 ) for( ; cFlag == 0 ; onVal -= 8 ) { if ( highVal-- <= 1 ) /* if was zero, or is now zero */ { if ( f4->flags[0] == 0 ) return r ; cFlag = f4->flags[0] ; if ( f4->isFlip ) cFlag = (unsigned char) ~cFlag ; onVal -= 8 ; /* for sure if highVal == 0, else?? */ break ; } cFlag = f4->flags[highVal] ; if ( f4->isFlip ) cFlag = (unsigned char) ~cFlag ; } for( i = 7 ; (int)i >= 0 ; i--, onVal-- ) if ( cFlag & ( 0x01 << i ) ) break ; return (r - onVal) ; } else { cFlag = (unsigned char)f4->flags[highVal] ; if ( f4->isFlip ) cFlag = (unsigned char) ~cFlag ; cFlag = (unsigned char) (cFlag >> lowVal) ; if ( cFlag == 0 ) { onVal -= lowVal ; for( ; cFlag == 0 ; onVal += 8 ) { if ( onVal >= f4->numFlags ) return (f4->numFlags - r + 1) ; cFlag = f4->flags[++highVal] ; if ( f4->isFlip ) cFlag = (unsigned char) ~cFlag ; } } for( i = 0 ; i <= 7 ; i++, onVal++ ) if ( cFlag & ( 0x01 << i ) ) break ; return (onVal - r) ; } } int r4dataListAdd( LIST4 *l4, DATA4 *data, RELATE4 *relate ) { R4DATA_LIST *r4 ; CODE4 *c4 ; c4 = relate->codeBase ; if ( error4code( c4 ) < 0 ) return -1 ; if ( c4->relateDataListMemory == 0 ) { c4->relateDataListMemory = mem4create( c4, 10, sizeof( R4DATA_LIST ), 10, 0 ) ; if ( c4->relateDataListMemory == 0 ) return 0 ; } r4 = (R4DATA_LIST *)mem4alloc( c4->relateDataListMemory ) ; if ( r4 == 0 ) return -1 ; r4->data = data ; r4->relate = relate ; l4add( l4, r4 ) ; return 0 ; } int r4dataListFind( LIST4 *l4, RELATE4 *r4 ) { R4DATA_LIST *link ; for ( link = 0 ;; ) { link = (R4DATA_LIST *)l4next( l4, link ) ; if ( link == 0 ) return 0 ; if ( link->relate == r4 ) return 1 ; } } void r4dataListFree( LIST4 *l4 ) { R4DATA_LIST *r4data, *r4data2 ; for ( r4data = (R4DATA_LIST *)l4first( l4 ) ; r4data ; ) { r4data->relate->sortType = 0 ; r4data2 = (R4DATA_LIST *)l4next( l4, r4data ) ; l4remove( l4, r4data ) ; mem4free( r4data->relate->codeBase->relateDataListMemory, r4data ) ; r4data = r4data2 ; } } /* this function takes a completed sort list, and adds data members in the following case: If (r)elate must be added, r's siblings must also be added Or, interpreted differently, if r is a relation, and any of it's children must be added, then all of its children must be added. */ static int r4dataListMassage( LIST4 *l4 ) { RELATE4 *relateChild ; R4DATA_LIST *r4data ; int addChildren, relateAdded ; if ( l4->nLink == 0 ) /* no work required */ return 0 ; r4data = 0 ; for( ;; ) { r4data = (R4DATA_LIST *)l4next( l4, r4data ) ; if ( r4data == 0 ) break ; relateChild = 0 ; addChildren = 0 ; for( ;; ) { relateChild = (RELATE4 *)l4next( &r4data->relate->slaves, relateChild ) ; if ( relateChild == 0 ) break ; if ( r4dataListFind( l4, relateChild ) ) { addChildren = 1 ; break ; } } if ( addChildren == 1 ) { relateAdded = 0 ; relateChild = 0 ; for( ;; ) { relateChild = (RELATE4 *)l4next( &r4data->relate->slaves, relateChild ) ; if ( relateChild == 0 ) break ; if ( r4dataListFind( l4, relateChild ) == 0 ) { r4dataListAdd( l4, relateChild->data, relateChild ) ; relateChild->sortType = relate4exact ; relateAdded = 1 ; } } if ( relateAdded == 1 ) r4data = 0 ; /* start at list top again to be sure none missed */ } } return 0 ; } /* 1 - database added, 0 - database not added, -1 - error */ /* checkType gives the caller's status in terms of whether we should be included */ int r4dataListBuild( LIST4 *l4, RELATE4 *relate, EXPR4 *expr, int checkType ) { int i ; char mustAdd ; E4INFO *info ; RELATE4 *slaveOn ; if ( error4code( relate->codeBase ) < 0 ) return -1 ; mustAdd = 0 ; /* 1st check if we must belong */ for( i = 0 ; i < expr->infoN ; i++ ) { info = expr->info + i ; if ( info->fieldPtr ) { if ( info->fieldPtr->data == relate->data ) { mustAdd = 1 ; break ; } } } relate->sortType = relate4exact ; if ( mustAdd ) checkType = relate4exact ; else { if ( relate->relationType == relate4scan ) checkType = relate4scan ; else if ( checkType != relate4scan ) /* non-scan parent must be added, so we add ourselves too, in order to save work later */ mustAdd = 1 ; } /* if a child must be added, we must be too: */ for ( slaveOn = 0 ;; ) { slaveOn = (RELATE4 *)l4next( &relate->slaves, slaveOn ) ; if ( slaveOn == 0 ) break ; if ( r4dataListBuild( l4, slaveOn, expr, checkType ) == 1 ) mustAdd = 1 ; } if ( mustAdd ) r4dataListAdd( l4, relate->data, relate ) ; else if ( relate->relationType == relate4scan ) relate->sortType = relate4sortSkip ; return mustAdd ; } /* direction : -1 = look backwards, 0 = lookup only, 1 = look forwards */ static int relate4blankSet( RELATE4 *relate, const char direction ) { RELATE4 *slave ; CODE4 *c4 ; int rc ; #ifdef E4PARM_LOW if ( direction < -1 || direction > 1 ) return error4( 0, e4parm, E94417 ) ; #endif c4 = relate->codeBase ; if ( error4code( c4 ) < 0 ) return -1 ; relate->isRead = 1 ; if ( direction >= 0 ) { if ( d4goEof( relate->data ) < 0 ) return -1 ; } else { rc = d4top( relate->data ) ; if ( rc ) return rc ; rc = d4skip( relate->data, -1L ) ; relate->data->recNum = -1 ; d4blank( relate->data ) ; relate->data->recordChanged = 0 ; if ( error4code( c4 ) < 0 ) return -1 ; #ifdef S4SINGLE if ( rc < 0 ) #else if ( rc == r4locked || rc < 0 ) #endif return rc ; } for( slave = 0 ;; ) { slave = (RELATE4 *)l4next( &relate->slaves, slave ) ; if ( slave == 0 ) return 0 ; rc = relate4blankSet( slave, direction ) ; if ( rc < 0 ) return rc ; } } #endif #ifdef S4CLIENT int relate4unpack( RELATION4 *relation, CONNECTION4 *connection ) { CONNECTION4RELATION_DATA_OUT *info ; RELATE4 *relate ; unsigned int pos ; long recCount, len ; const char *data ; #ifndef S4MEMO_OFF int i ; #endif len = connection4len( connection ) ; data = connection4data( connection ) ; if ( len < sizeof( CONNECTION4RELATION_DATA_OUT ) ) return error4( relation->relate.codeBase, e4packetLen, E94425 ) ; info = (CONNECTION4RELATION_DATA_OUT *)data ; if ( info->relationId != relation->relationId ) return error4( relation->relate.codeBase, e4connection, E84305 ) ; pos = sizeof( CONNECTION4RELATION_DATA_OUT ) ; for( relate = &relation->relate ;; ) { #ifndef S4MEMO_OFF if ( relate->data->dataFile->nFieldsMemo > 0 ) { for ( i = 0; i < relate->data->dataFile->nFieldsMemo; i++ ) f4memoReset( relate->data->fieldsMemo[i].field ) ; } #endif if ( len < (long)pos + (long)dfile4recWidth( relate->data->dataFile ) + (long)sizeof( relate->data->recNum ) + (long)sizeof(long) ) return error4( relation->relate.codeBase, e4packetLen, E94425 ) ; memcpy( &relate->data->recNum, data + pos, sizeof( relate->data->recNum ) ) ; pos += sizeof( relate->data->recNum ) ; recCount = *((long *)( data + pos )) ; pos += sizeof( long ) ; if ( recCount < 0 ) return error4( relation->relate.codeBase, e4result, E84305 ) ; if ( relate->data->recNum > recCount ) relate->data->eofFlag = 1 ; else relate->data->eofFlag = 0 ; if ( recCount == 0 || relate->data->recNum == 0 ) relate->data->bofFlag = 1 ; else relate->data->bofFlag = 0 ; memcpy( relate->data->record, data + pos, dfile4recWidth( relate->data->dataFile ) ) ; pos += dfile4recWidth( relate->data->dataFile ) ; if ( relate4next( &relate ) == 2 ) break ; } if ( len != (long)pos ) return error4( relation->relate.codeBase, e4packetLen, E94425 ) ; return 0 ; } #endif int S4FUNCTION relate4bottom( RELATE4 *relate ) { RELATION4 *relation ; int rc, rc2 ; CODE4 *c4 ; #ifdef S4CLIENT CONNECTION4 *connection ; CONNECTION4RELATE_BOTTOM_INFO_IN info ; #else #ifndef S4OFF_MULTI int oldReadLock ; #endif long rec ; #endif #ifdef S4VBASIC if ( c4parm_check( relate, 5, E94401 ) ) return -1 ; #endif #ifdef E4PARM_LOW if ( relate == 0 ) return error4( 0, e4parm_null, E94401 ) ; #endif c4 = relate->codeBase ; if ( error4code( c4 ) < 0 ) return e4codeBase ; relation = relate->relation ; relate = &relation->relate ; #ifdef S4CLIENT if ( relation->isInitialized == 0 ) { rc = relate4clientInit( relate ) ; if ( rc != 0 ) return rc ; } #ifdef S4CB51 #ifndef S4OFF_MULTI if ( c4->readLock ) { rc = relate4lock( relate ) ; if ( rc != 0 ) return rc ; } #endif #endif #ifdef E4ANALYZE if ( relate->data == 0 ) return error4( c4, e4parm, E94401 ) ; if ( relate->data->dataFile == 0 ) return error4( c4, e4parm, E94401 ) ; #endif connection = relate->data->dataFile->connection ; #ifdef E4ANALYZE if ( connection == 0 ) return error4( c4, e4parm, E94401 ) ; #endif connection4assign( connection, CON4RELATE_BOTTOM, 0, 0 ) ; info.relationId = relation->relationId ; connection4addData( connection, &info, sizeof( CONNECTION4RELATE_BOTTOM_INFO_IN ), 0 ) ; connection4send( connection ) ; rc = connection4receive( connection ) ; if ( rc < 0 ) return error4stack( c4, rc, E94401 ) ; rc = connection4status( connection ) ; if ( rc < 0 ) return connection4error( connection, c4, rc, E94401 ) ; rc2 = relate4unpack( relation, connection ) ; if ( rc2 < 0 ) return error4stack( c4, rc, E94401 ) ; return rc ; #else #ifndef S4OFF_MULTI oldReadLock = c4->readLock ; c4->readLock = 0 ; #endif if ( relation->skipBackwards == 0 ) { relate4sortFree( relation, 0 ) ; relate4skipEnable( relate, 1 ) ; } for ( ;; ) /* used to minimize return code areas, just break out... */ { rc = relate4topInit( relate ) ; if ( rc != 0 ) break ; relate4setNotRead( relate ) ; relation->currentRelateLevel = 0 ; relate4prevRelationList( relation, 1 ) ; if ( relation->inSort == relate4sortDone ) { if ( relate4sortGetRecord( relation, relation->sortRecCount ) == r4eof ) { rc = r4eof ; break ; } else relation->sortRecOn = relation->sortRecCount ; } else { rc = d4bottom( relate->data ) ; if ( rc != 0 ) break ; if ( relation->exprSource ) { rec = d4recNo( relate->data ) ; #ifndef S4INDEX_OFF if ( relate->dataTag ) { while ( f4flagIsSetFlip( &relate->set, (unsigned long)rec ) == 0 ) { #ifdef S4HAS_DESCENDING rc = (int)tfile4dskip( relate->dataTag->tagFile, -1L ) ; #else rc = (int)tfile4skip( relate->dataTag->tagFile, -1L ) ; #endif if ( rc != -1 ) { if ( rc == 0 ) rc = r4eof ; break ; } rec = tfile4recNo( relate->dataTag->tagFile ) ; } if ( rc == r4eof ) break ; } else { #endif if ( f4flagIsSetFlip( &relate->set, (unsigned long)rec ) == 0 ) { rec = d4recNo( relate->data ) - f4flagGetNextFlip( &relate->set, (unsigned long)d4recNo( relate->data), (char)-1 ) ; if ( rec == 0 ) { rc = r4eof ; break ; } } #ifndef S4INDEX_OFF } #endif rc = d4go( relate->data, rec ) ; if ( rc < 0 ) break ; } relate4setNotRead( relate ) ; } rc = relate4readRest( relate, -1 ) ; if ( rc == relate4filterRecord ) rc = relate4skip( relate, -1L ) ; if ( rc < 0 || rc == r4terminate ) break ; if ( relation->exprSource ) { rc2 = log4true( &relation->log ) ; if ( rc2 == r4terminate ) { rc = r4terminate ; break ; } if ( rc2 == 0 ) { if ( relation->inSort == relate4sortSkip ) /* must temporarily disable in order to get a matching scan if available */ { relation->inSort = 0 ; rc = relate4skip( relate, -1L ) ; relation->inSort = relate4sortSkip ; } else rc = relate4skip( relate, -1L ) ; } } if ( rc == r4bof ) rc = r4eof ; break ; } #ifndef S4OFF_MULTI c4->readLock = oldReadLock ; #endif return rc ; #endif } #ifndef S4CLIENT static int relate4buildScanList( RELATE4 *master, RELATE4 *relate, RELATION4 *relation ) { RELATE4 *relateOn ; RELATE4LIST *ptr ; if ( error4code( relate->codeBase ) < 0 ) return -1 ; for( relateOn = 0 ;; ) { relateOn = (RELATE4 *)l4next( &relate->slaves, relateOn ) ; if ( relateOn == 0 ) break ; if ( relate4buildScanList( relate, relateOn, relation ) < 0 ) return -1 ; } if ( master != 0 ) /* cannot have a scan without a base master */ if ( relate->relationType == relate4scan || relate == &relation->relate ) { ptr = (RELATE4LIST *)mem4createAlloc( relate->codeBase, &relate->codeBase->relateListMemory, 5, sizeof(RELATE4LIST), 5, 0 ) ; if ( ptr == 0 ) return -1 ; ptr->ptr = relate ; l4add( &master->relateList, ptr ) ; } return 0 ; } #endif static void relate4freeRelateList( RELATE4 *relate ) { RELATE4 *slaveOn ; void *ptr ; for( ;; ) { ptr = l4pop( &relate->relateList ) ; if ( ptr == 0 ) break ; mem4free( relate->codeBase->relateListMemory, ptr ) ; } for ( slaveOn = 0 ;; ) { slaveOn = (RELATE4 *)l4next( &relate->slaves, slaveOn ) ; if ( slaveOn == 0 ) return ; relate4freeRelateList( slaveOn ) ; } } int S4FUNCTION relate4changed( RELATE4 *relate ) { RELATION4 *relation ; CODE4 *c4 ; #ifndef S4CLIENT int j ; #endif #ifdef E4PARM_HIGH if ( relate == 0 ) return error4( 0, e4parm_null, E94402 ) ; #endif c4 = relate->codeBase ; if ( error4code( c4 ) < 0 ) return -1 ; #ifndef S4CLIENT u4free( relate->scanValue ) ; relate->scanValue = 0 ; #endif relation = relate->relation ; #ifdef S4CLIENT if ( relation->isInitialized != 0 ) relation->needsFreeing = 1 ; #endif relation->isInitialized = 0 ; relate4sortFree( relation, 0 ) ; relate4freeRelateList( &(relation->relate) ) ; #ifndef S4CLIENT u4free( relation->relate.set.flags ) ; memset( (void *)&relation->relate.set, 0, sizeof( F4FLAG ) ) ; if ( relation->log.expr != 0 ) { for( j = relation->log.expr->infoN; --j >= 0; ) { E4INFO_REPORT *info_ptr = relation->log.infoReport + j ; if ( info_ptr->relateDataList != 0 ) { u4free( (info_ptr->relateDataList->pointers) ) ; mem4free( c4->dataListMemory, info_ptr->relateDataList ) ; } } expr4free( relation->log.expr ) ; relation->log.expr = 0 ; u4free( relation->log.infoReport ) ; relation->log.infoReport = 0 ; } if ( relation->log.bufLen != 0 ) { u4free( relation->log.buf ) ; relation->log.bufLen = 0 ; relation->log.bufPos = 0 ; } relation->inSort = 0 ; #endif return 0 ; } RELATE4 *S4FUNCTION relate4createSlave( RELATE4 *master, DATA4 *slaveData, char *masterExpr, TAG4 *slaveTag ) { RELATION4 *relation ; RELATE4 *slave ; CODE4 *c4 ; int rc ; #ifdef S4SERVER LIST4 *oldList ; #endif if ( master == 0 ) return 0 ; #ifdef S4VBASIC if ( c4parm_check( master, 5, E94403 ) ) return (RELATE4 *) 0 ; #endif c4 = master->codeBase ; if ( error4code( c4 ) < 0 ) return 0 ; #ifdef E4PARM_LOW if ( slaveData == 0 || masterExpr == 0 ) { error4( c4, e4parm_null, E94403 ) ; return 0 ; } #endif relation = master->relation ; #ifdef E4MISC /* check that the d4 doesn't belong to any existing relation */ if ( relate4lookupRelate( &relation->relate, slaveData ) != 0 ) { error4( c4, e4parm, E84403 ) ; return 0 ; } #endif relate4changed( master ) ; slave = (RELATE4 *)mem4createAlloc( c4, &c4->relateMemory, 5, sizeof(RELATE4), 5, 0 ) ; if ( slave == 0 ) return 0 ; #ifdef S4SERVER rc = relate4initRelate( slave, relation, slaveData, c4, 0 ) ; #else rc = relate4initRelate( slave, relation, slaveData, c4, 1 ) ; #endif if ( rc < 0 ) { mem4free( c4->relateMemory, slave ) ; return 0 ; } #ifdef S4SERVER oldList = tran4dataList( code4trans( c4 ) ) ; tran4dataListSet( code4trans( c4 ), &relation->localDataList ) ; #endif slave->masterExpr = expr4parseLow( master->data, masterExpr, 0 ) ; #ifdef S4VFP_KEY if ( slaveTag != 0 ) slave->masterExpr->vfpInfo = &slaveTag->tagFile->vfpInfo ; #endif #ifdef S4SERVER tran4dataListSet( code4trans( c4 ), oldList ) ; #endif if ( slave->masterExpr == 0 ) { mem4free( c4->relateMemory, slave ) ; return 0 ; } #ifndef S4CLIENT #ifndef S4INDEX_OFF if ( slaveTag != 0 ) if ( tfile4type( slaveTag->tagFile ) != expr4type( slave->masterExpr ) ) { #ifndef S4SERVER error4( c4, e4relate, E84404 ) ; #endif mem4free( c4->relateMemory, slave ) ; return 0 ; } #endif #endif slave->dataTag = slaveTag ; slave->master = master ; l4add( &master->slaves, slave ) ; relate4matchLen( slave, -1 ) ; /* Set to maximum */ return slave ; } #ifdef S4CLIENT int S4FUNCTION relate4doAll( RELATE4 *relate ) { CONNECTION4RELATE_DO_INFO_IN info ; CONNECTION4 *connection ; int rc, rc2 ; CODE4 *c4 ; #ifdef E4PARM_HIGH if ( relate == 0 ) return error4( 0, e4parm_null, E94404 ) ; #endif c4 = relate->codeBase ; #ifdef E4PARM_HIGH if ( relate->master != 0 ) return error4( c4, e4parm, E84402 ) ; #endif if ( relate->relation->isInitialized == 0 ) /* need to initialize on server first */ { rc = relate4clientInit( relate ) ; if ( rc != 0 ) return rc ; } connection = relate->data->dataFile->connection ; #ifdef E4ANALYZE if ( connection == 0 ) return error4( c4, e4struct, E94404 ) ; #endif connection4assign( connection, CON4RELATE_DO, 0, 0 ) ; info.relationId = relate->relation->relationId ; info.relateId = relate->id ; info.masterStartPos = d4recNo( relate->data ) ; connection4addData( connection, &info, sizeof( CONNECTION4RELATE_DO_INFO_IN ), 0 ) ; connection4send( connection ) ; rc = connection4receive( connection ) ; if ( rc < 0 ) return error4stack( c4, rc, E94404 ) ; rc = connection4status( connection ) ; if ( rc < 0 ) return connection4error( connection, c4, rc, E94404 ) ; rc2 = relate4unpack( relate->relation, relate->data->dataFile->connection ) ; if ( rc2 < 0 ) return error4stack( c4, rc, E94404 ) ; return rc ; } int S4FUNCTION relate4doOne( RELATE4 *relate ) { CONNECTION4RELATE_DO_ONE_INFO_IN info ; CONNECTION4RELATE_DO_ONE_INFO_OUT *out ; CONNECTION4 *connection ; int rc, saveRc ; CODE4 *c4 ; #ifdef E4PARM_HIGH if ( relate == 0 ) return error4( 0, e4parm_null, E94405 ) ; if ( relate->master == 0 ) return error4( relate->codeBase, e4parm, E84405 ) ; #endif c4 = relate->codeBase ; if ( relate->relation->isInitialized == 0 ) /* need to initialize on server first */ { rc = relate4clientInit( relate ) ; if ( rc != 0 ) return rc ; } connection = relate->data->dataFile->connection ; #ifdef E4ANALYZE if ( connection == 0 ) return error4( 0, e4struct, E94405 ) ; #endif connection4assign( connection, CON4RELATE_DO_ONE, 0, 0 ) ; info.relationId = relate->relation->relationId ; info.relateId = relate->id ; info.masterStartPos = d4recNo( relate->master->data ) ; connection4addData( connection, &info, sizeof( CONNECTION4RELATE_DO_ONE_INFO_IN ), 0 ) ; connection4send( connection ) ; rc = connection4receive( connection ) ; if ( rc < 0 ) return error4stack( c4, rc, E94405 ) ; rc = connection4status( connection ) ; if ( rc < 0 ) return connection4error( connection, c4, rc, E94405 ) ; saveRc = rc ; if ( saveRc != r4terminate ) { if ( saveRc == r4eof ) /* end of file */ { saveRc = 0 ; rc = d4goEof( relate->data ) ; if ( rc < 0 ) return error4stack( c4, rc, E94405 ) ; } else { if ( connection4len( connection ) != sizeof( CONNECTION4RELATE_DO_ONE_INFO_OUT ) ) return error4stack( c4, e4packetLen, E94405 ) ; out = (CONNECTION4RELATE_DO_ONE_INFO_OUT *)connection4data( connection ) ; rc = d4go( relate->data, out->recNo ) ; if ( rc < 0 ) return error4stack( c4, rc, E94405 ) ; } } return saveRc ; } #ifdef S4CB51 static int relate4dbfInRelation( RELATE4 *relate, const DATA4 *dbf ) { RELATE4 *relateOn ; relateOn = &relate->relation->relate ; while( relateOn->master ) relateOn = relateOn->master ; do { if ( relateOn->data == dbf ) return 1 ; } while( relate4next( &relateOn ) != 2 ) ; return 0 ; } #endif /* S4CB51 */ #else /* checks if the given dbf belongs to one of the relations in relation */ #ifdef S4CB51 static int relate4dbfInRelation( RELATE4 *relate, const DATA4 *dbf ) { RELATE4 *relateOn ; relateOn = &relate->relation->relate ; while( relateOn->master ) relateOn = relateOn->master ; do { if ( relateOn->data == dbf ) return 1 ; } while( relate4next( &relateOn ) != 2 ) ; return 0 ; } #endif /* S4CB51 */ int S4FUNCTION relate4doAll( RELATE4 *relate ) { int rc ; CODE4 *c4 ; #ifndef S4OFF_MULTI int oldReadLock ; #endif #ifdef S4VBASIC if ( c4parm_check( relate, 5, E94405 ) ) return -1 ; #endif #ifdef E4PARM_HIGH if ( relate == 0 ) return error4( 0, e4parm_null, E94405 ) ; #endif c4 = relate->codeBase ; #ifdef E4ANALYZE if ( relate->master != 0 ) return error4( c4, e4struct, E84402 ) ; #endif if ( error4code( c4 ) < 0 ) return e4codeBase ; #ifndef S4OFF_MULTI oldReadLock = c4->readLock ; c4->readLock = 0 ; #endif relate4setNotRead( relate ) ; rc = relate4readRest( relate, 0 ) ; #ifndef S4OFF_MULTI c4->readLock = oldReadLock ; #endif if ( rc == relate4filterRecord ) /* no match is an error */ { #ifndef S4SERVER if ( c4->errRelate ) return error4describe( c4, e4lookupErr, E94404, relate->data->alias, 0, 0 ) ; #endif return r4terminate ; } return rc ; } int S4FUNCTION relate4doOne( RELATE4 *relate ) { int rc ; CODE4 *c4 ; #ifndef S4OFF_MULTI int oldReadLock ; #endif #ifdef S4VBASIC if ( c4parm_check( relate, 5, E94405 ) ) return -1 ; #endif #ifdef E4PARM_HIGH if ( relate == 0 ) return error4( 0, e4parm_null, E94405 ) ; #endif c4 = relate->codeBase ; #ifdef E4PARM_HIGH if ( relate->master == 0 ) return error4( c4, e4parm, E84405 ) ; #endif if ( error4code( c4 ) < 0 ) return e4codeBase ; if ( relate->master == 0 ) /* no master, so we must be read */ return 0 ; #ifndef S4OFF_MULTI oldReadLock = c4->readLock ; c4->readLock = 0 ; #endif relate4setNotRead( relate ) ; rc = relate4lookup( relate, 0 ) ; #ifndef S4OFF_MULTI c4->readLock = oldReadLock ; #endif relate->isRead = relate->master->isRead ; /* we are read if master is read */ if ( rc == relate4filterRecord ) /* no match is an error */ { #ifndef S4SERVER if ( c4->errRelate ) return error4describe( c4, e4info, E94405, relate->data->alias, 0, 0 ) ; #endif return r4terminate ; } return rc ; } #endif int S4FUNCTION relate4eof( RELATE4 *relate ) { #ifdef E4PARM_HIGH if ( relate == 0 ) return error4( 0, e4parm_null, E94406 ) ; #endif #ifdef E4MISC if ( relate->relation->isInitialized == 0 ) { error4( relate->codeBase, e4info, E84406 ) ; return -1 ; } #endif #ifndef S4CLIENT if ( relate->relation->inSort == relate4sortDone ) return relate->relation->sortEofFlag ; else #endif return d4eof( relate->relation->relate.data ) ; } int S4FUNCTION relate4errorAction( RELATE4 *relate, const int code ) { int rc ; #ifdef E4PARM_HIGH if ( relate == 0 ) return error4( 0, e4parm_null, E94407 ) ; if ( code != relate4blank && code != relate4skipRec && code != relate4terminate ) return error4( relate->codeBase, e4parm, E84407 ) ; #endif rc = relate->errorAction ; relate->errorAction = code ; return rc ; } int S4FUNCTION relate4freeRelate( RELATE4 *relate, const int closeFiles ) { int rc ; RELATE4 *relateOn ; CODE4 *c4 ; #ifdef S4SERVER LIST4 *oldList ; #endif rc = 0 ; if ( relate->master == 0 ) return relate4free( relate, closeFiles ) ; c4 = relate->codeBase ; relate4changed( relate ) ; if( closeFiles ) { #ifdef S4SERVER oldList = tran4dataList( code4trans( c4 ) ) ; tran4dataListSet( code4trans( c4 ), &relate->relation->localDataList ) ; #endif if( d4close( relate->data ) < 0 ) rc = -1 ; #ifdef S4SERVER tran4dataListSet( code4trans( c4 ), oldList ) ; #endif relate->data = 0 ; } for( ;; ) { relateOn = (RELATE4 *)l4last( &relate->slaves) ; if ( relateOn == 0 ) break ; if( relate4freeRelate( relateOn, closeFiles ) < 0 ) rc = -1 ; } expr4free( relate->masterExpr ) ; u4free( relate->scanValue ) ; relate->scanValue = 0 ; #ifndef S4CLIENT u4free( relate->set.flags ) ; relate->set.flags = 0 ; #endif l4remove( &relate->master->slaves, relate ) ; mem4free( c4->relateMemory, relate ) ; relate = 0 ; return rc ; } int S4FUNCTION relate4free( RELATE4 *relate, const int closeFiles ) { int rc ; RELATION4 *relation ; RELATE4 *relateOn ; CODE4 *c4 ; #ifdef S4CLIENT CONNECTION4RELATE_FREE_INFO_IN info ; CONNECTION4 *connection ; #endif #ifdef S4SERVER LIST4 *oldList ; #endif if ( relate == 0 ) return -1 ; rc = 0 ; #ifdef S4VBASIC if ( c4parm_check( relate, 5, E94408 ) ) return -1 ; #endif #ifdef S4CB51 #ifndef S4SINGLE relate4unlock( relate ) ; #endif #endif relate4changed( relate ) ; relation = relate->relation ; relate = &relation->relate ; c4 = relate->codeBase ; #ifdef S4CLIENT #ifdef E4ANALYZE if ( relate->data == 0 ) return error4( c4, e4struct, E94408 ) ; if ( relate->data->dataFile == 0 ) return error4( c4, e4struct, E94408 ) ; #endif if ( relation->relationId != 0 ) /* if not freed on the server */ { connection = relate->data->dataFile->connection ; #ifdef E4ANALYZE if ( connection == 0 ) return error4( c4, e4struct, E94408 ) ; #endif connection4assign( connection, CON4RELATE_FREE, 0, 0 ) ; info.relationId = relation->relationId ; connection4addData( connection, &info, sizeof( CONNECTION4RELATE_FREE_INFO_IN ), 0 ) ; connection4send( connection ) ; rc = connection4receive( connection ) ; if ( rc < 0 ) return error4stack( c4, rc, E94408 ) ; rc = connection4status( connection ) ; if ( rc != 0 ) return connection4error( connection, c4, rc, E94408 ) ; } #endif #ifdef S4SERVER if( closeFiles || relate->freeData == 1 ) #else if( closeFiles ) #endif { #ifdef S4SERVER oldList = tran4dataList( code4trans( c4 ) ) ; tran4dataListSet( code4trans( c4 ), &relation->localDataList ) ; #endif if( d4close( relate->data ) < 0 ) rc = -1 ; #ifdef S4SERVER tran4dataListSet( code4trans( c4 ), oldList ) ; #endif relate->data = 0 ; } for( relateOn = 0 ;; ) { relateOn = (RELATE4 *)l4last( &relate->slaves ) ; if ( relateOn == 0 ) break ; if( relate4freeRelate( relateOn, closeFiles ) < 0 ) rc = -1 ; } /* mem4release( relation->relateMemory ) ; */ /* mem4release( relation->relateListMemory ) ; */ /* mem4release( relation->relateDataListMemory ) ; */ relate4sortFree( relation, 1 ) ; u4free( relation->exprSource ) ; mem4free( c4->relationMemory, relation ) ; return rc ; } #ifdef S4SERVER static DATA4 *relate4dataOpen( RELATE4 *relate, DATA4 *oldData ) { DATA4 *d4 ; int oldAccessMode, oldReadOnly ; CODE4 *c4 ; #ifdef E4PARM_LOW if ( relate == 0 || oldData == 0 ) { error4( 0, e4parm_null, E94427 ) ; return 0 ; } #endif c4 = relate->codeBase ; relate->dataOld = oldData ; oldAccessMode = c4->accessMode ; c4->accessMode = OPEN4DENY_NONE ; oldReadOnly = c4->readOnly ; c4->readOnly = 1 ; d4 = d4openClone( oldData ) ; if ( d4 == 0 ) { error4( c4, e4info, E94409 ) ; return 0 ; } #ifdef S4SERVER d4->trans = &c4->currentClient->trans ; /* l4add( tran4dataList( &c4->currentClient->trans ), d4 ) ;*/ #endif c4->accessMode = oldAccessMode ; c4->readOnly = oldReadOnly ; return d4 ; } #endif /* S4SERVER */ RELATE4 *S4FUNCTION relate4init( DATA4 *master ) { RELATION4 *relation ; CODE4 *c4 ; int rc ; #ifdef S4VBASIC if ( c4parm_check( master, 5, E94410 ) ) return (RELATE4 *) 0 ; #endif #ifdef E4PARM_HIGH if ( master == 0 ) { error4( 0, e4parm_null, E94410 ) ; return 0 ; } #endif c4 = master->codeBase ; if ( error4code( c4 ) < 0 ) return 0 ; if ( c4->relationMemory == 0 ) { c4->relationMemory = mem4create( c4, 5, sizeof( RELATION4 ), 5, 0 ) ; if ( c4->relationMemory == 0 ) return 0 ; } relation = (RELATION4 *)mem4alloc( c4->relationMemory ) ; if ( relation == 0 ) return 0 ; #ifndef S4CLIENT relation->log.relation = relation ; relation->log.codeBase = c4 ; relation->sort.file.hand = -1 ; relation->sortedFile.hand = -1 ; #endif rc = relate4initRelate( &relation->relate, relation, master, c4, 1 ) ; if ( rc < 0 ) { mem4free( c4->relationMemory, relation ) ; return 0 ; } #ifdef S4SERVER relation->relate.freeData = 1 ; #endif return &relation->relate ; } #ifdef P4ARGS_USED #pragma argsused #endif static int relate4initRelate( RELATE4 *relate, RELATION4 *relation, DATA4 *data, CODE4 *c4, int doOpen ) { #ifdef S4SERVER LIST4 *oldList ; #endif relate->codeBase = c4 ; relate->relationType = relate4exact ; #ifdef E4ANALYZE if ( relate->data != 0 ) error4( relate->codeBase, e4info, E84406 ) ; #endif #ifndef S4SERVER relate->data = data ; #else if ( doOpen == 0 ) relate->data = data ; else { oldList = tran4dataList( code4trans( c4 ) ) ; tran4dataListSet( code4trans( c4 ), &relation->localDataList ) ; relate->data = relate4dataOpen( relate, data ) ; tran4dataListSet( code4trans( c4 ), oldList ) ; } #endif relate->errorAction = relate4blank ; relate->relation = relation ; if ( relate->data == 0 ) return error4( c4, e4info, E94410 ) ; relate->data->count = d4recCount( relate->data ) ; return 0 ; } #ifdef P4ARGS_USED #pragma argsused #endif int S4FUNCTION relate4lockAdd( RELATE4 *relate ) { #ifdef S4OFF_MULTI return 0 ; #else int rc ; RELATE4 *relateOn ; #ifdef E4PARM_HIGH if ( relate == 0 ) return error4( 0, e4parm_null, E94411 ) ; #endif #ifdef S4VBASIC if ( c4parm_check( relate, 5, E94411 ) ) return -1 ; #endif if ( error4code( relate->codeBase ) < 0 ) return e4codeBase ; rc = 0 ; for( relateOn = &relate->relation->relate ;; ) { if ( relateOn == 0 ) break ; rc = d4lockAddFile( relateOn->data ) ; if ( rc != 0 ) break ; if ( relate4next( &relateOn ) == 2 ) break ; } return rc ; #endif } #ifdef S4CB51 /* new form does not allow combining lockAdds spaced around relate4locks() if required to do that, S4OLD_RELATE_LOCK code will allow */ int S4FUNCTION relate4lock( RELATE4 *relate ) { int rc ; rc = relate4lockAdd( relate ) ; if ( rc < 0 ) return rc ; rc = code4lock( relate->codeBase ) ; if ( rc == 0 ) relate->relation->locked = 1 ; return rc ; } #endif #ifdef S4CLIENT #ifdef S4CB51 #ifdef S4OLD_RELATE_LOCK int S4FUNCTION relate4lock( RELATE4 *relate ) { CONNECTION4 *connection ; CONNECTION4RELATE_LOCK_INFO_IN info ; CONNECTION4RELATE_LOCK_INFO_OUT *out ; CONNECTION4RELATE_LOCK_SUB_DATA *subData ; int rc, count ; CODE4 *c4 ; DATA4 *data ; #ifdef E4PARM_HIGH if ( relate == 0 ) return error4( 0, e4parm_null, E94411 ) ; #endif c4 = relate->codeBase ; /* must ensure that client relation is registered before requesting a lock */ if ( relate->relation->isInitialized == 0 || relate->dataTag != relate->data->tagSelected ) { relate->dataTag = relate->data->tagSelected ; rc = relate4clientInit( relate ) ; if ( rc != 0 ) return rc ; } connection = relate->data->dataFile->connection ; #ifdef E4ANALYZE if ( connection == 0 ) { error4( c4, e4struct, E94411 ) ; return 0 ; } #endif connection4assign( connection, CON4RELATE_LOCK, 0, 0 ) ; info.relationId = relate->relation->relationId ; connection4addData( connection, &info, sizeof( CONNECTION4RELATE_LOCK_INFO_IN ), 0 ) ; rc = connection4repeat( connection, -2, -1, -1, 0 ) ; if ( rc == r4locked ) return r4locked ; if ( rc < 0 ) return connection4error( connection, c4, rc, E94411 ) ; if ( rc == 0 ) /* add locks */ { if ( connection4len( connection ) < sizeof(CONNECTION4RELATE_LOCK_INFO_OUT) ) return error4( c4, e4packetLen, E94411 ) ; out = (CONNECTION4RELATE_LOCK_INFO_OUT *)connection4data( connection ) ; if ( connection4len( connection ) != (long)sizeof(CONNECTION4RELATE_LOCK_INFO_OUT) + out->count * sizeof( CONNECTION4RELATE_LOCK_SUB_DATA ) ) return error4( c4, e4packetLen, E94411 ) ; subData = (CONNECTION4RELATE_LOCK_SUB_DATA *)( (char *)connection4data( connection ) + sizeof( CONNECTION4RELATE_LOCK_INFO_OUT ) ) ; for( count = out->count ; count > 0 ; count-- ) { data = tran4data( code4trans( c4 ), subData->serverId, subData->clientId ) ; if ( data == 0 ) return error4( c4, e4info, E94411 ) ; if ( data->dataFile->fileLock != 0 ) { #ifdef E4MISC if ( data->dataFile->fileLock != data ) return error4( c4, e4info, E94411 ) ; #endif } else data->dataFile->fileLock = data ; subData++ ; } } return rc ; } #endif /* S4OLD_RELATE_LOCK */ #endif /* S4CB51 */ #else #ifdef S4OLD_RELATE_LOCK int S4FUNCTION relate4lock( RELATE4 *relate ) { #ifdef S4SINGLE return 0 ; #else CODE4 *c4 ; int rc, oldAttempts, count ; RELATE4 *relateOn ; #ifdef S4SERVER RELATE4 *relateSkip ; DATA4 *dataTemp ; #endif #ifdef E4PARM_HIGH if ( relate == 0 ) return error4( 0, e4parm_null, E94411 ) ; #endif #ifdef S4VBASIC if ( c4parm_check( relate, 5, E94411 ) ) return -1 ; #endif c4 = relate->codeBase ; if ( error4code( c4 ) < 0 ) return e4codeBase ; relate->relation->locked = 1 ; count = oldAttempts = c4->lockAttempts ; /* take care of wait here */ c4->lockAttempts = 1 ; for( ;; ) { rc = 0 ; for( relateOn = &relate->relation->relate ;; ) { if ( relateOn == 0 ) break ; #ifdef S4SERVER if ( relateOn->dataOld->dataFile->fileServerLock == data4serverId( relateOn->dataOld ) ) /* check for duplicate lock */ { dataTemp = tran4data( code4trans( c4 ), relateOn->dataOld->dataFile->fileServerLock, relateOn->dataOld->dataFile->fileClientLock ) ; if ( dataTemp == 0 ) rc = r4locked ; else { /* check for data in relation via relate search */ for ( relateSkip = &relate->relation->relate ;; ) { if ( relateSkip == 0 ) { rc = r4locked ; break ; } if ( relateSkip->dataOld == dataTemp ) break ; if ( relate4next( &relateSkip ) == 2 ) break ; } } } else rc = dfile4lockAll( relateOn->dataOld->dataFile, data4clientId( relateOn->dataOld ), data4serverId( relateOn->dataOld ), relateOn->dataOld ) ; #else rc = d4lockAll( relateOn->data ) ; #endif if ( rc != 0 ) break ; if ( relate4next( &relateOn ) == 2 ) break ; } if ( rc != r4locked ) break ; relate4unlock( relate ) ; if ( count == 0 ) break ; if ( count > 0 ) count-- ; #ifdef S4TEMP if ( d4displayQuit( &display ) ) { rc = error4( c4, e4result, E84409 ) ; break ; } #endif u4delayHundredth( c4->lockDelay ) ; /* wait a second & try lock again */ } c4->lockAttempts = oldAttempts ; if ( error4code( c4 ) < 0 ) return error4code( c4 ) ; return rc ; #endif } #endif /* S4OLD_RELATE_LOCK */ /* direction : -1 = look backwards, 0 = lookup only, 1 = look forwards */ static int relate4lookup( RELATE4 *relate, const char direction ) { int rc ; long recno ; CODE4 *c4 ; RELATION4 *relation ; #ifndef S4SERVER int oldGoError ; #endif #ifndef S4INDEX_OFF char *ptr ; int len ; #endif #ifdef E4PARM_LOW if ( relate == 0 ) return error4( 0, e4parm_null, E94412 ) ; if ( direction < -1 || direction > 1 ) return error4( 0, e4parm, E94417 ) ; #endif c4 = relate->codeBase ; if ( error4code( c4 ) < 0 ) return e4codeBase ; relation = relate->relation ; #ifdef E4MISC if ( direction != 0 && relation->isInitialized == 0 ) return error4( c4, e4struct, E84406 ) ; #endif relate->isRead = 1 ; if ( relate->master == 0 ) return 0 ; #ifndef S4INDEX_OFF d4tagSelect( relate->data, relate->dataTag ) ; if ( relate->dataTag == 0 ) { #endif recno = (long)expr4double( relate->masterExpr ) ; if ( error4code( c4 ) < 0 ) return -1 ; if ( direction != 0 ) if ( f4flagIsSetFlip( &relate->set, (unsigned long)recno ) == 0 ) return relate4filterRecord ; #ifndef S4SERVER oldGoError = c4->errGo ; c4->errGo = 0 ; #endif rc = d4go( relate->data, recno ) ; #ifndef S4SERVER c4->errGo = oldGoError ; if ( rc < 0 ) return error4stack( c4, rc, E94412 ) ; #endif if ( rc != r4entry ) /* if not error, then return */ return 0 ; if ( relate->relationType == relate4approx ) { d4goEof( relate->data ) ; return 0 ; } #ifndef S4INDEX_OFF } else { len = expr4key( relate->masterExpr, &ptr ) ; if ( len < 0 ) return -1 ; len = (len < relate->matchLen) ? len : relate->matchLen ; /* min of len and match len */ if ( relate->relationType == relate4scan ) { #ifdef E4ANALYZE if ( relate->master == 0 ) return error4( c4, e4struct, E84410 ) ; #endif if ( relate->master->scanValue == 0 ) { relate->master->scanValueLen = len ; relate->master->scanValue = (char *)u4allocEr( c4, (long)len ) ; if ( relate->master->scanValue == 0 ) return -1 ; } memcpy( relate->master->scanValue, ptr, (unsigned int)len ) ; } rc = tfile4seek( relate->dataTag->tagFile, ptr, len ) ; if ( rc < 0 ) return -1 ; if ( relate->relationType == relate4approx || rc == 0 ) { if ( tfile4eof( relate->dataTag->tagFile) ) { d4goEof( relate->data ) ; return 0 ; } if ( (int)direction < 0 && rc == 0 && relate->relationType == relate4scan ) /* look for last one */ for( ;; ) { #ifdef S4HAS_DESCENDING if ( !tfile4dskip( relate->dataTag->tagFile, 1L ) ) #else if ( !tfile4skip( relate->dataTag->tagFile, 1L ) ) #endif break ; #ifdef S4FOX if ( u4keycmp( tfile4keyData( relate->dataTag->tagFile )->value, ptr, (unsigned int)len, (unsigned int)relate->dataTag->tagFile->header.keyLen, 0, &relate->dataTag->tagFile->vfpInfo ) ) #else if ( u4memcmp( tfile4keyData( relate->dataTag->tagFile )->value, ptr, (unsigned int)len ) ) #endif { #ifdef S4HAS_DESCENDING tfile4dskip( relate->dataTag->tagFile, -1L ) ; #else tfile4skip( relate->dataTag->tagFile, -1L ) ; #endif break ; } } recno = tfile4keyData( relate->dataTag->tagFile )->num ; if ( direction != 0 ) if ( f4flagIsSetFlip( &relate->set, (unsigned long)recno ) == 0 ) return relate4filterRecord ; if ( d4go( relate->data, recno ) < 0 ) return -1 ; return 0 ; } else recno = tfile4keyData( relate->dataTag->tagFile )->num ; } #endif switch( relate->errorAction ) /* if got here, must be error condition */ { case relate4blank: if ( d4goEof( relate->data ) < 0 ) return -1 ; if ( direction != 0 ) if ( f4flagIsSetFlip( &relate->set, (unsigned long)recno ) == 0 ) return relate4filterRecord ; return 0 ; case relate4skipRec: /* if a scan, and a failure, then, if the current relation set is below the current position, move it to above the current position */ if ( relate->relationType == relate4scan ) { if ( relate != &relation->relate ) while ( relate4currentIsChild( relate ) ) relate4nextRelationList( relation, 0 ) ; } return relate4filterRecord ; case relate4terminate: #ifndef S4SERVER if ( c4->errRelate ) return error4describe( c4, e4lookupErr, E94412, relate->data->alias, 0, 0 ) ; #endif return r4terminate ; default: /* should never get this far */ return error4( c4, e4info, E84411 ) ; } } #endif RELATE4 *relate4lookupRelate( RELATE4 *relate, const DATA4 *d4 ) { RELATE4 *relateReturn, *relateOn ; if ( relate->data == d4 ) return relate ; for( relateOn = 0 ;; ) { relateOn = (RELATE4 *)l4next( &relate->slaves, relateOn) ; if ( relateOn == 0 ) return 0 ; relateReturn = relate4lookupRelate( relateOn, d4 ) ; if ( relateReturn ) return relateReturn ; } } int S4FUNCTION relate4matchLen( RELATE4 *relate, const int matchLenIn ) { int len, matchLen ; #ifdef E4PARM_HIGH if ( relate == 0 ) return error4( 0, e4parm_null, E94413 ) ; #endif if ( error4code( relate->codeBase ) < 0 ) return e4codeBase ; matchLen = matchLenIn ; len = expr4keyLen( relate->masterExpr ) ; #ifdef S4CLIPPER if ( matchLen <= 0 ) matchLen = len ; #else if ( matchLen <= 0 ) { relate->matchLen = len ; return len ; } #endif #ifndef S4CLIENT #ifndef S4INDEX_OFF #ifndef S4CLIPPER #ifdef E4MISC if ( relate->dataTag ) if( expr4type( relate->dataTag->tagFile->expr ) != r4str ) /* make sure r4str only */ return error4( relate->codeBase, e4relate, E84412 ) ; #endif #endif #endif #endif if ( matchLen >= len ) matchLen = len ; #ifndef S4CLIENT #ifndef S4INDEX_OFF if ( relate->dataTag ) { len = expr4keyLen( relate->dataTag->tagFile->expr ) ; if ( matchLen >= len ) matchLen = len ; } #endif #endif relate->matchLen = matchLen ; relate4changed( relate ) ; return matchLen ; } /* r4same = 0, r4down = 1, r4complete = 2 */ int S4FUNCTION relate4next( RELATE4 **ptrPtr ) { RELATE4 *cur ; void *nextLink ; int rc ; #ifdef E4PARM_HIGH if ( ptrPtr == 0 ) return error4( 0, e4parm_null, E94414 ) ; if ( *ptrPtr == 0 ) return error4( 0, e4parm_null, E94414 ) ; #endif cur = *ptrPtr ; rc = r4down ; if ( cur->slaves.nLink > 0 ) { *ptrPtr = (RELATE4 *)l4first( &cur->slaves ) ; return r4down ; } for(;;) { rc -- ; if ( cur->master == 0 ) { *ptrPtr = 0 ; return r4complete ; } nextLink = l4next( &cur->master->slaves, cur ) ; if ( nextLink ) { *ptrPtr = (RELATE4 *)nextLink ; return rc ; } cur = cur->master ; } } #ifndef S4CLIENT static int relate4nextRecordInScan( RELATE4 *relate ) { long nextRec ; int rc ; DATA4 *d4 ; #ifndef S4SERVER int saveCode ; #endif #ifndef S4INDEX_OFF B4KEY_DATA *key ; #endif #ifndef S4INDEX_OFF char *ptr ; int len ; #endif #ifdef E4PARM_LOW if ( relate == 0 ) return error4( 0, e4parm_null, E94415 ) ; #endif if ( error4code( relate->codeBase ) < 0 ) return e4codeBase ; #ifdef E4MISC if ( relate->relation->isInitialized == 0 ) return error4( relate->codeBase, e4info, E84406 ) ; #endif if ( relate->relation->inSort == relate4sortSkip && relate->sortType == relate4sortSkip ) return r4eof ; d4 = relate->data ; #ifndef S4INDEX_OFF if ( relate->dataTag == 0 ) { #endif if ( d4bof( d4 ) ) nextRec = 1 ; else nextRec = d4recNo( d4 ) + 1 ; nextRec += f4flagGetNextFlip( &relate->set, (unsigned long)nextRec, (char)1 ) ; if ( d4recCountLessEq( d4, nextRec ) == 0 ) return r4eof ; #ifndef S4INDEX_OFF } else for(;;) { if ( d4bof( d4 ) ) { if ( d4recCountLessEq( d4, 1L ) == 0L ) /* count == 0 */ return r4eof ; if ( relate->masterExpr == 0 ) /* top relate, bof */ return r4bof ; len = expr4key( relate->masterExpr, &ptr ) ; if ( len < 0 ) return -1 ; len = (len < relate->matchLen) ? len : relate->matchLen ; /* min of len and match len */ rc = (int)tfile4seek( relate->dataTag->tagFile, relate->master->scanValue, len ) ; if ( rc < 0 ) return -1 ; if ( rc == 0 ) rc = 1 ; else rc = 0 ; } else #ifdef S4HAS_DESCENDING rc = (int)tfile4dskip( relate->dataTag->tagFile, 1L ) ; #else rc = (int)tfile4skip( relate->dataTag->tagFile, 1L ) ; #endif if ( rc < 0 ) return -1 ; if ( rc != 1 ) return r4eof ; key = tfile4keyData( relate->dataTag->tagFile) ; nextRec = key->num ; if ( relate->master ) { #ifdef S4FOX if ( u4keycmp( key->value, relate->master->scanValue, (unsigned int)relate->master->scanValueLen, (unsigned int)relate->dataTag->tagFile->header.keyLen, 0, &relate->dataTag->tagFile->vfpInfo ) ) #else if ( u4memcmp( key->value, relate->master->scanValue, (unsigned int)relate->master->scanValueLen ) ) #endif return r4eof ; } if ( f4flagIsSetFlip( &relate->set, (unsigned long)nextRec ) ) break ; } #endif #ifndef S4SERVER saveCode = relate->codeBase->errGo ; relate->codeBase->errGo = 0 ; #endif rc = d4go( d4, nextRec ) ; #ifndef S4SERVER relate->codeBase->errGo = saveCode ; #endif if ( rc < 0 ) return -1 ; if ( rc == r4entry ) return r4eof ; relate->isRead = 1 ; /* we have updated this one */ return relate4skipped ; } /* returns 1 if the current relation set is a child (or is itself) of the input relation */ static int relate4currentIsChild( RELATE4 *parent ) { RELATE4 *relateOn ; relateOn = parent->relation->currentRelateLevel ; if ( relateOn == parent ) return 1 ; for ( relateOn = 0 ;; ) /* now recursively check all the descendents */ { relateOn = (RELATE4 *)l4next( &parent->slaves, relateOn ) ; if ( relateOn == 0 ) return 0 ; if ( relate4currentIsChild( relateOn ) == 1 ) return 1 ; } } /* returns 1 if the parent is above the child at any level (grandparent, etc) */ static int relate4parent( RELATE4 *parent, RELATE4 *child ) { RELATE4 *slaveOn, *masterOn ; for ( slaveOn = child ;; ) { masterOn = slaveOn->master ; if ( masterOn == 0 ) return 0 ; if ( masterOn == parent ) return 1 ; slaveOn = slaveOn->master ; /* go up one level */ } } /* returns 1 if done, 0 if positioned to a new relate */ /* if setup is true, it just does positional work (for relate4top), no reads */ static int relate4nextRelationList( RELATION4 *relation, int setup ) { RELATE4 *relateOn, *master ; int rc, rc2 ; relateOn = relation->currentRelateLevel ; if ( relateOn == 0 ) /* means get the first one */ relateOn = &relation->relate ; else if ( setup != 1 ) { /* first see if we are part of a scan ourselves, and if so that we are scanned */ if ( relateOn->relationType == relate4scan || relateOn == &relation->relate ) /* the master is an implicit scan */ { relate4setNotRead( relateOn ) ; /* This data file & its slaves */ if ( relation->inSort == relate4sortDone ) if ( r4dataListFind( &relation->sortDataList, relateOn ) ) return relate4sortNextRecord( relation ) ; rc = relate4nextRecordInScan( relateOn ) ; if ( rc == relate4skipped ) return 0 ; rc2 = relate4blankSet( relateOn, (char)1 ) ; if ( rc2 == r4locked || rc2 < 0 ) /* error or locked */ return rc2 ; if ( relateOn->master == 0 ) if ( d4eof( relateOn->data ) ) return r4eof ; /* are our siblings also scanned? */ /* we are scanned, so fall through and check out our master */ } if ( relateOn->master == 0 ) { relation->currentRelateLevel = 0 ; return r4eof ; } master = relateOn->master ; /* try our masters next slave */ relateOn = (RELATE4 *)l4next( &master->slaves, relateOn ) ; if ( relateOn == 0 ) /* no more slaves, try the master itself */ { relation->currentRelateLevel = master ; return relate4continue ; /* either do ourselves or go up further */ } } /* we need to go down our own slave list to the bottom level and start seek */ while ( relateOn->slaves.nLink != 0 ) relateOn = (RELATE4 *)l4first( &relateOn->slaves ) ; /* at the bottom, so try ourselves */ relation->currentRelateLevel = relateOn->master ; if ( setup == 1 ) return relate4continue ; if ( relateOn->master == 0 ) /* done/ eof */ return r4eof ; if ( relateOn->relationType == relate4scan ) return relate4continue ; /* otherwise try our current masters other slaves--i.e. just go get next */ return relate4nextRelationList( relation, setup ) ; } static int relate4nextScanRecord( RELATION4 *relation ) { RELATE4 *relate ; int rc, rc2, tryMatches ; LIST4 *relateList ; if ( error4code( relation->relate.codeBase ) < 0 ) return -1 ; rc = 0 ; for ( ;; ) { if ( relation->currentRelateLevel == 0 ) relation->currentRelateLevel = &relation->relate ; for ( ;; ) { relateList = &relation->currentRelateLevel->relateList ; tryMatches = 1 ; if ( d4eof( relation->currentRelateLevel->data ) ) /* we are at eof, so all children cannot match */ tryMatches = 0 ; else { if ( relation->currentRelateLevel->master != 0 ) if ( d4eof( relation->currentRelateLevel->master->data ) ) /* means no matches possible */ tryMatches = 0 ; } if ( tryMatches == 1 && l4numNodes( relateList ) ) { if ( relateList->selected == 0 ) relateList->selected = (LINK4 *)l4first( relateList ) ; for( ;; ) { if ( relateList->selected == 0 ) /* finished with matches for this list */ break ; relate = ((RELATE4LIST *)relateList->selected)->ptr ; relate4setNotRead( relate ) ; /* This data file & its slaves */ if ( relation->inSort == relate4sortDone ) if ( r4dataListFind( &relation->sortDataList, relate ) ) return relate4sortNextRecord( relation ) ; rc = relate4nextRecordInScan( relate ) ; if ( rc == relate4skipped ) return 0 ; if ( rc < 0 ) return rc ; rc2 = relate4blankSet( relate, (char)1 ) ; if ( rc2 == r4locked || rc2 < 0 ) /* error or locked */ return rc2 ; if ( relate->master == 0 ) if ( d4eof( relate->data ) ) return r4eof ; relateList->selected =(LINK4 *)l4next( relateList, relateList->selected ) ; } } rc = relate4nextRelationList( relation, 0 ) ; if ( rc != relate4continue ) return rc ; } /* for ( ;; ) { if ( d4eof( relation->relate.data ) ) { rc = r4eof ; break ; } if ( relation->inSort == relate4sortDone ) if ( r4dataListFind( &relation->sortDataList, &relation->relate ) ) { relate4setNotRead( &relation->relate ) ; return relate4sortNextRecord( relation ) ; } rc = relate4nextRecordInScan( &relation->relate ) ; if ( rc < 0 ) return rc ; if ( rc != relate4skipped ) { rc = r4bof ; break ; } relate4setNotRead( &relation->relate ) ; rc = relate4lookup( &relation->relate, 1 ) ; if ( rc != 0 ) continue ; rc = relate4readRest( &relation->relate, 1 ) ; if ( rc == 0 || rc < 0 || rc == r4terminate ) return rc ; rc = 0 ; } if ( rc != 0 ) break ; */ } /* d4goEof( relation->relate.data ) ; */ /* return r4eof ; */ } /* returns 1 if done, 0 if positioned to a new relate */ /* if setup is true, it just does positional work (for relate4top), no reads */ static int relate4prevRelationList( RELATION4 *relation, int setup ) { RELATE4 *relateOn, *master ; int rc, rc2 ; relateOn = relation->currentRelateLevel ; if ( relateOn == 0 ) /* means get the first one */ relateOn = &relation->relate ; else if ( setup != 1 ) { /* first see if we are part of a scan ourselves, and if so that we are scanned */ if ( relateOn->relationType == relate4scan || relateOn == &relation->relate ) /* the master is an implicit scan */ { relate4setNotRead( relateOn ) ; /* This data file & its slaves */ if ( relation->inSort == relate4sortDone ) if ( r4dataListFind( &relation->sortDataList, relateOn ) ) return relate4sortPrevRecord( relation ) ; rc = relate4prevRecordInScan( relateOn ) ; if ( rc == relate4skipped ) { if ( relate4eof( relateOn ) ) { if ( relation->inSort == relate4sortDone && relation->sortEofFlag == 1 ) { relation->sortRecOn-- ; /* move off eof on sort part */ relation->sortEofFlag = 0 ; } else { rc = d4go( relation->relate.data, d4recCount( relation->relate.data ) ) ; if ( rc < 0 ) return rc ; } } return 0 ; } if ( rc < 0 ) return rc ; rc2 = relate4blankSet( relateOn, (char)-1 ) ; if ( rc2 == r4locked || rc2 < 0 ) /* error or locked */ return rc2 ; if ( relateOn->master == 0 ) { if ( d4bof( relateOn->data ) ) return r4bof ; if ( d4eof( relateOn->data ) ) return r4eof ; } /* are our siblings also scanned? */ /* we are scanned, so fall through and check out our master */ } if ( relateOn->master == 0 ) { relation->currentRelateLevel = 0 ; return r4bof ; } master = relateOn->master ; /* try our masters prev slave */ relateOn = (RELATE4 *)l4prev( &master->slaves, relateOn ) ; if ( relateOn == 0 ) /* no more slaves, try the master itself */ { relation->currentRelateLevel = master ; return relate4continue ; /* either do ourselves or go up further */ } } /* we need to go down our own slave list to the bottom level and start seek */ while ( relateOn->slaves.nLink != 0 ) relateOn = (RELATE4 *)l4last( &relateOn->slaves ) ; /* at the bottom, so try ourselves */ relation->currentRelateLevel = relateOn->master ; if ( setup == 1 ) return relate4continue ; if ( relateOn->master == 0 ) /* done / bof */ return r4bof ; if ( relateOn->relationType == relate4scan ) return relate4continue ; /* otherwise try our current masters other slaves--i.e. just go get next */ return relate4prevRelationList( relation, setup ) ; } static int relate4prevRecordInScan( RELATE4 *relate ) { long nextRec ; int rc ; DATA4 *d4 ; #ifndef S4SERVER int saveCode ; #endif #ifndef S4INDEX_OFF B4KEY_DATA *key ; #ifdef S4HAS_DESCENDING unsigned short int oldDesc ; int len ; char *ptr ; #endif #endif #ifdef E4PARM_LOW if ( relate == 0 ) return error4( 0, e4parm_null, E94416 ) ; #endif #ifdef E4MISC if ( relate->relation->isInitialized == 0 ) return error4( relate->codeBase, e4info, E84406 ) ; #endif d4 = relate->data ; #ifndef S4INDEX_OFF if ( relate->dataTag == 0 ) { #endif nextRec = d4recNo( d4 ) - 1 ; nextRec -= f4flagGetNextFlip( &relate->set, (unsigned long)nextRec, (char)-1 ) ; if ( nextRec <= 0 ) return r4bof ; if ( d4recCountLessEq( d4, nextRec ) == 0 ) return r4eof ; #ifndef S4INDEX_OFF } else for(;;) { if ( relate4eof( relate ) ) /* if eof in relate, just leave on last tag entry */ rc = tfile4eof( relate->dataTag->tagFile ) ? 0 : -1 ; else { if ( d4eof( d4 ) == 1 ) { if ( d4recCountLessEq( d4, 1L ) == 0L ) /* count == 0 */ return r4bof ; if ( relate->masterExpr == 0 ) /* top relate, bof */ return r4eof ; #ifdef S4HAS_DESCENDING len = expr4key( relate->masterExpr, &ptr ) ; if ( len < 0 ) return -1 ; len = (len < relate->matchLen) ? len : relate->matchLen ; /* min of len and match len */ oldDesc = relate->dataTag->tagFile->header.descending ; tfile4descending( relate->dataTag->tagFile, ((unsigned short int)(1 - oldDesc)) ) ; /* invert the descending */ rc = (int)tfile4seek( relate->dataTag->tagFile, relate->master->scanValue, len ) ; tfile4descending( relate->dataTag->tagFile, oldDesc ) ; #else /* need to find the last matching entry, without seek */ rc = (int)tfile4bottom( relate->dataTag->tagFile ) ; if ( rc == 0 ) { rc = -1 ; while ( rc == -1 ) { key = tfile4keyData( relate->dataTag->tagFile) ; if ( u4memcmp( key->value, relate->master->scanValue, (unsigned int)relate->master->scanValueLen ) == 0 ) { rc = 0 ; break ; } if ( u4memcmp( key->value, relate->master->scanValue, (unsigned int)relate->master->scanValueLen ) < 0 ) return r4bof ; rc = (int)tfile4skip( relate->dataTag->tagFile, -1L ) ; } } #endif if ( rc < 0 ) return -1 ; if ( rc == 0 ) rc = -1 ; else rc = 0 ; } else #ifdef S4HAS_DESCENDING rc = (int)tfile4dskip( relate->dataTag->tagFile, -1L ) ; #else rc = (int)tfile4skip( relate->dataTag->tagFile, -1L ) ; #endif } if ( rc > 0 ) return -1 ; if ( rc != -1L ) return r4bof ; key = tfile4keyData( relate->dataTag->tagFile) ; nextRec = key->num ; if ( relate->master ) #ifdef S4FOX if ( u4keycmp( key->value, relate->master->scanValue, (unsigned int)relate->master->scanValueLen, (unsigned int)relate->dataTag->tagFile->header.keyLen, 0, &relate->dataTag->tagFile->vfpInfo ) ) #else if ( u4memcmp( key->value, relate->master->scanValue, (unsigned int)relate->master->scanValueLen ) ) #endif return r4bof ; if ( f4flagIsSetFlip( &relate->set, (unsigned long)nextRec ) ) break ; } #endif #ifndef S4SERVER saveCode = relate->codeBase->errGo ; relate->codeBase->errGo = 0 ; #endif rc = d4go( d4, nextRec ) ; #ifndef S4SERVER relate->codeBase->errGo = saveCode ; #endif if ( rc < 0 ) return -1 ; if ( rc == r4entry ) return r4eof ; relate->isRead = 1 ; /* we have updated this one */ return relate4skipped ; } static int relate4prevScanRecord( RELATION4 *relation ) { RELATE4 *relate ; int rc, rc2, tryMatches ; LIST4 *relateList ; if ( error4code( relation->relate.codeBase ) < 0 ) return -1 ; rc = 0 ; for ( ;; ) { if ( relation->currentRelateLevel == 0 ) relation->currentRelateLevel = &relation->relate ; for ( ;; ) { relateList = &relation->currentRelateLevel->relateList ; tryMatches = 1 ; if ( d4eof( relation->currentRelateLevel->data ) ) /* we are at eof, so all children cannot match */ tryMatches = 0 ; else { if ( relation->currentRelateLevel->master != 0 ) if ( d4eof( relation->currentRelateLevel->master->data ) ) /* means no matches possible */ tryMatches = 0 ; } if ( tryMatches == 1 && l4numNodes( relateList ) ) { if ( relateList->selected == 0 ) relateList->selected = (LINK4 *)l4last( relateList ) ; for (;; ) { if ( relateList->selected == 0 ) /* finished with matches for this list */ break ; relate = ((RELATE4LIST *)l4last( relateList ))->ptr ; if ( relate4eof( relate ) ) /* at eof means we must read this record */ if ( relation->inSort != relate4sortDone ) { rc = relate4bottom( relate ) ; if ( rc == r4eof ) /* no records, so can't skip back */ return r4bof ; else return rc ; } relate = ((RELATE4LIST *)relateList->selected)->ptr ; relate4setNotRead( relate ) ; /* This data file & its slaves */ if ( relation->inSort == relate4sortDone ) if ( r4dataListFind( &relation->sortDataList, relate ) ) return relate4sortPrevRecord( relation ) ; rc = relate4prevRecordInScan(relate) ; if ( rc == relate4skipped ) { if ( relate4eof( relate ) ) { if ( relation->inSort == relate4sortDone && relation->sortEofFlag == 1 ) { relation->sortRecOn-- ; /* move off eof on sort part */ relation->sortEofFlag = 0 ; } else { rc = d4go( relation->relate.data, d4recCount( relation->relate.data ) ) ; if ( rc < 0 ) return rc ; } } return 0 ; } if ( rc < 0 ) return rc ; rc2 = relate4blankSet( relate, (char)-1 ) ; if ( rc2 == r4locked || rc2 < 0 ) /* error or locked */ return rc2 ; if ( relate->master == 0 ) { if ( d4bof(relate->data) ) return r4bof ; if ( d4eof(relate->data) ) return r4eof ; } relateList->selected =(LINK4 *)l4prev( relateList, relateList->selected ) ; } } rc = relate4prevRelationList( relation, 0 ) ; if ( rc != relate4continue ) return rc ; } /* for ( ;; ) { if ( d4bof( relation->relate.data ) || d4eof( relation->relate.data ) ) { rc = r4bof ; break ; } if ( relation->inSort == relate4sortDone ) if ( r4dataListFind( &relation->sortDataList, &relation->relate ) ) { relate4setNotRead( &relation->relate ) ; return relate4sortPrevRecord( relation ) ; } rc = relate4prevRecordInScan( &relation->relate ) ; if ( rc < 0 ) return rc ; if ( rc != relate4skipped ) { rc = r4bof ; break ; } relate4setNotRead( &relation->relate ) ; rc = relate4lookup( &relation->relate, -1 ) ; if ( rc != 0 ) continue ; rc = relate4readRest( &relation->relate, -1 ) ; if ( rc == 0 || rc < 0 || rc == r4terminate ) return rc ; rc = 0 ; } if ( rc != 0 ) break ; */ } /* return r4bof ; */ } #endif int S4FUNCTION relate4querySet( RELATE4 *relate, const char *expr ) { int len ; if ( relate == 0 ) return -1 ; #ifdef S4VBASIC if ( c4parm_check( relate, 5, E94428 ) ) return -1 ; #endif if ( error4code( relate->codeBase ) < 0 ) return -1 ; relate4changed( relate ) ; u4free( relate->relation->exprSource ) ; relate->relation->exprSource = 0 ; if ( expr == 0 ) return 0 ; if ( expr[0] == 0 ) return 0 ; len = strlen( expr ) + 1 ; relate->relation->exprSource = (char *)u4allocEr( relate->codeBase, (long)len ) ; if ( relate->relation->exprSource == 0 ) return -1 ; memcpy( relate->relation->exprSource, expr, (unsigned int)len ) ; return 0 ; } #ifndef S4CLIENT int relate4readIn( RELATE4 *relate ) { int rc ; if ( error4code( relate->codeBase ) < 0 ) return -1 ; if ( relate->isRead ) return 0 ; if ( relate->master ) if ( relate->master->isRead == 0 ) { rc = relate4readIn( relate->master ) ; if ( rc == relate4filterRecord || rc == r4terminate ) return rc ; } return relate4lookup( relate, 1 ) ; } /* direction : -1 = look backwards, 0 = lookup only, 1 = look forwards */ static int relate4readRest( RELATE4 *relate, char direction ) { RELATE4 *slave ; int rc, scanDone ; #ifdef E4PARM_LOW if ( relate == 0 ) return error4( 0, e4parm_null, E94417 ) ; if ( direction < -1 || direction > 1 ) return error4( 0, e4parm, E94417 ) ; #endif #ifdef E4ANALYZE if ( error4code( relate->codeBase ) < 0 ) return e4codeBase ; #endif rc = 0 ; if ( relate->isRead == 0 ) { rc = relate4lookup( relate, direction ); if ( rc < 0 || rc == relate4filterRecord || rc == r4terminate ) return rc ; } scanDone = 0 ; for( slave = 0 ;; ) { if ( direction == 1 ) slave = (RELATE4 *)l4next( &relate->slaves, slave ) ; else slave = (RELATE4 *)l4prev( &relate->slaves, slave ) ; if ( slave == 0 ) break ; if ( slave->isRead == 0 ) if ( slave->relationType == relate4scan ) { /* if the currentRelateLevel is an upward master of ourselves, then make our master the currentRelateLevel so that the next skip will scan through us */ if ( relate4parent( slave->relation->currentRelateLevel, slave->master ) ) slave->relation->currentRelateLevel = slave->master ; if ( direction == 1 ) { d4top( slave->data ) ; #ifndef S4OFF_INDEX tfile4top( slave->dataTag->tagFile ) ; #endif } else { d4bottom( slave->data ) ; #ifndef S4OFF_INDEX tfile4bottom( slave->dataTag->tagFile ) ; #endif } } } for( slave = 0 ;; ) { if ( direction == 1 ) slave = (RELATE4 *)l4next( &relate->slaves, slave ) ; else slave = (RELATE4 *)l4prev( &relate->slaves, slave ) ; if ( slave == 0 ) return 0 ; if ( slave->relationType == relate4scan && scanDone == 1 ) { if ( slave->isRead == 0 ) { relate4blankSet( slave, (char)(-direction) ) ; /* do reverse of direction */ slave->isRead = 1 ; rc = relate4readRest( slave, direction ) ; } } else { rc = relate4readRest( slave, direction ) ; if ( slave->relationType == relate4scan && rc == 0 ) { switch ( direction ) { case -1: if ( !d4bof( slave->data ) ) scanDone = 1 ; break ; case 1: if ( !d4eof( slave->data ) ) scanDone = 1 ; break ; default: break ; } } } if ( rc < 0 || rc == relate4filterRecord || rc == r4terminate ) return rc ; } } static void relate4setNotRead( RELATE4 *relate ) { RELATE4 *slaveOn ; if ( relate->isRead ) { relate->isRead = 0 ; for( slaveOn = 0 ;; ) { slaveOn = (RELATE4 *)l4next(&relate->slaves,slaveOn) ; if ( slaveOn == 0 ) return ; relate4setNotRead( slaveOn ) ; } } } #endif int S4FUNCTION relate4skip( RELATE4 *relate, const long numSkip ) { int rc ; long numskip ; RELATION4 *relation ; CODE4 *c4 ; #ifdef S4CLIENT CONNECTION4RELATE_SKIP_INFO_IN info ; CONNECTION4 *connection ; int saveRc ; #else int sortStatus, rc2 ; signed char sign ; #ifdef S4REPORT #ifdef S4WINDOWS char countstring[22]; static long int scanrecCount = 0, selectrecCount = 0, sortrecCount = 0; HWND statwin; #endif #endif #ifndef S4SINGLE int oldReadLock ; #endif #endif #ifdef S4VBASIC if ( c4parm_check( relate, 5, E94418 ) ) return -1 ; #endif #ifdef E4PARM_HIGH if ( relate == 0 ) return error4( 0, e4parm_null, E94418 ) ; #endif c4 = relate->codeBase ; if ( error4code( c4 ) < 0 ) return e4codeBase ; numskip = numSkip ; relation = relate->relation ; if ( relation->isInitialized == 0 ) return error4( c4, e4info, E84406 ) ; relate = &relation->relate ; #ifdef S4CLIENT #ifdef E4ANALYZE if ( relate->data == 0 ) return error4( c4, e4struct, E94418 ) ; if ( relate->data->dataFile == 0 ) return error4( c4, e4struct, E94418 ) ; #endif connection = relate->data->dataFile->connection ; #ifdef E4ANALYZE if ( connection == 0 ) return error4( c4, e4struct, E94418 ) ; #endif connection4assign( connection, CON4RELATE_SKIP, 0, 0 ) ; info.relationId = relation->relationId ; info.numSkips = numskip ; connection4addData( connection, &info, sizeof( CONNECTION4RELATE_SKIP_INFO_IN ), 0 ) ; connection4send( connection ) ; saveRc = connection4receive( connection ) ; if ( saveRc < 0 ) return error4stack( c4, saveRc, E94418 ) ; saveRc = connection4status( connection ) ; if ( saveRc < 0 ) return connection4error( connection, c4, saveRc, E94418 ) ; rc = relate4unpack( relation, relate->data->dataFile->connection ) ; if ( rc < 0 ) return error4stack( c4, rc, E94418 ) ; return saveRc ; #else if ( numskip < 0 ) { if ( relation->skipBackwards == 0 ) return error4( c4, e4info, E84417 ) ; sign = -1 ; } else sign = 1 ; sortStatus = 0 ; rc = 0 ; #ifndef S4SINGLE /* suspend auto read locking from within the relate */ oldReadLock = c4->readLock ; c4->readLock = 0 ; #endif for( ; numskip ; ) { #ifdef S4REPORT #ifdef S4WINDOWS if( GetWindowWord( c4->hWnd, 8 ) == 666 ) statwin = c4->hWnd ; if ( statwin ) { if ( GetWindowWord( statwin, 6 ) == 0 ) { SetWindowWord( statwin, 6, 1 ) ; scanrecCount = sortrecCount = selectrecCount = 0 ; } scanrecCount++ ; if ( scanrecCount < 20 || ( scanrecCount % 20 ) == 0 ) { c4ltoa45( scanrecCount, countstring, sizeof( countstring ) -1 ) ; countstring[21] = 0 ; SendMessage( (HWND)GetWindowWord( statwin, 0 ), WM_SETTEXT, 0, (LPARAM)((LPSTR)countstring ) ) ; } } #endif #endif if ( sign > 0 ) { rc = relate4nextScanRecord( relation ) ; if ( rc == r4eof ) break ; } else { rc = relate4prevScanRecord( relation ) ; if ( rc == r4bof ) break ; } #ifdef S4SINGLE if ( rc < 0 || rc == r4terminate ) #else if ( rc < 0 || rc == r4locked || rc == r4terminate ) #endif break ; rc = relate4readRest( relate, sign ) ; if ( rc == relate4filterRecord ) continue ; if ( rc < 0 || rc == r4terminate ) break ; if ( relation->exprSource ) { rc2 = log4true(&relation->log ) ; if ( rc2 == r4terminate ) { rc = r4terminate ; break ; } if ( rc2 == 0 ) { if ( relation->inSort == relate4sortSkip ) /* must temporarily disable in order to get a matching scan if available */ { sortStatus = 1 ; relation->inSort = 0 ; } continue ; } } numskip -= sign ; } #ifndef S4SINGLE /* suspend auto read locking from within the relate */ c4->readLock = oldReadLock ; #endif #ifdef S4WINDOWS #ifdef S4REPORT if(GetWindowWord( c4->hWnd, 8 ) == 666 ) statwin = c4->hWnd; if ( statwin ) { selectrecCount++; if ( selectrecCount < 20 || (selectrecCount % 20) == 0 ) { c4ltoa45(selectrecCount,countstring,sizeof(countstring)-1); countstring[21] = 0; SendMessage((HWND)GetWindowWord(statwin,2),WM_SETTEXT,0,(LPARAM)((LPSTR)countstring)); } if ( relation->inSort ) { sortrecCount++; if( sortrecCount < 20 || (sortrecCount % 20) == 0) { c4ltoa45(sortrecCount,countstring,sizeof(countstring)-1); countstring[21] = 0; SendMessage((HWND)GetWindowWord(statwin,4),WM_SETTEXT,0,(LPARAM)((LPSTR)countstring)); } } } #endif #endif if ( sortStatus == 1 ) relation->inSort = relate4sortSkip ; return rc ; #endif } int S4FUNCTION relate4skipEnable( RELATE4 *relate, const int doEnable ) { if ( relate == 0 ) return -1 ; if ( error4code( relate->codeBase ) < 0 ) return -1 ; if ( relate->relation->skipBackwards != (char) doEnable ) { relate->relation->skipBackwards = (char) doEnable ; relate4changed( relate ) ; } return 0 ; } static void relate4sortFree( RELATION4 *relation, const int deleteSort ) { if ( relation == 0 ) return ; #ifndef S4CLIENT sort4free( &relation->sort ) ; u4free( relation->otherData ) ; relation->otherData = 0 ; if ( relation->sortedFile.hand >= 0 ) file4close( &relation->sortedFile ) ; r4dataListFree( &relation->sortDataList ) ; relation->inSort = 0 ; #endif if ( deleteSort ) { u4free( relation->sortSource ) ; relation->sortSource = 0 ; } } #ifndef S4CLIENT static int relate4sort( RELATE4 *relate ) { EXPR4 *sortExpr ; int rc, i, len ; long j, zero ; char nDbf, *sortKey ; R4DATA_LIST *r4data ; RELATION4 *relation ; CODE4 *c4 ; #ifdef S4SERVER LIST4 *oldList ; #endif zero = 0L ; #ifdef E4PARM_LOW if ( relate == 0 ) return error4( 0, e4parm_null, E94419 ) ; #endif c4 = relate->codeBase ; #ifdef E4ANALYZE if ( error4code( c4 ) < 0 ) return e4codeBase ; #endif relation = relate->relation ; relate = &relation->relate ; rc = 0 ; #ifdef S4SERVER oldList = tran4dataList( code4trans( c4 ) ) ; tran4dataListSet( code4trans( c4 ), &relation->localDataList ) ; #endif sortExpr = expr4parseLow( relate->data, relation->sortSource, 0 ) ; #ifdef S4SERVER tran4dataListSet( code4trans( c4 ), oldList ) ; #endif relation->inSort = relate4sortSkip ; relation->sortDoneFlag = 0 ; rc = relate4top( relate ) ; if ( rc ) /* no records satisfy the relate, or error */ { expr4free( sortExpr ) ; return rc ; } len = expr4key( sortExpr, &sortKey ) ; if ( len <= 0 ) { expr4free( sortExpr ) ; return -1 ; } #ifdef E4ANALYZE if ( relation->sortDataList.nLink != 0 ) return error4( c4, e4struct, E84413 ) ; #endif if ( r4dataListBuild( &relation->sortDataList, relate, sortExpr, relate4exact ) < 0 ) { expr4free( sortExpr ) ; return -1 ; } if ( r4dataListMassage( &relation->sortDataList ) < 0 ) { expr4free( sortExpr ) ; return -1 ; } nDbf = (char)relation->sortDataList.nLink ; relation->sortOtherLen = (int)(nDbf * sizeof( long )) ; relation->otherData = (char *)u4alloc( (long)relation->sortOtherLen ) ; if ( relation->otherData == 0 ) return -1 ; rc = sort4initFree( &relation->sort, c4, len, relation->sortOtherLen, relate ) ; if ( rc ) { expr4free( sortExpr ) ; return rc ; } #ifndef S4FOX #ifndef S4CLIPPER switch( expr4type( sortExpr ) ) { #ifdef S4NDX case r4num: case r4num_doub: case r4date: case r4date_doub: relation->sort.cmp = (S4CMP_FUNCTION *)t4cmpDoub ; break ; #endif #ifdef S4MDX case r4num: relation->sort.cmp = (S4CMP_FUNCTION *)c4bcdCmp ; break ; case r4date: relation->sort.cmp = (S4CMP_FUNCTION *)t4cmpDoub ; break ; #endif default: break ; } #endif #endif /* call relate4top() again in case free-ups occurred */ rc = relate4top( relate ) ; if ( rc ) /* no records satisfy the relate, or error */ { expr4free( sortExpr ) ; return rc ; } for ( j = 0L, rc = 0 ; !rc ; j++, rc = relate4skip( relate, 1L ) ) { for ( i = 0, r4data = 0 ;; i++ ) { r4data = (R4DATA_LIST *)l4next( &relation->sortDataList, r4data ) ; if ( r4data == 0 ) break ; if ( d4eof( r4data->data ) || d4bof( r4data->data ) ) /* relate4blank case */ memcpy( relation->otherData + i * sizeof(long), (void *)&zero, sizeof( long ) ) ; else memcpy( relation->otherData + i * sizeof(long), (void *)&r4data->data->recNum, sizeof( long ) ) ; } if ( expr4key( sortExpr, &sortKey ) < 0 ) { expr4free( sortExpr ) ; u4free( relation->otherData ) ; relation->otherData = 0 ; return -1 ; } if ( sort4put( &relation->sort, j, sortKey, relation->otherData ) < 0 ) { expr4free( sortExpr ) ; u4free( relation->otherData ) ; relation->otherData = 0 ; return -1 ; } } expr4free( sortExpr ) ; if ( rc < 0 || rc == r4terminate ) { u4free( relation->otherData ) ; relation->otherData = 0 ; return rc ; } relation->sortRecCount = j ; relation->inSort = relate4sortDone ; if ( relation->skipBackwards ) if ( file4tempLow( &relation->sortedFile, c4, 1 ) < 0 ) { u4free( relation->otherData ) ; relation->otherData = 0 ; return -1 ; } if ( sort4getInitFree( &relation->sort, relate ) < 0 ) return -1 ; relation->sortRecOn = relation->sortFilePos = relation->sortRecTo = 0L ; return 0 ; } static int relate4sortGetRecord( RELATION4 *relation, const long num ) { int len, i, rc ; char *key ; char *other = 0 ; R4DATA_LIST *linkOn ; long j, numLeft ; #ifdef S4DATA_ALIGN long longPtr ; #endif if ( error4code( relation->relate.codeBase ) < 0 ) return -1 ; if ( num <= 0 ) return r4bof ; relation->sortEofFlag = 0 ; numLeft = num - relation->sortRecTo ; if ( numLeft <= 0 ) /* already read, so just return from file */ { if ( relation->skipBackwards == 0 ) return -1 ; len = file4read( &relation->sortedFile, ( num - 1 ) * relation->sortOtherLen, relation->otherData, (unsigned int)relation->sortOtherLen ) ; if ( len != relation->sortOtherLen ) /* free up and exit */ return -1 ; other = relation->otherData ; } else while ( numLeft-- ) { if ( relation->sortDoneFlag == 1 ) /* sort is finished, therefore must be eof */ return r4eof ; rc = sort4get( &relation->sort, &j, (void **)&key, (void **)&other ) ; if ( rc ) /* no more items, or error */ { sort4free( &relation->sort ) ; if ( rc == 1 ) { relation->sortEofFlag = 1 ; relation->sortDoneFlag = 1 ; return r4eof ; } else return rc ; } relation->sortRecTo++ ; if ( relation->skipBackwards ) { file4write( &relation->sortedFile, relation->sortFilePos, other, (unsigned int)relation->sortOtherLen ) ; relation->sortFilePos += relation->sortOtherLen ; } } /* now read the database records in */ for ( i = 0, linkOn = 0 ;; i++ ) { linkOn = (R4DATA_LIST *)l4next( &relation->sortDataList, linkOn ) ; if ( linkOn == 0 ) return 0 ; /* note that the sort positions all blanks to eof whereas in non-sort it is not consistent whether it is positioned bof or eof. For sort it doesn't matter because further skips all go through the sort, therefore we already know the record order thus it doesn't need to be calculated. This is a small efficience. */ #ifdef S4DATA_ALIGN memcpy( &longPtr, (void *)(other + i*sizeof(longPtr)), sizeof(longPtr) ) ; if ( longPtr == 0 ) /* relate4blank case */ rc = d4goEof( linkOn->data ) ; else rc = d4go( linkOn->data, longPtr ) ; #else if ( *((long *)(other) + i ) == 0 ) /* relate4blank case */ rc = d4goEof( linkOn->data ) ; else rc = d4go( linkOn->data, *((long *)(other) + i ) ) ; #endif if ( rc < 0 ) return rc ; linkOn->relate->isRead = 1 ; } } static int relate4sortNextRecord( RELATION4 *relation ) { int rc ; if ( error4code( relation->relate.codeBase ) < 0 ) return -1 ; rc = relate4sortGetRecord( relation, relation->sortRecOn + 1 ) ; if ( rc == 0 ) relation->sortRecOn++ ; if ( rc == r4eof ) relation->sortRecOn = relation->sortRecCount + 1 ; return rc ; } static int relate4sortPrevRecord( RELATION4 *relation ) { int rc ; if ( error4code( relation->relate.codeBase ) < 0 ) return -1 ; rc = relate4sortGetRecord( relation, relation->sortRecOn - 1 ) ; if ( rc == 0 ) relation->sortRecOn-- ; return rc ; } #endif int S4FUNCTION relate4sortSet( RELATE4 *relate, const char *expr ) { RELATION4 *relation ; int len ; if ( relate == 0 ) return -1 ; #ifdef S4VBASIC if ( c4parm_check( relate, 5, E94429 ) ) return -1 ; #endif if ( error4code( relate->codeBase ) < 0 ) return -1 ; relation = relate->relation ; relate = &relation->relate ; relate4changed( relate ) ; u4free( relation->sortSource ) ; relation->sortSource = 0 ; if ( expr ) if ( expr[0] ) { len = strlen( expr ) ; relation->sortSource = (char *)u4allocEr( relate->codeBase, (long)len + 1L ) ; if ( relation->sortSource == 0 ) return -1 ; memcpy( relation->sortSource, expr, (unsigned int)len ) ; } return 0 ; } #ifdef S4CLIENT static int relate4add( CONNECTION4 *connection, RELATE4 *relate, unsigned int *relatePos, unsigned int *flexPos, char *relateData ) { int len, savePos ; RELATE4 *slaveOn ; CONNECTION4RELATE *info ; TAG4 *tag ; /* add this relate's info */ savePos = *relatePos ; info = (CONNECTION4RELATE *)( relateData + *relatePos ) ; info->matchLen = relate->matchLen ; info->relationType = relate->relationType ; info->errorAction = relate->errorAction ; info->numSlaves = relate->slaves.nLink ; info->clientId = data4clientId( relate->data ) ; tag = relate->dataTag ; if ( tag == 0 ) info->dataTagName.offset = 0 ; else { len = strlen( tag->tagFile->alias ) + 1 ; info->dataTagName.offset = *flexPos ; connection4addData( connection, tag->tagFile->alias, len, 0 ) ; *flexPos += len ; } if ( relate->masterExpr == 0 ) info->masterExpr.offset = 0 ; else { len = strlen( relate->masterExpr->source ) + 1 ; info->masterExpr.offset = *flexPos ; connection4addData( connection, relate->masterExpr->source, len, 0 ) ; *flexPos += len ; } if ( relate->data == 0 ) info->dataAccessName.offset = 0 ; else { len = strlen( dfile4name( relate->data->dataFile ) ) + 1 ; info->dataAccessName.offset = *flexPos ; connection4addData( connection, dfile4name( relate->data->dataFile ), len, 0 ) ; *flexPos += len ; } *relatePos += sizeof( CONNECTION4RELATE ) ; /* and do it's slaves: */ for ( slaveOn = 0 ;; ) { slaveOn = (RELATE4 *)l4next( &relate->slaves, slaveOn ) ; if ( slaveOn == 0 ) break ; relate4add( connection, slaveOn, relatePos, flexPos, relateData ) ; } return 0 ; } static int relate4clientInit( RELATE4 *master ) { CONNECTION4RELATE_INIT_INFO_IN info ; CONNECTION4RELATE_INIT_INFO_OUT *out ; int rc, relateCount, i ; RELATE4 *relateOn ; CONNECTION4 *connection ; unsigned int relatePos, flexPos, exprLen, sortLen ; char *relateData ; CODE4 *c4 ; #ifdef E4PARM_LOW if ( master == 0 ) return error4( 0, e4parm_null, E94421 ) ; #endif c4 = master->codeBase ; connection = master->data->dataFile->connection ; relateCount = 1 ; memset( &info, 0, sizeof( CONNECTION4RELATE_INIT_INFO_IN ) ) ; for( relateOn = master ;; relateCount++ ) { if ( relate4next( &relateOn ) == 2 ) break ; #ifdef E4MISC if ( relateOn->data != 0 ) { if ( connection == 0 ) connection = relateOn->data->dataFile->connection ; else if ( connection != relateOn->data->dataFile->connection ) /* multi-servers not supported on relations */ return error4( c4, e4notSupported, E84414 ) ; } #endif } #ifdef E4ANALYZE if ( connection == 0 ) return error4( c4, e4struct, E94421 ) ; #endif if ( relateCount == 0 || connection == 0 ) return error4( c4, e4relate, E84415 ) ; if ( master->relation->needsFreeing == 1 ) info.relationId = master->relation->relationId ; else info.relationId = 0 ; info.relateOffset = sizeof( CONNECTION4RELATE_INIT_INFO_IN ) ; info.flexOffset = info.relateOffset + relateCount * sizeof( CONNECTION4RELATE ) ; info.relation.skipBackwards = master->relation->skipBackwards ; info.bitmapDisable = master->relation->bitmapDisable ; info.masterClientId = master->data->clientId ; if ( master->relation->exprSource == 0 ) { info.relation.exprSource.offset = 0 ; exprLen = 0 ; } else { exprLen = strlen( master->relation->exprSource ) + 1 ; info.relation.exprSource.offset = info.flexOffset ; } if ( master->relation->sortSource == 0 ) { info.relation.sortSource.offset = 0 ; sortLen = 0 ; } else { sortLen = strlen( master->relation->sortSource ) + 1 ; info.relation.sortSource.offset = info.flexOffset + exprLen ; } connection4assign( connection, CON4RELATE_INIT, 0, 0 ) ; connection4addData( connection, &info, sizeof( CONNECTION4RELATE_INIT_INFO_IN ), 0 ) ; relateData = (char *)u4allocFree( c4, relateCount * sizeof( CONNECTION4RELATE ) ) ; if ( relateData == 0 ) return error4stack( c4, e4memory, E94421 ) ; connection4addData( connection, relateData, relateCount * sizeof( CONNECTION4RELATE ), 0 ) ; if ( exprLen != 0 ) connection4addData( connection, master->relation->exprSource, exprLen, 0 ) ; if ( sortLen != 0 ) connection4addData( connection, master->relation->sortSource, sortLen, 0 ) ; relatePos = 0 ; flexPos = info.flexOffset + exprLen + sortLen ; rc = relate4add( connection, master, &relatePos, &flexPos, relateData ) ; if ( rc < 0 ) { u4free( relateData ) ; return error4stack( c4, rc, E94421 ) ; } connection4send( connection ) ; u4free( relateData ) ; rc = connection4receive( connection ) ; if ( rc < 0 ) return error4stack( c4, rc, E94421 ) ; rc = connection4status( connection ) ; if ( rc < 0 ) return connection4error( connection, c4, rc, E84401 ) ; if ( rc != 0 ) return rc ; if ( connection4len( connection ) != (long)sizeof( CONNECTION4RELATE_INIT_INFO_OUT ) + relateCount * (long)sizeof( relateOn->id ) ) return error4stack( c4, e4packetLen, E94421 ) ; out = (CONNECTION4RELATE_INIT_INFO_OUT *)connection4data( connection ) ; master->relation->relationId = out->relationId ; #ifdef E4ANALYZE if ( sizeof( relateOn->id ) != sizeof( unsigned short int ) ) return error4( c4, e4struct, E94421 ) ; #endif out++ ; /* go to the end of out for the variable length data */ for( relateOn = master, i = 0 ;; i++ ) { if ( relateOn == 0 ) break ; relateOn->id = *((unsigned short int *)(((char *)out) + i * sizeof( relateOn->id ) ) ) ; rc = relate4next( &relateOn ) ; if ( rc == 2 ) break ; } master->relation->isInitialized = 1 ; return 0 ; } #endif #ifndef S4CLIENT static int relate4topInit( RELATE4 *relate ) { RELATION4 *relation ; int rc ; CODE4 *c4 ; #ifndef S4OPTIMIZE_OFF int has_opt ; #endif #ifdef S4SERVER LIST4 *oldList ; #endif #ifdef S4VBASIC if ( c4parm_check( relate, 5, E94422 ) ) return -1 ; #endif #ifdef E4PARM_HIGH if( relate == 0 ) return error4( 0, e4parm_null, E94422 ) ; #endif c4 = relate->codeBase ; if ( error4code( c4 ) < 0 ) return e4codeBase ; relation = relate->relation ; relate = &relation->relate ; rc = 0 ; if ( relation->inSort == relate4sortDone ) if ( relation->skipBackwards == 0 ) relate4changed( relate ) ; relate->dataTag = relate->data->tagSelected ; if ( relation->isInitialized == 0 ) { #ifndef S4OPTIMIZE_OFF has_opt = (char)c4->hasOpt ; #endif if ( rc < 0 ) return rc ; relation->bitmapsFreed = 0 ; if ( relation->exprSource ) { #ifdef S4SERVER oldList = tran4dataList( code4trans( c4 ) ) ; tran4dataListSet( code4trans( c4 ), &relation->localDataList ) ; #endif relation->log.expr = expr4parseLow( relate->data, relation->exprSource, 0 ) ; #ifdef S4SERVER tran4dataListSet( code4trans( c4 ), oldList ) ; #endif if ( relation->log.expr == 0 ) return -1 ; if ( log4bitmapDo( &relation->log ) < 0 ) relation->bitmapsFreed = 1 ; log4determineEvaluationOrder( &relation->log ) ; } if ( relate4buildScanList( 0, relate, relation ) < 0 ) return -1 ; /* relation->relateList.selected = (LINK4 *)l4first( &relation->relateList) ;*/ relation->isInitialized = 1 ; if ( relation->sortSource ) { rc = relate4sort( relate ) ; if ( rc < 0 || rc == r4terminate ) return rc ; } #ifndef S4OPTIMIZE_OFF if ( has_opt ) code4optRestart( c4 ) ; #endif } return 0 ; } #endif int S4FUNCTION relate4top( RELATE4 *relate ) { RELATION4 *relation ; int rc ; CODE4 *c4 ; DATA4 *d4 ; #ifdef S4CLIENT CONNECTION4RELATE_TOP_INFO_IN info ; CONNECTION4 *connection ; int saveRc ; #else long rec ; int rc2 ; #ifndef S4OFF_MULTI int oldReadLock ; #endif #endif #ifdef S4VBASIC if ( c4parm_check( relate, 5, E94422 ) ) return -1 ; #endif #ifdef E4PARM_HIGH if( relate == 0 ) return error4( 0, e4parm_null, E94422 ) ; #endif c4 = relate->codeBase ; if ( error4code( c4 ) < 0 ) return e4codeBase ; relation = relate->relation ; relate = &relation->relate ; d4 = relate->data ; rc = 0 ; #ifdef S4CLIENT #ifdef E4ANALYZE if ( d4 == 0 ) return error4( c4, e4struct, E94422 ) ; if ( d4->dataFile == 0 ) return error4( c4, e4struct, E94422 ) ; #endif if ( relation->isInitialized == 0 || relate->dataTag != d4->tagSelected ) { relate->dataTag = d4->tagSelected ; rc = relate4clientInit( relate ) ; if ( rc != 0 ) return rc ; } #ifdef S4CB51 #ifndef S4OFF_MULTI if ( c4->readLock ) { rc = relate4lock( relate ) ; if ( rc != 0 ) return rc ; } #endif #endif connection = d4->dataFile->connection ; #ifdef E4ANALYZE if ( connection == 0 ) return error4( c4, e4struct, E94422 ) ; #endif connection4assign( connection, CON4RELATE_TOP, 0, 0 ) ; info.relationId = relation->relationId ; connection4addData( connection, &info, sizeof( CONNECTION4RELATE_TOP_INFO_IN ), 0 ) ; connection4send( connection ) ; saveRc = connection4receive( connection ) ; if ( saveRc < 0 ) return error4stack( c4, saveRc, E94422 ) ; saveRc = connection4status( connection ) ; if ( saveRc < 0 ) return connection4error( connection, c4, saveRc, E94422 ) ; rc = relate4unpack( relation, d4->dataFile->connection ) ; if ( rc != 0 ) return error4stack( c4, rc, E94422 ) ; return saveRc ; #else #ifndef S4OFF_MULTI oldReadLock = c4->readLock ; c4->readLock = 0 ; #endif for ( ;; ) /* used to minimize return code areas, just break out... */ { rc = relate4topInit( relate ) ; if ( rc != 0 ) break ; relate4setNotRead( relate ) ; relation->currentRelateLevel = 0 ; relate4nextRelationList( relation, 1 ) ; if ( relation->inSort == relate4sortDone ) { relation->sortRecOn = 0 ; rc = relate4sortNextRecord( relation ) ; } else rc = d4top( d4 ) ; if ( rc ) /* eof or error */ break ; if ( relation->exprSource ) { rec = d4recNo( d4 ) ; if ( f4flagIsSetFlip( &relate->set, (unsigned long)rec ) == 0 ) { #ifndef S4INDEX_OFF if ( relate->dataTag ) { while ( f4flagIsSetFlip( &relate->set, (unsigned long)rec ) == 0 ) { #ifdef S4HAS_DESCENDING rc = (int)tfile4dskip( relate->dataTag->tagFile, 1L ) ; #else rc = (int)tfile4skip( relate->dataTag->tagFile, 1L ) ; #endif if ( rc != 1 ) { if ( rc == 0 ) { d4goEof( d4 ) ; rc = r4eof ; } break ; } rec = tfile4recNo( relate->dataTag->tagFile ) ; } if ( rc == r4eof ) break ; } else { #endif rec = f4flagGetNextFlip( &relate->set, 1L, 1 ) + 1L ; if ( d4recCountLessEq( d4, rec ) == 0 ) { d4goEof( d4 ) ; rc = r4eof ; break ; } #ifndef S4INDEX_OFF } #endif } rc = d4go( d4, rec ) ; if ( rc != 0 ) break ; } rc = relate4readRest( relate, 1 ) ; if ( rc == relate4filterRecord ) { rc = relate4skip( relate, 1L ) ; break ; } if ( rc < 0 || rc == r4terminate ) break ; if ( relation->exprSource ) { rc2 = log4true( &relation->log ) ; if ( rc2 == r4terminate ) { rc = r4terminate ; break ; } if ( rc2 == 0 ) { if ( relation->inSort == relate4sortSkip ) /* must temporarily disable in order to get a matching scan if available */ { relation->inSort = 0 ; rc = relate4skip( relate, 1L ) ; relation->inSort = relate4sortSkip ; } else rc = relate4skip( relate, 1L ) ; } } break ; } #ifndef S4OFF_MULTI c4->readLock = oldReadLock ; #endif return rc ; #endif } int S4FUNCTION relate4type( RELATE4 *relate, int relateType ) { int rc ; #ifdef E4PARM_HIGH if ( relate == 0 ) return error4( 0, e4parm_null, E94423 ) ; if ( relateType != relate4exact && relateType != relate4scan && relateType != relate4approx ) return error4( relate->codeBase, e4parm, E84416 ) ; #endif rc = relate->relationType ; if ( rc != relateType ) { relate->relationType = relateType ; relate4changed( relate ) ; } return rc ; } #ifdef S4CB51 int S4FUNCTION relate4unlock( RELATE4 *relate ) { #ifndef S4SINGLE DATA4 *dataOn ; #ifdef E4PARM_HIGH if ( relate == 0 ) return error4( 0, e4parm_null, E94424 ) ; #endif if ( !relate->relation->locked ) return 0 ; for ( dataOn = (DATA4 *)l4first( tran4dataList( code4trans( relate->codeBase ) ) ) ; dataOn ; dataOn = (DATA4 *)l4next( tran4dataList ( code4trans( relate->codeBase ) ), dataOn ) ) if ( relate4dbfInRelation( relate, dataOn ) ) d4unlock( dataOn ) ; relate->relation->locked = 0 ; #endif return 0 ; } #endif #ifdef S4VB_DOS RELATE4 *S4FUNCTION relate4createSlave_v( RELATE4 *master, DATA4 *slave, char *masterExpr, TAG4 *slaveTag ) { return relate4createSlave( master, slave, c4str( masterExpr ), slaveTag ) ; } char * relate4masterExpr_v( RELATE4 *r4 ) { #ifdef S4VBASIC if ( c4parm_check( r4, 5, "relate4masterExpr():" ) ) return 0 ; #endif return v4str(r4->masterExpr->source) ; } int S4FUNCTION relate4querySet_v ( RELATE4 *relate, char *expr ) { return relate4querySet( relate, c4str(expr) ) ; } int S4FUNCTION relate4sortSet_v ( RELATE4 *relate, char *expr ) { return relate4sortSet( relate, c4str( expr ) ) ; } #endif