mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-25 01:23:03 +01:00
658 lines
16 KiB
Plaintext
658 lines
16 KiB
Plaintext
/*
|
|
* PROGRAM: Interbase layered support library
|
|
* MODULE: array.epp
|
|
* DESCRIPTION: Dynamic array support
|
|
*
|
|
* The contents of this file are subject to the Interbase Public
|
|
* License Version 1.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy
|
|
* of the License at http://www.Inprise.com/IPL.html
|
|
*
|
|
* Software distributed under the License is distributed on an
|
|
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
|
|
* or implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code was created by Inprise Corporation
|
|
* and its predecessors. Portions created by Inprise Corporation are
|
|
* Copyright (C) Inprise Corporation.
|
|
*
|
|
* All Rights Reserved.
|
|
* Contributor(s): ______________________________________.
|
|
*
|
|
* 2001.09.18 Claudio Valderrama: get_name() was preventing the API calls
|
|
* isc_array_lookup_bounds, isc_lookup_desc & isc_array_set_desc
|
|
* from working properly with dialect 3 names. Therefore, incorrect names
|
|
* could be returned or a lookup for a blob field could fail. In addition,
|
|
* a possible buffer overrun due to unchecked bounds was closed. The fc
|
|
* get_name() as been renamed copy_exact_name().
|
|
*
|
|
* 2002-02-24 Sean Leyne - Code Cleanup of old Win 3.1 port (WINDOWS_ONLY)
|
|
*
|
|
*
|
|
*/
|
|
|
|
#include "firebird.h"
|
|
#include <string.h>
|
|
#include "../jrd/common.h"
|
|
#include <stdarg.h>
|
|
#include "../jrd/gds.h"
|
|
#include "../dsql/array_proto.h"
|
|
#include "../jrd/gds_proto.h"
|
|
|
|
DATABASE DB = STATIC FILENAME "yachts.lnk";
|
|
|
|
#define ARRAY_DESC_COLUMN_MAJOR 1 /* Set for FORTRAN */
|
|
|
|
typedef struct gen {
|
|
SCHAR *gen_sdl;
|
|
SCHAR **gen_sdl_ptr;
|
|
SCHAR *gen_end;
|
|
STATUS *gen_status;
|
|
SSHORT gen_internal;
|
|
} *GEN;
|
|
|
|
static void adjust_length(ISC_ARRAY_DESC *);
|
|
static void copy_exact_name (char *, char*, SSHORT);
|
|
static STATUS copy_status(STATUS *, STATUS *);
|
|
static STATUS error(STATUS *, SSHORT, ...);
|
|
static STATUS gen_sdl(STATUS *, ISC_ARRAY_DESC *, SSHORT *, SCHAR **,
|
|
SSHORT *, BOOLEAN);
|
|
static STATUS lookup_desc(STATUS *, FRBRD **, FRBRD **, SCHAR *, SCHAR *,
|
|
ISC_ARRAY_DESC *, SCHAR *);
|
|
static STATUS stuff(GEN, SSHORT, ...);
|
|
static STATUS stuff_literal(GEN, SLONG);
|
|
static STATUS stuff_string(GEN, SCHAR, SCHAR *);
|
|
|
|
/* STUFF_SDL used in place of STUFF to avoid confusion with BLR STUFF
|
|
macro defined in dsql.h */
|
|
|
|
#define STUFF_SDL(byte) if (stuff (gen, 1, byte)) return status [1]
|
|
#define STUFF_SDL_WORD(word) if (stuff (gen, 2, word, word >> 8)) return status [1]
|
|
#define STUFF_SDL_LONG(word) if (stuff (gen, 4, word, word >> 8, word >> 16, word >> 24)) return status [1]
|
|
|
|
|
|
STATUS API_ROUTINE isc_array_gen_sdl(STATUS * status,
|
|
ISC_ARRAY_DESC * desc,
|
|
SSHORT * sdl_buffer_length,
|
|
SCHAR * sdl_buffer, SSHORT * sdl_length)
|
|
{
|
|
/**************************************
|
|
*
|
|
* i s c _ a r r a y _ g e n _ s d l
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
*
|
|
**************************************/
|
|
|
|
return gen_sdl(status, desc, sdl_buffer_length, &sdl_buffer, sdl_length,
|
|
FALSE);
|
|
}
|
|
|
|
|
|
STATUS API_ROUTINE isc_array_get_slice(STATUS * status,
|
|
FRBRD **db_handle,
|
|
FRBRD **trans_handle,
|
|
GDS_QUAD * array_id,
|
|
ISC_ARRAY_DESC * desc,
|
|
void *array, SLONG * slice_length)
|
|
{
|
|
/**************************************
|
|
*
|
|
* i s c _ a r r a y _ g e t _ s l i c e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
*
|
|
**************************************/
|
|
SCHAR *sdl, sdl_buffer[512];
|
|
SSHORT sdl_length;
|
|
|
|
sdl_length = sizeof(sdl_buffer);
|
|
sdl = sdl_buffer;
|
|
|
|
if (gen_sdl(status, desc, &sdl_length, &sdl, &sdl_length, TRUE))
|
|
return status[1];
|
|
|
|
isc_get_slice(status, db_handle, trans_handle, array_id,
|
|
sdl_length, sdl, 0, (ISC_LONG*) NULL_PTR,
|
|
*slice_length, array, slice_length);
|
|
|
|
if (sdl != sdl_buffer)
|
|
gds__free((SLONG *) sdl);
|
|
|
|
return status[1];
|
|
}
|
|
|
|
|
|
STATUS API_ROUTINE isc_array_lookup_bounds(STATUS * status,
|
|
FRBRD **db_handle,
|
|
FRBRD **trans_handle,
|
|
SCHAR * relation_name,
|
|
SCHAR * field_name,
|
|
ISC_ARRAY_DESC * desc)
|
|
{
|
|
/**************************************
|
|
*
|
|
* i s c _ a r r a y _ l o o k u p _ b o u n d s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
*
|
|
**************************************/
|
|
ISC_ARRAY_BOUND *tail;
|
|
SCHAR global[32];
|
|
|
|
if (lookup_desc(status, db_handle, trans_handle,
|
|
field_name, relation_name, desc, global))
|
|
return status[1];
|
|
|
|
tail = desc->array_desc_bounds;
|
|
|
|
FOR X IN RDB$FIELD_DIMENSIONS WITH X.RDB$FIELD_NAME EQ global SORTED BY X.RDB$DIMENSION
|
|
tail->array_bound_lower = (SSHORT)X.RDB$LOWER_BOUND;
|
|
tail->array_bound_upper = (SSHORT)X.RDB$UPPER_BOUND;
|
|
++tail;
|
|
END_FOR
|
|
ON_ERROR
|
|
return copy_status(gds_status, status);
|
|
END_ERROR;
|
|
|
|
return status[1];
|
|
}
|
|
|
|
|
|
STATUS API_ROUTINE isc_array_lookup_desc(STATUS * status,
|
|
FRBRD **db_handle,
|
|
FRBRD **trans_handle,
|
|
SCHAR * relation_name,
|
|
SCHAR * field_name,
|
|
ISC_ARRAY_DESC * desc)
|
|
{
|
|
/**************************************
|
|
*
|
|
* i s c _ a r r a y _ l o o k u p _ d e s c
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
*
|
|
**************************************/
|
|
|
|
return lookup_desc(status, db_handle, trans_handle,
|
|
field_name, relation_name, desc, (SCHAR*) NULL_PTR);
|
|
}
|
|
|
|
|
|
STATUS API_ROUTINE isc_array_put_slice(STATUS * status,
|
|
FRBRD **db_handle,
|
|
FRBRD **trans_handle,
|
|
GDS_QUAD * array_id,
|
|
ISC_ARRAY_DESC * desc,
|
|
void *array, SLONG * slice_length)
|
|
{
|
|
/**************************************
|
|
*
|
|
* i s c _ a r r a y _ p u t _ s l i c e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
*
|
|
**************************************/
|
|
SCHAR *sdl, sdl_buffer[512];
|
|
SSHORT sdl_length;
|
|
|
|
sdl_length = sizeof(sdl_buffer);
|
|
sdl = sdl_buffer;
|
|
|
|
if (gen_sdl(status, desc, &sdl_length, &sdl, &sdl_length, TRUE))
|
|
return status[1];
|
|
|
|
isc_put_slice(status, db_handle, trans_handle, array_id,
|
|
sdl_length, sdl, 0, (ISC_LONG*) NULL_PTR, *slice_length, array);
|
|
|
|
if (sdl != sdl_buffer)
|
|
gds__free((SLONG *) sdl);
|
|
|
|
return status[1];
|
|
}
|
|
|
|
|
|
STATUS API_ROUTINE isc_array_set_desc(STATUS * status,
|
|
SCHAR * relation_name,
|
|
SCHAR * field_name,
|
|
SSHORT * sql_dtype,
|
|
SSHORT * sql_length,
|
|
SSHORT * dimensions,
|
|
ISC_ARRAY_DESC * desc)
|
|
{
|
|
/**************************************
|
|
*
|
|
* i s c _ a r r a y _ s e t _ d e s c
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
*
|
|
**************************************/
|
|
SSHORT dtype;
|
|
|
|
copy_exact_name (field_name, desc->array_desc_field_name,
|
|
sizeof(desc->array_desc_field_name));
|
|
copy_exact_name (relation_name, desc->array_desc_relation_name,
|
|
sizeof(desc->array_desc_relation_name));
|
|
|
|
|
|
desc->array_desc_flags = 0;
|
|
desc->array_desc_dimensions = *dimensions;
|
|
desc->array_desc_length = *sql_length;
|
|
desc->array_desc_scale = 0;
|
|
|
|
dtype = *sql_dtype & ~1;
|
|
|
|
if (dtype == SQL_VARYING)
|
|
desc->array_desc_dtype = blr_varying;
|
|
else if (dtype == SQL_TEXT)
|
|
desc->array_desc_dtype = blr_text;
|
|
else if (dtype == SQL_DOUBLE)
|
|
desc->array_desc_dtype = blr_double;
|
|
else if (dtype == SQL_FLOAT)
|
|
desc->array_desc_dtype = blr_float;
|
|
else if (dtype == SQL_D_FLOAT)
|
|
desc->array_desc_dtype = blr_d_float;
|
|
else if (dtype == SQL_TIMESTAMP)
|
|
desc->array_desc_dtype = blr_timestamp;
|
|
else if (dtype == SQL_TYPE_DATE)
|
|
desc->array_desc_dtype = blr_sql_date;
|
|
else if (dtype == SQL_TYPE_TIME)
|
|
desc->array_desc_dtype = blr_sql_time;
|
|
else if (dtype == SQL_LONG)
|
|
desc->array_desc_dtype = blr_long;
|
|
else if (dtype == SQL_SHORT)
|
|
desc->array_desc_dtype = blr_short;
|
|
else if (dtype == SQL_INT64)
|
|
desc->array_desc_dtype = blr_int64;
|
|
else if (dtype == SQL_QUAD)
|
|
desc->array_desc_dtype = blr_quad;
|
|
else
|
|
return error(status, 7, (STATUS) gds_sqlerr,
|
|
(STATUS) gds_arg_number, (STATUS) - 804,
|
|
(STATUS) gds_arg_gds, (STATUS) gds_random,
|
|
(STATUS) gds_arg_string,
|
|
(STATUS) "data type not understood");
|
|
|
|
return error(status, 1, (STATUS) FB_SUCCESS);
|
|
}
|
|
|
|
|
|
static void adjust_length( ISC_ARRAY_DESC * desc)
|
|
{
|
|
/**************************************
|
|
*
|
|
* a d j u s t _ l e n g t h
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Make architectural adjustment to fixed datatypes.
|
|
*
|
|
**************************************/
|
|
}
|
|
|
|
static void copy_exact_name (
|
|
char *from,
|
|
char *to,
|
|
SSHORT bsize)
|
|
{
|
|
/**************************************
|
|
*
|
|
* c o p y _ e x a c t _ n a m e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Copy null terminated name ot stops at bsize - 1.
|
|
* CVC: This is just another fc like DYN_terminate.
|
|
*
|
|
**************************************/
|
|
|
|
char *from_end = from + bsize - 1, *to2 = to - 1;
|
|
while (*from && from < from_end) {
|
|
if (*from != ' ') {
|
|
to2 = to;
|
|
}
|
|
*to++ = *from++;
|
|
}
|
|
*++to2 = 0;
|
|
}
|
|
|
|
|
|
|
|
static STATUS copy_status( STATUS * from, STATUS * to)
|
|
{
|
|
/**************************************
|
|
*
|
|
* c o p y _ s t a t u s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Copy a status vector.
|
|
*
|
|
**************************************/
|
|
STATUS status, *end;
|
|
|
|
status = from[1];
|
|
|
|
for (end = from + ISC_STATUS_LENGTH; from < end;)
|
|
*to++ = *from++;
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
static STATUS error( STATUS * status, SSHORT count, ...)
|
|
{
|
|
/**************************************
|
|
*
|
|
* e r r o r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Stuff a status vector.
|
|
*
|
|
**************************************/
|
|
STATUS *stat;
|
|
va_list ptr;
|
|
|
|
VA_START(ptr, count);
|
|
stat = status;
|
|
*stat++ = gds_arg_gds;
|
|
|
|
for (; count; --count)
|
|
*stat++ = (STATUS) va_arg(ptr, STATUS);
|
|
|
|
*stat = gds_arg_end;
|
|
|
|
return status[1];
|
|
}
|
|
|
|
|
|
static STATUS gen_sdl(STATUS * status,
|
|
ISC_ARRAY_DESC * desc,
|
|
SSHORT * sdl_buffer_length,
|
|
SCHAR ** sdl_buffer,
|
|
SSHORT * sdl_length, BOOLEAN internal_flag)
|
|
{
|
|
/**************************************
|
|
*
|
|
* g e n _ s d l
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
*
|
|
**************************************/
|
|
SSHORT n, from, to, increment, dimensions;
|
|
ISC_ARRAY_BOUND *tail;
|
|
struct gen *gen, gen_block;
|
|
|
|
|
|
dimensions = desc->array_desc_dimensions;
|
|
|
|
if (dimensions > 16)
|
|
return error(status, 5, (STATUS) gds_invalid_dimension,
|
|
(STATUS) gds_arg_number, (STATUS) dimensions,
|
|
(STATUS) gds_arg_number, (STATUS) 16);
|
|
|
|
gen = &gen_block;
|
|
gen->gen_sdl = *sdl_buffer;
|
|
gen->gen_sdl_ptr = sdl_buffer;
|
|
gen->gen_end = *sdl_buffer + *sdl_buffer_length;
|
|
gen->gen_status = status;
|
|
gen->gen_internal = internal_flag ? 0 : -1;
|
|
|
|
if (stuff
|
|
(gen, 4, gds_sdl_version1, gds_sdl_struct, 1,
|
|
desc->array_desc_dtype)) return status[1];
|
|
|
|
switch (desc->array_desc_dtype) {
|
|
case blr_short:
|
|
case blr_long:
|
|
case blr_int64:
|
|
case blr_quad:
|
|
STUFF_SDL(desc->array_desc_scale);
|
|
break;
|
|
|
|
case blr_text:
|
|
case blr_cstring:
|
|
case blr_varying:
|
|
STUFF_SDL_WORD(desc->array_desc_length);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (stuff_string(gen, gds_sdl_relation, desc->array_desc_relation_name))
|
|
return status[1];
|
|
|
|
if (stuff_string(gen, gds_sdl_field, desc->array_desc_field_name))
|
|
return status[1];
|
|
|
|
if (desc->array_desc_flags & ARRAY_DESC_COLUMN_MAJOR) {
|
|
from = dimensions - 1;
|
|
to = -1;
|
|
increment = -1;
|
|
}
|
|
else {
|
|
from = 0;
|
|
to = dimensions;
|
|
increment = 1;
|
|
}
|
|
|
|
for (n = from; n != to; n += increment) {
|
|
tail = desc->array_desc_bounds + n;
|
|
if (tail->array_bound_lower == 1) {
|
|
if (stuff(gen, 2, gds_sdl_do1, n))
|
|
return status[1];
|
|
}
|
|
else {
|
|
if (stuff(gen, 2, gds_sdl_do2, n))
|
|
return status[1];
|
|
if (stuff_literal(gen, (SLONG) tail->array_bound_lower))
|
|
return status[1];
|
|
}
|
|
if (stuff_literal(gen, (SLONG) tail->array_bound_upper))
|
|
return status[1];
|
|
}
|
|
|
|
if (stuff(gen, 5, gds_sdl_element, 1, gds_sdl_scalar, 0, dimensions))
|
|
return status[1];
|
|
|
|
for (n = 0; n < dimensions; n++)
|
|
if (stuff(gen, 2, gds_sdl_variable, n))
|
|
return status[1];
|
|
|
|
STUFF_SDL(gds_sdl_eoc);
|
|
*sdl_length = gen->gen_sdl - *gen->gen_sdl_ptr;
|
|
|
|
return error(status, 1, (STATUS) FB_SUCCESS);
|
|
}
|
|
|
|
|
|
|
|
static STATUS lookup_desc(STATUS * status,
|
|
FRBRD **db_handle,
|
|
FRBRD **trans_handle,
|
|
SCHAR * field_name,
|
|
SCHAR * relation_name, ISC_ARRAY_DESC * desc, SCHAR * global)
|
|
{
|
|
/**************************************
|
|
*
|
|
* l o o k u p _ d e s c
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
*
|
|
**************************************/
|
|
SSHORT flag;
|
|
|
|
if (DB && DB != *db_handle)
|
|
RELEASE_REQUESTS;
|
|
|
|
DB = *db_handle;
|
|
gds_trans = *trans_handle;
|
|
|
|
copy_exact_name (field_name, desc->array_desc_field_name,
|
|
sizeof(desc->array_desc_field_name));
|
|
copy_exact_name (relation_name, desc->array_desc_relation_name,
|
|
sizeof(desc->array_desc_relation_name));
|
|
|
|
|
|
desc->array_desc_flags = 0;
|
|
|
|
flag = FALSE;
|
|
|
|
FOR X IN RDB$RELATION_FIELDS CROSS Y IN RDB$FIELDS
|
|
WITH X.RDB$FIELD_SOURCE EQ Y.RDB$FIELD_NAME AND
|
|
X.RDB$RELATION_NAME EQ desc->array_desc_relation_name AND
|
|
X.RDB$FIELD_NAME EQ desc->array_desc_field_name
|
|
flag = TRUE;
|
|
desc->array_desc_dtype = (UCHAR)Y.RDB$FIELD_TYPE;
|
|
desc->array_desc_scale = (SCHAR)Y.RDB$FIELD_SCALE;
|
|
desc->array_desc_length = Y.RDB$FIELD_LENGTH;
|
|
adjust_length(desc);
|
|
desc->array_desc_dimensions = Y.RDB$DIMENSIONS;
|
|
if (global) {
|
|
copy_exact_name (Y.RDB$FIELD_NAME, global, sizeof(Y.RDB$FIELD_NAME));
|
|
}
|
|
|
|
END_FOR
|
|
ON_ERROR
|
|
return copy_status(gds_status, status);
|
|
END_ERROR;
|
|
|
|
if (!flag)
|
|
return error(status, 5, (STATUS) gds_fldnotdef,
|
|
(STATUS) gds_arg_string,
|
|
(STATUS) desc->array_desc_field_name,
|
|
(STATUS) gds_arg_string,
|
|
(STATUS) desc->array_desc_relation_name);
|
|
|
|
return error(status, 1, (STATUS) FB_SUCCESS);
|
|
}
|
|
|
|
|
|
static STATUS stuff( GEN gen, SSHORT count, ...)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s t u f f
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Stuff a SDL byte.
|
|
*
|
|
**************************************/
|
|
SCHAR c, *new_sdl;
|
|
va_list ptr;
|
|
SSHORT new_len, current_len;
|
|
|
|
if (gen->gen_sdl + count >= gen->gen_end) {
|
|
if (gen->gen_internal < 0)
|
|
return error(gen->gen_status, 3, (STATUS) gds_misc_interpreted,
|
|
(STATUS) gds_arg_string,
|
|
(STATUS) "SDL buffer overflow");
|
|
|
|
/* The sdl buffer is too small. Allocate a larger one. */
|
|
|
|
new_len = gen->gen_end - *gen->gen_sdl_ptr + 512 + count;
|
|
new_sdl = (SCHAR *) gds__alloc((SLONG) new_len);
|
|
if (!new_sdl)
|
|
return error(gen->gen_status, 5, (STATUS) gds_misc_interpreted,
|
|
(STATUS) gds_arg_string,
|
|
(STATUS) "SDL buffer overflow", (STATUS) gds_arg_gds,
|
|
(STATUS) gds_virmemexh);
|
|
|
|
current_len = gen->gen_sdl - *gen->gen_sdl_ptr;
|
|
memcpy(new_sdl, *gen->gen_sdl_ptr, current_len);
|
|
if (gen->gen_internal++)
|
|
gds__free(*gen->gen_sdl_ptr);
|
|
gen->gen_sdl = new_sdl + current_len;
|
|
*gen->gen_sdl_ptr = new_sdl;
|
|
gen->gen_end = new_sdl + new_len;
|
|
}
|
|
|
|
VA_START(ptr, count);
|
|
|
|
for (; count; --count) {
|
|
c = va_arg(ptr, int);
|
|
*(gen->gen_sdl)++ = c;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static STATUS stuff_literal( GEN gen, SLONG literal)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s t u f f _ l i t e r a l
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Stuff an SDL literal.
|
|
*
|
|
**************************************/
|
|
STATUS *status;
|
|
|
|
status = gen->gen_status;
|
|
|
|
if (literal >= -128 && literal <= 127)
|
|
return stuff(gen, 2, gds_sdl_tiny_integer, literal);
|
|
|
|
if (literal >= -32768 && literal <= 32767)
|
|
return stuff(gen, 3, gds_sdl_short_integer, literal, literal >> 8);
|
|
|
|
STUFF_SDL(gds_sdl_long_integer);
|
|
STUFF_SDL_LONG(literal);
|
|
|
|
return FB_SUCCESS;
|
|
}
|
|
|
|
|
|
static STATUS stuff_string( GEN gen, SCHAR sdl, SCHAR * string)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s t u f f _ s t r i n g
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Stuff a "thing" then a counted string.
|
|
*
|
|
**************************************/
|
|
STATUS *status;
|
|
|
|
status = gen->gen_status;
|
|
|
|
STUFF_SDL(sdl);
|
|
STUFF_SDL(strlen(string));
|
|
|
|
while (*string)
|
|
STUFF_SDL(*string++);
|
|
|
|
return FB_SUCCESS;
|
|
}
|