ct_scroll_fetch

Description

Used for scrollable fetching after a supported scrollable cursor has been declared and successfully opened.

The capability properties of ct_scroll_fetch detect if the corresponding ASE server supports scrollable cursors. If scrollable cursors is not supported, a fatal error is generated and ct_scroll_fetch cannot be used. If this happens, use ct_fetch instead.

Syntax

CS_RETCODE ct_scroll_fetch(md, type, offset, option, rows_read)

CS_COMMAND  *cmd;
 CS_INT               type;
 CS_INT               offset;
 CS_INT               option;
 CS_INT               *rows_read;

Parameters

cmd

Command handle that holds the scrollable cursor definition.

type

Fetch orientation, with valid entries listed in Table 3-57:

Table 3-57: Values for ct_scroll_fetch type

Type

Offset Value

Meaning

CS_NEXT

Ignored

Returns CS_CURSOR_ROWS at each call.

If the number of rows in CS_CURSOR_ROWS is greater than the number of rows in the cursor result set, the array may have undefined values. This also happens if the last fetch produces fewer rows than CS_CURSOR_ROWS.

To find the number of rows returned, view rows_read. This allows you to validate the application array entries.A repeated sequence of CS_NEXT also causes the cursor to move beyond the last table row. If this occurs, zero rows is returned and ct_scroll_fetch returns CS_CURSOR_AFTER_LAST. A warning message is also generated, informing you that the scrollable cursor has moved beyond the resultset boundary. Please note that this is a warning, and does not indicate that an error has occurred.

Where the number of rows in CS_CURSOR_ROWS is greater than the number of rows in the cursor result set, a subsequent call to CS_NEXT positions the cursor beyond the last row.

CS_FIRST

Ignored

Setting CS_FIRST returns CS_CURSOR_ROWS, starting at the first row. If followed by CS_PREV, zero rows is returned and ct_scroll_fetch returns CS_CURSOR_BEFORE_FIRST.

CS_PREV

Ignored

CS_PREV positions the cursor to the row before the current position.

If repeated, CS_PREV calls bring the cursor back to the row 1, the next CS_PREV call will return 0 rows, and CS_CURSOR_BEFORE_FIRST is returned. A warning message is also generated, informing you that the scrollable cursor has moved beyond the resultset boundary. Please note that this is a warning, and does not indicate that an error has occurred.

CS_LAST

Ignored

Returns the last rows in CS_CURSOR_ROWS. If CS_LAST is followed by CS_NEXT, zero rows are returned and ct_scroll_fetch returns CS_CURSOR_AFTER_LAST.

CS_RELATIVE

Positive or negative (if zero, client generates a warning).

The offset value is treated as a signed integer (CS_INT), and is either positive or negative. This indicates a relative jump from the current cursor position.

CS_ABSOLUTE

Positive or negative (if zero, client generates a warning).

The absolute row number must be supplied. This is a signed integer (CS_INT).

offset

Passed as a signed integer, and valid only if type is CS_RELATIVE or CS_ABSOLUTE. In other cases, offset is CS_UNUSED.

option

Used to continue scrolling or to stop. If option is CS_TRUE, ct_scroll_fetch continues, based on (new) values given to type and offset. If option is CS_FALSE, the cursor stops scrolling and CS_SCROLL_CURSOR_ENDS is returned.

rows_read

Returns the number of rows per ct_scroll_fetch call.

Returns

ct_scroll_fetch returns the following values, in addition to those provided in “ct_fetch return values” (except CS_END_DATA):

Table 3-58: ct_scroll_fetch return values

Return value

Meaning

CS_SCROLL_ CURSOR_ENDS

CS_SCROLL_CURSOR_ENDS is returned when ct_scroll_fetch receives a CS_FALSE value.The return value may be used to signify that no more data is to be fetched from ASE.Typically, ct_scroll_fetch runs under the control of ct_results. Each call to ct_scroll_fetch returns the maximum number of rows indicated in CS_CURSOR_ROW. The application issues a new ct_scroll_fetch call or stops fetching.

If the application stops fetching, CS_SCROLL_CURSOR_ENDS is returned by ct_scroll_fetch, ct_results is processed and an internal cleanup is performed.

