C++ BSFN CACHE MULTIPLE SEGMENT IN INDEX

WSW

Member
I am new to C++ programing and am having a problem where I am trying to use multiple segments in an index for cache. Any one of the three segments will work alone but when multiple are selected only the first is being used with the others ignored. Thus any change in the other two segments data will not force a new cache record. The cache record with the first match will be updated instead.

The definition is identical for both the initialize and terminate functions which I have not included below.

Any help in identifying what I have done wrong would be appreciated.

HCACHE hUtilCache = NULL;
HJDECURSOR hUtilCursor = NULL;
JDECMINDEXSTRUCT Index1[1];
/************************************************************************
* Declare structures
************************************************************************/
DSUTILCACHE dsUtilCache;
/************************************************************************
* Main Processing
************************************************************************/
/* Initialize Data Structures to NULL */
memset(&dsUtilCache, 0x00, sizeof(DSUTILCACHE));

/* Initialize Cache Index to NULL */
memset(&Index1, 0x00, sizeof(JDECMINDEXSTRUCT));

/* Set up Index to Cache */
Index1->nNumSegments = 1;
Index1->CacheKey[0].nOffset = offsetof(DSUTILCACHE, PayPeriodEndDate);
Index1->CacheKey[0].nSize = sizeof(dsUtilCache.PayPeriodEndDate);
Index1->CacheKey[0].idDataType = EVDT_JDEDATE;
Index1->CacheKey[1].nOffset = offsetof(DSUTILCACHE, MinorityCode);
Index1->CacheKey[1].nSize = sizeof(dsUtilCache.MinorityCode);
Index1->CacheKey[1].idDataType = EVDT_CHAR;
Index1->CacheKey[2].nOffset = offsetof(DSUTILCACHE, Sex);
Index1->CacheKey[2].nSize = 1;
Index1->CacheKey[2].idDataType = EVDT_CHAR;

/* Get Cache Handle */
jdeCacheCode = jdeCacheInit(hUser, &hUtilCache, lpDS->UtilizationCache, Index1);

/* Determine Processing Mode: Add/Retrieval */
if (lpDS->ProcessingMode == _J('0'))
{
/* Add/Update Mode */
dsUtilCache.PayPeriodEndDate = lpDS->PayPeriodEndDate;
jdeStrncpy(dsUtilCache.MinorityCode, (const JCHAR *) (lpDS->MinorityCode),
DIM(dsUtilCache.MinorityCode)-1);
dsUtilCache.Sex = lpDS->Sex;

/* Determine whether the Cache has records */
if (jdeCacheGetNumRecords(hUtilCache) > (long) 0)
{

******* This is what does not work properly

/* Open Cursor - Point to first record in Cache */
jdeCacheCode = jdeCacheOpenCursor(hUtilCache, &hUtilCursor);
/* Attempt to Fetch Matching Record */
jdeCacheCode = jdeCacheFetchPosition(hUtilCache, hUtilCursor, (void *) &dsUtilCache,
1, (void *) &dsUtilCache, (long int) sizeof(dsUtilCache));


*******


if (jdeCacheCode == JDECM_FAILED)
{
/* Add Detail to Cache */
}
else
{
/* Update Cache */
jdeCacheCode = jdeCacheUpdate(hUtilCache, hUtilCursor, (void *) &dsUtilCache,
(long int) sizeof(dsUtilCache));
}
}
else
{
/* Add Detail to Cache */
jdeCacheCode = jdeCacheAdd(hUtilCache, (void *) &dsUtilCache, sizeof(dsUtilCache));
}
}
else if (lpDS->ProcessingMode == _J('1'))
{
/* Retrieval Mode */
}
 
Without knowing the full data structure of the cache record it's tough, but it appears that MinorityCode is a string (array of characters) but the cache init structure has it defined as a CHAR.

Perhaps it should be:

Index1->CacheKey[1].nOffset = offsetof(DSUTILCACHE, MinorityCode);
Index1->CacheKey[1].nSize = DIM(dsUtilCache.MinorityCode);
Index1->CacheKey[1].idDataType = EVDT_STRING;

Craig
 
I changed the keys as suggested.
This is the definition of the input record:

JCHAR UtilizationCache[11];
JDEDATE PayPeriodEndDate;
JCHAR MinorityCode[3];
JCHAR Sex;
MATH_NUMERIC HoursWorked;
JCHAR ProcessingMode;
MATH_NUMERIC Count;

