#define SQL_TEXT Latin_Text
#include <sqltypes_td.h>
#include <string.h>
#define BUFFER_SIZE 100000
#define PREFIX_SIZE 500
/* Aggregate intermediate record */
typedef struct
{
FNC_LobLength_t length;
int initflag;
LOB_REF ref;
BYTE prefix[PREFIX_SIZE];
} intrec_t;
/***************************************************************
load_rec() Initialize the intrec structure for a specified
object;
***************************************************************/
static void
load_rec (intrec_t* rec, LOB_LOCATOR loc)
{
LOB_CONTEXT_ID id;
FNC_LobLength_t actsize;
rec->length = FNC_GetLobLength(loc);
rec->initflag = 1;
FNC_LobOpen(loc, &id, 0, PREFIX_SIZE);
FNC_LobRead(id, rec->prefix, PREFIX_SIZE, &actsize);
FNC_LobClose(id);
/* if the object length is less than the size allotted for the
prefix, pad the remaining bytes with zeros. */
while (actsize < PREFIX_SIZE)
rec->prefix[actsize++] = 0;
FNC_LobLoc2Ref(loc, &rec->ref);
}
/***************************************************************
findnz() Scan the open stream and return 1 if a nonzero
byte is found, else return 0.
***************************************************************/
static int
findnz(LOB_CONTEXT_ID id)
{
FNC_LobLength_t i, actsize;
BYTE buffer[BUFFER_SIZE];
while (FNC_LobRead(id, buffer, BUFFER_SIZE, &actsize) == 0)
{
for (i = 0; i < actsize; ++i)
if (buffer[i] != 0)
return 1;
}
return 0;
}
/***************************************************************
greater() Compare 2 byte strings and return 1 if a > b,
-1 if a < b, else return 0.
***************************************************************/
static int
greater(BYTE* a, BYTE* b, int length)
{
for (; length > 0; ++a, ++b, --length)
{
if ( *a > *b)
return 1;
else if (*a < *b)
return -1;
}
return 0;
}
/***************************************************************
rest_greater() Compare the rest of 2 open streams (a, b) and
return 1 if a > b, else return 0.
***************************************************************/
static int
rest_greater(LOB_CONTEXT_ID a, LOB_CONTEXT_ID b)
{
FNC_LobLength_t i, actsizea, actsizeb;
BYTE bufa[BUFFER_SIZE/2], bufb[BUFFER_SIZE/2];
while (FNC_LobRead(a, bufa, sizeof(bufa), &actsizea) == 0)
{
if (FNC_LobRead(b, bufb, actsizea, &actsizeb) == 0)
{
int cmpres = greater(bufa, bufb, actsizeb);
if (cmpres != 0)
return cmpres;
if (actsizea > actsizeb)
{
/* exhausted b before a */
for (i = actsizeb; i < actsizea; ++i)
if (bufa[i] != 0)
return 1;
return findnz(a);
}
}
else
{
/* b exhausted; then a > b iff the rest of a contains
any nonzero byte */
return findnz(a);
}
}
/* Reached the end of a without exhausting b; ergo a >= b */
return 0;
}
/**********************************************************************
greater_obj() Compare 2 objects (a, b) using cached prefix
strings when possible, or by reading whole objects
when necessary. Return 1 if a > b, else return 0.
**********************************************************************/
static int
greater_obj(intrec_t* a, intrec_t* b)
{
LOB_CONTEXT_ID streama, streamb;
int cmpres = greater(a->prefix, b->prefix, PREFIX_SIZE);
/* If the comparison was resolved in the prefix, then we
are done */
if (cmpres != 0)
return cmpres;
if (a->length < PREFIX_SIZE)
return 0; /* must be a <= b */
/* We will have to read the rest of object a */
FNC_LobOpen(FNC_LobRef2Loc(&a->ref), &streama,
PREFIX_SIZE, 0);
if (b->length < PREFIX_SIZE)
{
/* a > b iff substr(a, PREFIX_SIZE+1) has any
nonzero byte */
cmpres = findnz(streama);
}
else
{
/* We will have to read the rest of object b also */
FNC_LobOpen(FNC_LobRef2Loc(&b->ref), &streamb,
PREFIX_SIZE, 0);
/* Compare the rest of the 2 objects */
cmpres = rest_greater(streama, streamb);
FNC_LobClose(streamb);
}
FNC_LobClose(streama);
return cmpres;
}
/***************************************************************
append_lob() Copy source to dest
***************************************************************/
static void
append_lob(LOB_RESULT_LOCATOR dest, LOB_LOCATOR source )
{
BYTE buffer[BUFFER_SIZE];
LOB_CONTEXT_ID id;
FNC_LobLength_t actlen;
int trunc_err = 0;
FNC_LobOpen(source, &id, 0, 0);
while( FNC_LobRead(id, buffer, BUFFER_SIZE, &actlen) == 0 &&
!trunc_err)
trunc_err = FNC_LobAppend(dest, buffer, actlen, &actlen);
FNC_LobClose(id);
}
/***************************************************************
Max_Blob() Main function
***************************************************************/
void Max_Blob(FNC_Phase phase,
FNC_Context_t* fctx,
LOB_LOCATOR* x,
LOB_RESULT_LOCATOR* result,
int* x_i,
int* result_i,
char sqlstate[6])
{
intrec_t detail_rec;
intrec_t* s1 = (intrec_t*) fctx->interim1;
intrec_t* s2 = (intrec_t*) fctx->interim2;
switch (phase)
{
case AGR_INIT:
if ((s1 = (intrec_t*)FNC_DefMem(sizeof(intrec_t))) == NULL)
{
/* could not get storage */
strcpy(sqlstate,"U0001");
return;
}
s1->initflag = 0;
/* Now fall through to detail phase logic */
case AGR_DETAIL:
if (*x_i == -1)
return;
if (s1->initflag)
{
load_rec(&detail_rec, *x);
if (greater_obj(&detail_rec, s1) == 1)
*s1 = detail_rec;
}
else
{
s1->initflag = 1;
load_rec(s1, *x);
}
break;
case AGR_COMBINE:
if (greater_obj(s2, s1) == 1)
*s1 = *s2;
break;
case AGR_FINAL:
if (s1->initflag)
append_lob(*result, FNC_LobRef2Loc(&s1->ref));
else
*result_i = -1; /* set result to null */
break;
case AGR_NODATA:
*result_i = -1; /* set result to null */
break;
default:
strcpy(sqlstate, "U0005");
}
}