CS_CURSOR_ BEFORE_FIRST

CS_CURSOR_BEFORE_FIRST is returned when a call to ct_scroll_fetch causes the cursor to move before the first row in the ASE result-set. No rows are returned, and rows_read is zero. CS_CURSOR_BEFORE_FIRST may generate a warning if an error handler is installed.

CS_CURSOR_ AFTER_LAST

CS_CURSOR_AFTER_LAST is returned when a call to ct_scroll_fetch causes the cursor to move after the last row in ASE result set. No rows are returned, and rows_read is zero. CS_CURSOR_BEFORE_FIRST may generate a warning if an error handler is installed.

Examples

Example 1

The following fragment illustrates the call sequence to scroll fetch:

CS_RETCODE CS_PUBLIC
ex_scroll_fetch_1(CS_COMMAND *cmd)
{
    CS_RETCODE       retcode;
    CS_INT           num_cols;
    CS_INT           i;
    CS_INT           j;
    CS_INT           k;
    CS_INT           row_count = 0;
    CS_INT           rows_read;
    CS_INT           disp_len;
    CS_INT           sc_type;
    CS_INT           sc_offset;
    CS_INT           sc_option;
    CS_DATAFMT       *datafmt;
    EX_COLUMN_DATA   *coldata;

/*
** Find out how many columns there are in this result set.
*/
retcode = ct_res_info(cmd, CS_NUMDATA, &num_cols, CS_UNUSED, NULL);
if (retcode != CS_SUCCEED)
{
    ex_error("ex_scroll_fetch_data: ct_res_info() failed");
    return retcode;
}

/*
** Make sure we have at least one column
*/
if (num_cols <= 0)
{
    ex_error("ex_scroll_fetch_data: ct_res_info() returned zero columns");
    return CS_FAIL;
}

/*
** Our program variable, called 'coldata', is an array of 
** EX_COLUMN_DATA structures. Each array element represents
** one column.  Each array element will re-used for each row.
**
** First, allocate memory for the data element to process.
*/
coldata = (EX_COLUMN_DATA *)malloc(num_cols * sizeof (EX_COLUMN_DATA));
if (coldata == NULL)
{
    ex_error("ex_scroll_fetch_data: malloc() failed");
    return CS_MEM_ERROR;
}

datafmt = (CS_DATAFMT *)malloc(num_cols * sizeof (CS_DATAFMT));
if (datafmt == NULL)
{
    ex_error("ex_scroll_fetch_data: malloc() failed");
    free(coldata);
    return CS_MEM_ERROR;
}

/*
** Loop through the columns getting a description of each one
** and binding each one to a program variable.
**
** We're going to bind each column to a character string; 
** this will show how conversions from server native datatypes
** to strings can occur via bind.
**
** We're going to use the same datafmt structure for both the describe
** and the subsequent bind.
**
** If an error occurs within the for loop, a break is used to get out
** of the loop and the data that was allocated is free'd before
** returning.
*/
for (i = 0; i < num_cols; i++)
{
    /*
    ** Get the column description.  ct_describe() fills the
    ** datafmt parameter with a description of the column.
    */
    retcode = ct_describe(cmd, (i + 1), &datafmt[i]);    if (retcode != CS_SUCCEED)
    {
        ex_error("ex_scroll_fetch_data: ct_describe() failed");
        break;
    }

    /*
    ** update the datafmt structure to indicate that we want the
    ** results in a null terminated character string.
    **
    ** First, update datafmt.maxlength to contain the maximum
    ** possible length of the column. To do this, call
    ** ex_display_len() to determine the number of bytes needed
    ** for the character string representation, given the
    ** datatype described above.  Add one for the null
    ** termination character.
    */
    datafmt[i].maxlength = ex_display_dlen(&datafmt[i]) + 1;

    /*
    ** Set datatype and format to tell bind we want things
    ** converted to null terminated strings
    */
    datafmt[i].datatype = CS_CHAR_TYPE;
    datafmt[i].format   = CS_FMT_NULLTERM;

    /*
    ** Allocate memory for the column string
    */
    coldata[i].value = (CS_CHAR *)malloc(datafmt[i].maxlength);
    if (coldata[i].value == NULL)
    {
        ex_error("ex_scroll_fetch_data: malloc() failed");
        retcode = CS_MEM_ERROR;
        break;
    }
    
    /*
    ** Now bind.
    */
    retcode = ct_bind(cmd, (i + 1), &datafmt[i],
                coldata[i].value, &coldata[i].valuelen,
               (CS_SMALLINT *)&coldata[i].indicator);
    if (retcode != CS_SUCCEED)
    {
        ex_error("ex_scroll_fetch_data: ct_bind() failed");
        break;
    }
}
if (retcode != CS_SUCCEED)
{
        for (j = 0; j < i; j++)
        {
            free(coldata[j].value);
        }
    free(coldata);
    free(datafmt);
    return retcode;
}

/*
** Display column header
*/
ex_display_header(num_cols, datafmt);

/*
** Fetch the rows.  Call ct_scroll_fetch() as long as it returns 
** CS_SUCCEED, CS_ROW_FAIL, CS_CURSOR_BEFORE_FIRST or 
** CS_CURSOR_AFTER_LAST.
** These are recoverable or "row" producing conditions, e.g. non-fatal.
** All other terminate the loop, either by error or choice. 
*/
for (i = 0; i < EX_MAX_ARR; i++)
{
    sc_type = scroll_index(type_list0[i], scroll_arrmap);
    sc_offset = offset_list0[i];

    if (type_list0[i] != EX_BADVAL)
    {
        sc_option = CS_TRUE;
    }
    else
    {
        /*
        ** Since EX_BADVAL is not valid to pass into
        ** either sc_type or sc_offset we set these
        ** to CS_UNUSED respectively.
        */
        sc_type = CS_UNUSED;
        sc_offset = CS_UNUSED;
        sc_option = CS_FALSE;
    }

    retcode = ct_scroll_fetch(cmd, sc_type, sc_offset, sc_option,
                &rows_read);
    switch ((int)retcode)
    {
        case CS_ROW_FAIL:

            fprintf(stdout, "Error on row %d.\n", row_count);
            fflush(stdout);
            break;

        case CS_CURSOR_BEFORE_FIRST:

            fprintf(stdout, " Cursor before first row\n");
            fflush(stdout);
            break;

        case CS_CURSOR_AFTER_LAST:

            fprintf(stdout, " Cursor after last row\n");
            fflush(stdout);
            break;

        case CS_SUCCEED:

            /*
            ** Increment our row count by the number of 
            ** rows just fetched.
            */
            row_count = row_count + rows_read;

            /*
            ** Assume we have a row. Loop through the 
            ** columns displaying the column values.
            */
            for (k = 0; k < num_cols; k++)
            {
                /*
                ** Display the column value
                */
                fprintf(stdout, "%s", coldata[k].value);
                fflush(stdout);

                /*
                ** If not last column, Print out spaces between
                ** this column and next one. 
                */
                if (k != num_cols - 1)
                {
                    disp_len = ex_display_dlen(&datafmt[k]);
                    disp_len -= coldata[k].valuelen - 1;
                    for (j = 0; j < disp_len; j++)
                    {
                        fputc(' ', stdout);
                    }
                }
            } 
            fprintf(stdout, "\n");
            fflush(stdout);
            break;

        case CS_FAIL:

            /*
            ** Free allocated space.
            */
            for (k = 0; k < num_cols; k++)
            {
                free(coldata[k].value);
            }
            free(coldata);
            free(datafmt);
            return retcode;

        case CS_SCROLL_CURSOR_ENDS:

            /*
            ** User signalled ct_scroll_fetch() to stop
            ** scrolling, we are done with this result set.
            ** Free allocated space.
            */
            for (k = 0; k < num_cols; k++)
            {
                free(coldata[k].value);
            }
            free(coldata);
            free(datafmt);
            return retcode;

        default:

            fprintf(stdout, "Hit default, this should not happen. 
                    Exiting program.\n");
            fflush(stdout);
            exit(0);
    } /* end switch */
  } /* end for */
  return CS_SUCCEED;
}

Usage

See also

ct_fetch.