I just set the index like this:
Index1->nNumSegments = 3;
Index1->CacheKey[0].nOffset = offsetof(DSUTILCACHE, PayPeriodEndDate);
Index1->CacheKey[0].nSize = sizeof(dsUtilCache.PayPeriodEndDate);
Index1->CacheKey[0].idDataType = EVDT_JDEDATE;
Index1->CacheKey[1].nOffset = offsetof(DSUTILCACHE, MinorityCode);
Index1->CacheKey[1].nSize = sizeof(dsUtilCache.MinorityCode);
Index1->CacheKey[1].idDataType = EVDT_STRING;
Index1->CacheKey[2].nOffset = offsetof(DSUTILCACHE, Sex);
Index1->CacheKey[2].nSize = sizeof(dsUtilCache.Sex);
Index1->CacheKey[2].idDataType = EVDT_STRING;

Here are the test results:
*****Initialize Cache*****
UtilizationCache: UT1
OA_BSFN_RETURN_VALUE: 0

*****Combo 1 = 01/01/2013 01 A
UtilizationCache: UT1
PayPeriodEndDate: 01/01/2013
MinorityCode: 01
Sex: A
HoursWorked: 8.00
ProcessingMode: 0
Count: 1.00
OA_BSFN_RETURN_VALUE: 0

*****Combo 2 = 01/01/2013 01 B
UtilizationCache: UT1
PayPeriodEndDate: 01/01/2013
MinorityCode: 01
Sex: B
HoursWorked: 8.00
ProcessingMode: 0
Count: 1.00
OA_BSFN_RETURN_VALUE: 0

*****Combo 3 = 01/01/2013 02 B
UtilizationCache: UT1
PayPeriodEndDate: 01/01/2013
MinorityCode: 02
Sex: B
HoursWorked: 8.00
ProcessingMode: 0
Count: 1.00
OA_BSFN_RETURN_VALUE: 0

*****Results feed back*****
UtilizationCache: UT1
PayPeriodEndDate: 01/01/2013
MinorityCode: 01
Sex: A
HoursWorked: 8.00
ProcessingMode: 1
Count: 1.00
OA_BSFN_RETURN_VALUE: 0

UtilizationCache: UT1
PayPeriodEndDate: 01/01/2013
MinorityCode: 01
Sex: B
HoursWorked: 16.00
ProcessingMode: 1
Count: 2.00
OA_BSFN_RETURN_VALUE: 0

UtilizationCache: UT1
PayPeriodEndDate:
MinorityCode:
Sex:
HoursWorked:
ProcessingMode: 1
Count:
OA_BSFN_RETURN_VALUE: 0

There were three different keys entered but only two worked properly. There was not separation on the Minority Code. In JDE this is a two position string value.
 
