/* * 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 #include "../jrd/common.h" #include #include "../jrd/gds.h" #include "../dsql/array_proto.h" #include "../jrd/gds_proto.h" DATABASE DB = STATIC FILENAME "yachts.lnk"; const int array_desc_column_major = 1; // Set for FORTRAN typedef struct gen_t { UCHAR *gen_sdl; UCHAR **gen_sdl_ptr; UCHAR *gen_end; ISC_STATUS *gen_status; USHORT gen_internal; } *GEN; static void adjust_length(ISC_ARRAY_DESC *); static void copy_exact_name (char *, char*, SSHORT); static ISC_STATUS copy_status(ISC_STATUS *, ISC_STATUS *); static ISC_STATUS error(ISC_STATUS *, SSHORT, ...); static ISC_STATUS gen_sdl(ISC_STATUS *, ISC_ARRAY_DESC *, SSHORT *, UCHAR **, SSHORT *); static ISC_STATUS lookup_desc(ISC_STATUS *, FRBRD **, FRBRD **, SCHAR *, SCHAR *, ISC_ARRAY_DESC *, SCHAR *); static ISC_STATUS stuff(GEN, SSHORT, ...); static ISC_STATUS stuff_literal(GEN, SLONG); static ISC_STATUS stuff_string(GEN, UCHAR, 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] ISC_STATUS API_ROUTINE isc_array_get_slice(ISC_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 * **************************************/ UCHAR *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)) return status[1]; isc_get_slice(status, db_handle, trans_handle, array_id, // SD: I do not complain but in ibase.h sdl is a plain char in functions' // declaration while it is UCHAR in the functions' implementations. Damned legacy! sdl_length, reinterpret_cast(sdl), 0, NULL, *slice_length, array, slice_length); if (sdl != sdl_buffer) gds__free((SLONG *) sdl); return status[1]; } ISC_STATUS API_ROUTINE isc_array_lookup_bounds(ISC_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]; } ISC_STATUS API_ROUTINE isc_array_lookup_desc(ISC_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, NULL); } ISC_STATUS API_ROUTINE isc_array_put_slice(ISC_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 * **************************************/ UCHAR *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)) return status[1]; isc_put_slice(status, db_handle, trans_handle, array_id, sdl_length, reinterpret_cast(sdl), 0, NULL, *slice_length, array); if (sdl != sdl_buffer) gds__free((SLONG *) sdl); return status[1]; } ISC_STATUS API_ROUTINE isc_array_set_desc(ISC_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, (ISC_STATUS) gds_sqlerr, (ISC_STATUS) gds_arg_number, (ISC_STATUS) - 804, (ISC_STATUS) gds_arg_gds, (ISC_STATUS) gds_random, (ISC_STATUS) gds_arg_string, (ISC_STATUS) "data type not understood"); return error(status, 1, (ISC_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 ISC_STATUS copy_status( ISC_STATUS * from, ISC_STATUS * to) { /************************************** * * c o p y _ s t a t u s * ************************************** * * Functional description * Copy a status vector. * **************************************/ ISC_STATUS status, *end; status = from[1]; for (end = from + ISC_STATUS_LENGTH; from < end;) *to++ = *from++; return status; } static ISC_STATUS error( ISC_STATUS * status, SSHORT count, ...) { /************************************** * * e r r o r * ************************************** * * Functional description * Stuff a status vector. * **************************************/ ISC_STATUS *stat; va_list ptr; VA_START(ptr, count); stat = status; *stat++ = gds_arg_gds; for (; count; --count) *stat++ = (ISC_STATUS) va_arg(ptr, ISC_STATUS); *stat = gds_arg_end; return status[1]; } static ISC_STATUS gen_sdl(ISC_STATUS * status, ISC_ARRAY_DESC * desc, SSHORT * sdl_buffer_length, UCHAR ** sdl_buffer, SSHORT * sdl_length) { /************************************** * * g e n _ s d l * ************************************** * * Functional description * **************************************/ SSHORT n, from, to, increment, dimensions; ISC_ARRAY_BOUND *tail; gen_t* gen; gen_t gen_block; dimensions = desc->array_desc_dimensions; if (dimensions > 16) return error(status, 5, (ISC_STATUS) gds_invalid_dimension, (ISC_STATUS) gds_arg_number, (ISC_STATUS) dimensions, (ISC_STATUS) gds_arg_number, (ISC_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 = 0; 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, (ISC_STATUS) FB_SUCCESS); } static ISC_STATUS lookup_desc(ISC_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, (ISC_STATUS) gds_fldnotdef, (ISC_STATUS) gds_arg_string, (ISC_STATUS) desc->array_desc_field_name, (ISC_STATUS) gds_arg_string, (ISC_STATUS) desc->array_desc_relation_name); return error(status, 1, (ISC_STATUS) FB_SUCCESS); } static ISC_STATUS stuff( GEN gen, SSHORT count, ...) { /************************************** * * s t u f f * ************************************** * * Functional description * Stuff a SDL byte. * **************************************/ UCHAR c, *new_sdl; va_list ptr; SSHORT new_len, current_len; if (gen->gen_sdl + count >= gen->gen_end) { /* The sdl buffer is too small. Allocate a larger one. */ new_len = gen->gen_end - *gen->gen_sdl_ptr + 512 + count; new_sdl = (UCHAR *) gds__alloc(new_len); if (!new_sdl) return error(gen->gen_status, 5, (ISC_STATUS) gds_misc_interpreted, (ISC_STATUS) gds_arg_string, (ISC_STATUS) "SDL buffer overflow", (ISC_STATUS) gds_arg_gds, (ISC_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 ISC_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. * **************************************/ ISC_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 ISC_STATUS stuff_string( GEN gen, UCHAR sdl, SCHAR * string) { /************************************** * * s t u f f _ s t r i n g * ************************************** * * Functional description * Stuff a "thing" then a counted string. * **************************************/ ISC_STATUS *status; status = gen->gen_status; STUFF_SDL(sdl); STUFF_SDL(strlen(string)); while (*string) STUFF_SDL(*string++); return FB_SUCCESS; }