I think you need to update the structure again. Do not use sizeof for strings, use the DIM macro for number of elements. The Sex member is a JCHAR and has a size of 1 (sizeof will return 2 as it's 2 bytes)

Index1->CacheKey[1].nOffset = offsetof(DSUTILCACHE, MinorityCode);
Index1->CacheKey[1].nSize = DIM(dsUtilCache.MinorityCode);
Index1->CacheKey[1].idDataType = EVDT_STRING;
Index1->CacheKey[2].nOffset = offsetof(DSUTILCACHE, Sex);
Index1->CacheKey[2].nSize = 1
Index1->CacheKey[2].idDataType = EVDT_CHAR;
 
STILL NO GO. ONLY THE DATE APPEARS TO WORK.

COMPLETE CODE IS NOW ATTACHED.

THANK YOU MUCH FOR YOUR TIME

RESULTS ARE:

*** 1ST ENTRY FOR 01/01/2013 01 M
UtilizationCache: UT1
PayPeriodEndDate: 01/01/2013
MinorityCode: 01
Sex: M
HoursWorked: 8.00
ProcessingMode: 0
Count: 1.00
OA_BSFN_RETURN_VALUE: 0

*** 2ND ENTRY FOR 01/01/2013 02 M
UtilizationCache: UT1
PayPeriodEndDate: 01/01/2013
MinorityCode: 02
Sex: M
HoursWorked: 8.00
ProcessingMode: 0
Count: 1.00
OA_BSFN_RETURN_VALUE: 0

*** 3RD ENTRY FOR 01/01/2013 01 F
UtilizationCache: UT1
PayPeriodEndDate: 01/01/2013
MinorityCode: 01
Sex: F
HoursWorked: 8.00
ProcessingMode: 0
Count: 1.00
OA_BSFN_RETURN_VALUE: 0

*** RETRIEVE DATA
UtilizationCache: UT1
PayPeriodEndDate: 01/01/2013
MinorityCode: 01
Sex: M
HoursWorked: 24.00
ProcessingMode: 1
Count: 3.00

*** ALL ROWS COMBINED?????

OA_BSFN_RETURN_VALUE: 0
UtilizationCache: UT1
PayPeriodEndDate:
MinorityCode:
Sex:
HoursWorked:
ProcessingMode: 1
Count:
OA_BSFN_RETURN_VALUE: 0
 

Attachments

  • 185061-B579719.C.TXT
    24.9 KB · Views: 62
Its kind of annoying that JDE did this too us. They should have been consistent and just used sizeof for all data types. This is really a hassle when moving from a non-unicode version of E1 like Xe to a unicode E1 release. I have a BSFN that is bascially just a bunch of commonly used utility macros (I just include it in every BSFN). In my acme commons utility macros I created two macros to address this very situation and I haven't had a bug yet where I used sizeof for a string or JCHAR param (I have been using these macros since 2009). Also makes the code less verbose. Here are the macros along with the notes from the .h file:


The macros let you write jdeCache init code that looks like this (and you dont have to think about sizeof vs. DIM):

<font class="small">Code:</font><hr /><pre>
ushort i=0;
JDECMINDEXSTRUCT dsIndex[1]={0};
Dxxxxxxx_xxxxxxxxxx dsCache;

dsIndex[0].nKeyID = 1;
dsIndex[0].nNumSegments = 2;
acmeCacheInitKey(&dsIndex[0].CacheKey[i++], &dsCache, Dxxxxxxx_xxxxxxxxxx, mnMyNumber, EVDT_MATH_NUMERIC);
acmeCacheInitKey(&dsIndex[0].CacheKey[i++], &dsCache, Dxxxxxxx_xxxxxxxxxx, szMyString, EVDT_STRING);
</pre><hr />


Instead of code that looks like this:
<font class="small">Code:</font><hr /><pre>
ushort i=0;
JDECMINDEXSTRUCT dsIndex[1]={0};
Dxxxxxxx_xxxxxxxxxx dsCache;

dsIndex[0].CacheKey.nOffset = offsetof(Dxxxxxxx_xxxxxxxxxx, mnMyNumber);
dsIndex[0].CacheKey.nSize = sizeof(dsCache.mnMyNumber);
dsIndex[0].CacheKey[i++].idDataType = EVDT_MATH_NUMERIC;

dsIndex[0].CacheKey.nOffset = offsetof(Dxxxxxxx_xxxxxxxxxx, szMyString);
dsIndex[0].CacheKey.nSize = DIM(dsCache.szMyString);
dsIndex[0].CacheKey[i++].idDataType = EVDT_STRING;
</pre><hr />



Macro Definitions in BSFN:

<font class="small">Code:</font><hr /><pre>
/***************************************************************************************
Macro: acmeCacheKeySize(dsm, idType)

Notes: Sets the Cache Key size.
With the addition of unicode, the CacheKey.nSize parameter can no longer be set with sizeof for
all data types. To help avoid simple bugs/mistakes, this provides a consistent call to set this
value.

As an alternate for future code, acmeCacheInitKey can be used as a one line call
to set all three of the common values for the cache key.

params:
dsm = the cache data structure and member in the form of dsCache.memberName
idType = the same value assigned to CacheKey.idDataType

Returns:
the correct size value for the cache key (jde_n_char for JCHAR and Strings, jde_n_byte for everything else)

Example:
dsIndex[0].CacheKey.nOffset = offsetof(F4211, sdkcoo);
dsIndex[0].CacheKey.nSize = acmeCacheKeySize(dsCache.sdkcoo, EVDT_STRING);
dsIndex[0].CacheKey[i++].idDataType = EVDT_STRING;
*/
#define acmeCacheKeySize(dsm, idType) \
(idType == EVDT_CHAR || idType == EVDT_STRING ? sizeof(dsm) / sizeof(JCHAR) : sizeof(dsm))


/***************************************************************************************
Macro: acmeCacheInitKey(pKey, pCacheDS, DSNAME, MEMBERNAME, idType)

Notes: Used to set the following cache key values with one line
CacheKey.nOffset
CacheKey.nSize
CacheKey.idDataType

params:
pKey = pointer to JDECMINDEXSTRUCT.CacheKey
pCacheDS = pointer to a the cache struct to be used as the cache key
DSNAME = the cache key struct name
MEMBERNAME = the cache key struct member name being set
idType = the data type (EVDT_CHAR, EVDT_STRING, EVDT_MATH_NUMERIC, EVDT_JDEDATE, etc.)

Returns:
void

Example:
F4211 dsCache;
int i=0
dsIndex[0].nKeyID = 1;
dsIndex[0].nNumSegments = 2;
acmeCacheInitKey(&dsIndex[0].CacheKey[i++], &dsCache, F4211, sdkcoo, EVDT_STRING);
acmeCacheInitKey(&dsIndex[0].CacheKey[i++], &dsCache, F4211, sddoco, EVDT_MATH_NUMERIC);

Developer Notes:
The wierd looking while expession is used to avoid compiler warnings about constants and guaruntees that
the do/while loop will execute exactly once (macro trick).
IF THIS MACRO IS MODIFIED, DO NOT USE pKey more than once as it could possibly increment index values
more than once if this macro is called like the example above - i would get incremented multiple times.
*/
#define acmeCacheInitKey(pKey, pCacheDS, DSNAME, MEMBERNAME, idType) \
do{ \
JDECMKEYSEGMENT *pCK = pKey; \
pCK->nOffset = offsetof(DSNAME, MEMBERNAME); \
pCK->idDataType = idType; \
pCK->nSize = acmeCacheKeySize((pCacheDS)-> ## MEMBERNAME, idType); \
}while( (pCacheDS) != (pCacheDS) )
</pre><hr />
 
You still have not update the structure to use the DIM macro for the size of the string.

Your Code:

Index1->CacheKey[1].nOffset = offsetof(DSUTILCACHE, MinorityCode);
Index1->CacheKey[1].nSize = sizeof(dsUtilCache.MinorityCode);
Index1->CacheKey[1].idDataType = EVDT_STRING;

Change it to:

Index1->CacheKey[1].nOffset = offsetof(DSUTILCACHE, MinorityCode);
Index1->CacheKey[1].nSize = DIM(dsUtilCache.MinorityCode);
Index1->CacheKey[1].idDataType = EVDT_STRING;
 
I have just tried the settings shown here and my results are always the same.

I can use any of the three segments and they work properly when only one segment is specified. If I specify multiples it just seems to ignore the additional segments.

/* Set up Index to Cache */
Index1->nNumSegments = 3;
Index1->CacheKey[0].nOffset = offsetof(DSUTILCACHE, PayPeriodEndDate);
Index1->CacheKey[0].nSize = sizeof(dsUtilCache.PayPeriodEndDate);
Index1->CacheKey[0].idDataType = EVDT_JDEDATE;
Index1->CacheKey[1].nOffset = offsetof(DSUTILCACHE, MinorityCode);
Index1->CacheKey[1].nSize = DIM(dsUtilCache.MinorityCode);
Index1->CacheKey[1].idDataType = EVDT_STRING;
Index1->CacheKey[2].nOffset = offsetof(DSUTILCACHE, Sex);
Index1->CacheKey[2].nSize = 1;
Index1->CacheKey[2].idDataType = EVDT_CHAR;

Or

///* Set up Index to Cache */
//Index1->nNumSegments = 2;
//Index1->CacheKey[0].nOffset = offsetof(DSUTILCACHE, PayPeriodEndDate);
//Index1->CacheKey[0].nSize = sizeof(dsUtilCache.PayPeriodEndDate);
//Index1->CacheKey[0].idDataType = EVDT_JDEDATE;
//Index1->CacheKey[1].nOffset = offsetof(DSUTILCACHE, MinorityCode);
//Index1->CacheKey[1].nSize = sizeof(dsUtilCache.MinorityCode);
//Index1->CacheKey[1].idDataType = EVDT_STRING;
 
Without knowing HOW you are calling these functions makes it really difficult to understand what you are trying to do. In other words, post the application code that calls these.

One thing ...

jdeCacheCode = jdeCacheFetchPosition(hUtilCache, hUtilCursor, (void *) &dsUtilCache, 1, (void *) &dsUtilCache, (long int) sizeof(dsUtilCache));

Parm 4 is how many keys to use. Setting it to 1 will only use PayPeriodEndDate.
 
That ambiguous little 1 in parameter 4 was the culprit, changed that and all works as it should. Thank you very much for your time.

Bill
 
What do you mean by "ignore" the additional segments. What are you trying to do? what do you expect to happen?
 
I am trying to accumulate totals to use later in a report. The problem was in the line

jdeCacheCode = jdeCacheFetchPosition(hUtilCache, hUtilCursor, (void *) &dsUtilCache,
3, (void *) &dsUtilCache, (long int) sizeof(dsUtilCache));

where I had a value of 1 for the number of segments.

Thanks for the reply.
 
Back
Top