driver/desc.cc (721 lines of code) (raw):
// Copyright (c) 2007, 2024, Oracle and/or its affiliates.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License, version 2.0, as
// published by the Free Software Foundation.
//
// This program is designed to work with certain software (including
// but not limited to OpenSSL) that is licensed under separate terms, as
// designated in a particular file or component or in included license
// documentation. The authors of MySQL hereby grant you an additional
// permission to link the program and your derivative works with the
// separately licensed software that they have either included with
// the program or referenced in the documentation.
//
// Without limiting anything contained in the foregoing, this file,
// which is part of Connector/ODBC, is also subject to the
// Universal FOSS Exception, version 1.0, a copy of which can be found at
// https://oss.oracle.com/licenses/universal-foss-exception.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License, version 2.0, for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
/**
@file desc.c
@brief Functions for handling descriptors.
*/
/***************************************************************************
* The following ODBC APIs are implemented in this file: *
* SQLSetDescField (ISO 92) *
* SQLGetDescField (ISO 92) *
* SQLCopyDesc (ISO 92) *
****************************************************************************/
#include "driver.h"
#include <algorithm>
/* Utility macros for defining descriptor fields */
#define HDR_FLD(field, perm, type) \
static desc_field HDR_##field= \
{(perm), (type), DESC_HDR, offsetof(DESC, field)}
/* parens around field in offsetof() confuse GCC */
#define REC_FLD(field, perm, type) \
static desc_field REC_##field= \
{(perm), (type), DESC_REC, offsetof(DESCREC, field)}
/*
* Allocate a new descriptor.
* Should be used to allocate 'implicit' descriptors on the statement
* or 'explicit' user-requested descriptors.
*/
DESC *desc_alloc(STMT *stmt, SQLSMALLINT alloc_type,
desc_ref_type ref_type, desc_desc_type desc_type)
{
DESC *desc= new DESC(stmt, alloc_type, ref_type, desc_type);
if (!desc)
return NULL;
return desc;
}
/*
Free a descriptor.
*/
void desc_free(DESC *desc)
{
delete desc;
}
void DESC::reset()
{
records2.clear();
}
void DESC::free_paramdata()
{
for (auto &r : records2)
r.par.reset();
}
/*
* Initialize APD
*/
void DESCREC::desc_rec_init_apd()
{
/* ODBC defaults */
concise_type= SQL_C_DEFAULT;
data_ptr= NULL;
indicator_ptr= NULL;
octet_length_ptr= NULL;
type= SQL_C_DEFAULT;
/* internal */
par.reset();
}
/*
* Initialize IPD
*/
void DESCREC::desc_rec_init_ipd()
{
/* ODBC defaults */
fixed_prec_scale= SQL_TRUE;
local_type_name= (SQLCHAR *)"";
nullable= SQL_NULLABLE;
parameter_type= SQL_PARAM_INPUT;
type_name= (SQLCHAR *)"VARCHAR";
is_unsigned= SQL_FALSE;
/* driver defaults */
name= (SQLCHAR *)"";
}
/*
* Initialize ARD
*/
void DESCREC::desc_rec_init_ard()
{
/* ODBC defaults */
concise_type= SQL_C_DEFAULT;
data_ptr= NULL;
indicator_ptr= NULL;
octet_length_ptr= NULL;
type= SQL_C_DEFAULT;
}
/*
* Initialize IRD
*/
void DESCREC::desc_rec_init_ird()
{
/* ODBC defaults */
/* driver defaults */
auto_unique_value= SQL_FALSE;
case_sensitive= SQL_TRUE;
concise_type= SQL_VARCHAR;
display_size= 100;/*?*/
fixed_prec_scale= SQL_TRUE;
length= 100;/*?*/
nullable= SQL_NULLABLE_UNKNOWN;
type= SQL_VARCHAR;
type_name= (SQLCHAR *)"VARCHAR";
unnamed= SQL_UNNAMED;
is_unsigned= SQL_FALSE;
}
void DESCREC::reset_to_defaults()
{
par.reset();
row.reset();
if (m_desc_type == DESC_PARAM && m_ref_type == DESC_APP)
desc_rec_init_apd();
else if (m_desc_type == DESC_PARAM && m_ref_type == DESC_IMP)
desc_rec_init_ipd();
else if (m_desc_type == DESC_ROW && m_ref_type == DESC_APP)
desc_rec_init_ard();
else if (m_desc_type == DESC_ROW && m_ref_type == DESC_IMP)
desc_rec_init_ird();
}
void DESCREC::par_struct::add_param_data(const char *chunk,
unsigned long length)
{
tempbuf.add_to_buffer(chunk, length);
// TODO: do the memory errors
// return stmt->set_error(MYERR_S1001, NULL, 4001);
}
/*
* Get a record from the descriptor.
*
* @param desc Descriptor
* @param recnum 0-based record number
* @param expand Whether to expand the descriptor to include the given
* recnum.
* @return The requested record of NULL if it doesn't exist
* (and isn't created).
*/
DESCREC *desc_get_rec(DESC *desc, int recnum, my_bool expand)
{
DESCREC *rec= NULL;
if (recnum == -1 && desc->stmt->stmt_options.bookmarks == SQL_UB_VARIABLE)
{
if (expand)
{
if (!desc->bookmark_count)
{
desc->bookmark2.emplace_back(desc->desc_type, desc->ref_type);
rec = &desc->bookmark2.back();
++desc->bookmark_count;
}
}
rec= &desc->bookmark2.back();
}
else if (recnum < 0)
{
desc->stmt->set_error("07009", "Invalid descriptor index", MYERR_07009);
return NULL;
}
else
{
assert(recnum >= 0);
/* expand if needed */
if (expand)
{
for (size_t i = desc->rcount(); expand && i <= recnum; ++i)
{
desc->records2.emplace_back(desc->desc_type, desc->ref_type);
rec = &desc->records2.back();
rec->reset_to_defaults();
}
}
if (recnum < desc->rcount())
rec = &desc->records2[recnum];
}
if (expand)
assert(rec);
return rec;
}
/*
* Check with the given descriptor contains any data-at-exec
* records. Return the record number or -1 if none are found.
*/
int desc_find_dae_rec(DESC *desc)
{
int i;
DESCREC *rec;
SQLLEN *octet_length_ptr;
for (i= 0; i < desc->rcount(); ++i)
{
rec= desc_get_rec(desc, i, FALSE);
assert(rec);
octet_length_ptr= (SQLLEN*)ptr_offset_adjust(rec->octet_length_ptr,
desc->bind_offset_ptr,
desc->bind_type,
sizeof(SQLLEN), /*row*/0);
if (IS_DATA_AT_EXEC(octet_length_ptr))
return i;
}
return -1;
}
/*
* Check with the given descriptor contains any output streams
* @param recnum[in,out] - pointer to 0-based record number to begin search from
and to store found record number
* @param res_col_num[in,out] - pointer to 0-based column number in output parameters resultset
* Returns the found record or NULL.
*/
DESCREC * desc_find_outstream_rec(STMT *stmt, uint *recnum, uint *res_col_num)
{
int i, start= recnum != NULL ? *recnum + 1 : 0;
DESCREC *rec;
uint column= *res_col_num;
/* No streams in iODBC */
#ifndef USE_IODBC
for (i= start; i < stmt->ipd->rcount(); ++i)
{
rec= desc_get_rec(stmt->ipd, i, FALSE);
assert(rec);
if (rec->parameter_type == SQL_PARAM_INPUT_OUTPUT_STREAM
|| rec->parameter_type == SQL_PARAM_OUTPUT_STREAM)
{
if (recnum != NULL)
{
*recnum= i;
}
*res_col_num= ++column;
/* Valuable information is in apd */
return desc_get_rec(stmt->apd, i, FALSE);
}
else if (rec->parameter_type == SQL_PARAM_INPUT_OUTPUT
|| rec->parameter_type == SQL_PARAM_OUTPUT)
{
++column;
}
}
#endif
return NULL;
}
/*
* Apply the actual value to the descriptor field.
*
* @param dest Pointer to descriptor field to be set.
* @param dest_type Type of descriptor field (same type constants as buflen).
* @param src Value to be set.
* @param buflen Length of value (as specified by SQLSetDescField).
*/
static void
apply_desc_val(void *dest, SQLSMALLINT dest_type, void *src, SQLINTEGER buflen)
{
switch (buflen)
{
case SQL_IS_SMALLINT:
case SQL_IS_INTEGER:
case SQL_IS_LEN:
if (dest_type == SQL_IS_SMALLINT)
*(SQLSMALLINT *)dest= (SQLSMALLINT)((SQLLEN)src);
else if (dest_type == SQL_IS_USMALLINT)
*(SQLUSMALLINT *)dest = (SQLUSMALLINT)((SQLLEN)src);
else if (dest_type == SQL_IS_INTEGER)
*(SQLINTEGER *)dest = (SQLINTEGER)((SQLLEN)src);
else if (dest_type == SQL_IS_UINTEGER)
*(SQLUINTEGER *)dest = (SQLUINTEGER)((SQLLEN)src);
else if (dest_type == SQL_IS_LEN)
*(SQLLEN *)dest = (SQLLEN)src;
else if (dest_type == SQL_IS_ULEN)
*(SQLULEN *)dest = (SQLULEN)src;
break;
case SQL_IS_USMALLINT:
case SQL_IS_UINTEGER:
case SQL_IS_ULEN:
if (dest_type == SQL_IS_SMALLINT)
*(SQLSMALLINT *)dest = (SQLSMALLINT)((SQLULEN)src);
else if (dest_type == SQL_IS_USMALLINT)
*(SQLUSMALLINT *)dest = (SQLUSMALLINT)((SQLULEN)src);
else if (dest_type == SQL_IS_INTEGER)
*(SQLINTEGER *)dest = (SQLINTEGER)((SQLULEN)src);
else if (dest_type == SQL_IS_UINTEGER)
*(SQLUINTEGER *)dest = (SQLUINTEGER)((SQLULEN)src);
else if (dest_type == SQL_IS_LEN)
*(SQLLEN *)dest= (SQLULEN)src;
else if (dest_type == SQL_IS_ULEN)
*(SQLULEN *)dest= (SQLULEN)src;
break;
case SQL_NTS:
case SQL_IS_POINTER:
*(SQLPOINTER *)dest= src;
break;
default:
/* TODO it's an actual data length */
/* free/malloc to the field and copy it */
/* TODO .. check for 22001 - String data, right truncated
* The FieldIdentifier argument was SQL_DESC_NAME,
* and the BufferLength argument was a value larger
* than SQL_MAX_IDENTIFIER_LEN.
*/
break;
}
}
/*
* Get a descriptor field based on the constant.
*/
static desc_field *
getfield(SQLSMALLINT fldid)
{
/* all field descriptions are immutable */
/* See: SQLSetDescField() documentation
* http://msdn2.microsoft.com/en-us/library/ms713560.aspx */
HDR_FLD(alloc_type , P_RI|P_RA , SQL_IS_SMALLINT);
HDR_FLD(array_size , P_RA|P_WA , SQL_IS_ULEN );
HDR_FLD(array_status_ptr , P_RI|P_WI|P_RA|P_WA, SQL_IS_POINTER );
HDR_FLD(bind_offset_ptr , P_RA|P_WA , SQL_IS_POINTER );
HDR_FLD(bind_type , P_RA|P_WA , SQL_IS_INTEGER );
HDR_FLD(count , P_RI|P_WI|P_RA|P_WA, SQL_IS_LEN );
HDR_FLD(rows_processed_ptr, P_RI|P_WI , SQL_IS_POINTER );
REC_FLD(auto_unique_value, PR_RIR , SQL_IS_INTEGER);
REC_FLD(base_column_name , PR_RIR , SQL_IS_POINTER);
REC_FLD(base_table_name , PR_RIR , SQL_IS_POINTER);
REC_FLD(case_sensitive , PR_RIR|PR_RIP , SQL_IS_INTEGER);
REC_FLD(catalog_name , PR_RIR , SQL_IS_POINTER);
REC_FLD(concise_type , PR_WAR|PR_WAP|PR_RIR|PR_WIP, SQL_IS_SMALLINT);
REC_FLD(data_ptr , PR_WAR|PR_WAP , SQL_IS_POINTER);
REC_FLD(display_size , PR_RIR , SQL_IS_LEN);
REC_FLD(fixed_prec_scale , PR_RIR|PR_RIP , SQL_IS_SMALLINT);
REC_FLD(indicator_ptr , PR_WAR|PR_WAP , SQL_IS_POINTER);
REC_FLD(label , PR_RIR , SQL_IS_POINTER);
REC_FLD(length , PR_WAR|PR_WAP|PR_RIR|PR_WIP, SQL_IS_ULEN);
REC_FLD(literal_prefix , PR_RIR , SQL_IS_POINTER);
REC_FLD(literal_suffix , PR_RIR , SQL_IS_POINTER);
REC_FLD(local_type_name , PR_RIR|PR_RIP , SQL_IS_POINTER);
REC_FLD(name , PR_RIR|PR_WIP , SQL_NTS);
REC_FLD(nullable , PR_RIR|PR_RIP , SQL_IS_SMALLINT);
REC_FLD(num_prec_radix , PR_WAR|PR_WAP|PR_RIR|PR_WIP, SQL_IS_INTEGER);
REC_FLD(octet_length , PR_WAR|PR_WAP|PR_RIR|PR_WIP, SQL_IS_LEN);
REC_FLD(octet_length_ptr , PR_WAR|PR_WAP , SQL_IS_POINTER);
REC_FLD(parameter_type , PR_WIP , SQL_IS_SMALLINT);
REC_FLD(precision , PR_WAR|PR_WAP|PR_RIR|PR_WIP, SQL_IS_SMALLINT);
REC_FLD(rowver , PR_RIR|PR_RIP , SQL_IS_SMALLINT);
REC_FLD(scale , PR_WAR|PR_WAP|PR_RIR|PR_WIP, SQL_IS_SMALLINT);
REC_FLD(schema_name , PR_RIR , SQL_IS_POINTER);
REC_FLD(searchable , PR_RIR , SQL_IS_SMALLINT);
REC_FLD(table_name , PR_RIR , SQL_IS_POINTER);
REC_FLD(type , PR_WAR|PR_WAP|PR_RIR|PR_WIP, SQL_IS_SMALLINT);
REC_FLD(type_name , PR_RIR|PR_RIP , SQL_IS_POINTER);
REC_FLD(unnamed , PR_RIR|PR_WIP , SQL_IS_SMALLINT);
REC_FLD(is_unsigned , PR_RIR|PR_RIP , SQL_IS_SMALLINT);
REC_FLD(updatable , PR_RIR , SQL_IS_SMALLINT);
REC_FLD(datetime_interval_code , PR_WAR|PR_WAP|PR_RIR|PR_WIP, SQL_IS_SMALLINT);
REC_FLD(datetime_interval_precision , PR_WAR|PR_WAP|PR_RIR|PR_WIP, SQL_IS_INTEGER);
/* match 'field' names above */
switch(fldid)
{
case SQL_DESC_ALLOC_TYPE:
return &HDR_alloc_type;
case SQL_DESC_ARRAY_SIZE:
return &HDR_array_size;
case SQL_DESC_ARRAY_STATUS_PTR:
return &HDR_array_status_ptr;
case SQL_DESC_BIND_OFFSET_PTR:
return &HDR_bind_offset_ptr;
case SQL_DESC_BIND_TYPE:
return &HDR_bind_type;
case SQL_DESC_COUNT:
return &HDR_count;
case SQL_DESC_ROWS_PROCESSED_PTR:
return &HDR_rows_processed_ptr;
case SQL_DESC_AUTO_UNIQUE_VALUE:
return &REC_auto_unique_value;
case SQL_DESC_BASE_COLUMN_NAME:
return &REC_base_column_name;
case SQL_DESC_BASE_TABLE_NAME:
return &REC_base_table_name;
case SQL_DESC_CASE_SENSITIVE:
return &REC_case_sensitive;
case SQL_DESC_CATALOG_NAME:
return &REC_catalog_name;
case SQL_DESC_CONCISE_TYPE:
return &REC_concise_type;
case SQL_DESC_DATA_PTR:
return &REC_data_ptr;
case SQL_DESC_DISPLAY_SIZE:
return &REC_display_size;
case SQL_DESC_FIXED_PREC_SCALE:
return &REC_fixed_prec_scale;
case SQL_DESC_INDICATOR_PTR:
return &REC_indicator_ptr;
case SQL_DESC_LABEL:
return &REC_label;
case SQL_DESC_LENGTH:
return &REC_length;
case SQL_DESC_LITERAL_PREFIX:
return &REC_literal_prefix;
case SQL_DESC_LITERAL_SUFFIX:
return &REC_literal_suffix;
case SQL_DESC_LOCAL_TYPE_NAME:
return &REC_local_type_name;
case SQL_DESC_NAME:
return &REC_name;
case SQL_DESC_NULLABLE:
return &REC_nullable;
case SQL_DESC_NUM_PREC_RADIX:
return &REC_num_prec_radix;
case SQL_DESC_OCTET_LENGTH:
return &REC_octet_length;
case SQL_DESC_OCTET_LENGTH_PTR:
return &REC_octet_length_ptr;
case SQL_DESC_PARAMETER_TYPE:
return &REC_parameter_type;
case SQL_DESC_PRECISION:
return &REC_precision;
case SQL_DESC_ROWVER:
return &REC_rowver;
case SQL_DESC_SCALE:
return &REC_scale;
case SQL_DESC_SCHEMA_NAME:
return &REC_schema_name;
case SQL_DESC_SEARCHABLE:
return &REC_searchable;
case SQL_DESC_TABLE_NAME:
return &REC_table_name;
case SQL_DESC_TYPE:
return &REC_type;
case SQL_DESC_TYPE_NAME:
return &REC_type_name;
case SQL_DESC_UNNAMED:
return &REC_unnamed;
case SQL_DESC_UNSIGNED:
return &REC_is_unsigned;
case SQL_DESC_UPDATABLE:
return &REC_updatable;
case SQL_DESC_DATETIME_INTERVAL_CODE:
return &REC_datetime_interval_code;
case SQL_DESC_DATETIME_INTERVAL_PRECISION:
return &REC_datetime_interval_precision;
}
return NULL;
}
/*
@type : ODBC 3.0 API
@purpose : Get a field of a descriptor.
*/
SQLRETURN
MySQLGetDescField(SQLHDESC hdesc, SQLSMALLINT recnum, SQLSMALLINT fldid,
SQLPOINTER valptr, SQLINTEGER buflen, SQLINTEGER *outlen)
{
DESC *desc= (DESC *)hdesc;
desc_field *fld = getfield(fldid);
void *src_struct;
void *src;
if (desc == NULL)
{
return SQL_INVALID_HANDLE;
}
CLEAR_DESC_ERROR(desc);
if (IS_IRD(desc) && desc->stmt->state < ST_PREPARED)
/* TODO if it's prepared is the IRD still ok to access?
* or must we pre-execute it */
return set_desc_error(desc, "HY007",
"Associated statement is not prepared",
MYERR_S1007);
if ((fld == NULL) ||
/* header permissions check */
(fld->loc == DESC_HDR &&
(desc->ref_type == DESC_APP && (~fld->perms & P_RA)) ||
(desc->ref_type == DESC_IMP && (~fld->perms & P_RI))))
{
return set_desc_error(desc, "HY091",
"Invalid descriptor field identifier",
MYERR_S1091);
}
else if (fld->loc == DESC_REC)
{
int perms= 0; /* needed perms to access */
if (desc->ref_type == DESC_APP)
perms= P_RA;
else if (desc->ref_type == DESC_IMP)
perms= P_RI;
if (desc->desc_type == DESC_PARAM)
perms= P_PAR(perms);
else if (desc->desc_type == DESC_ROW)
perms= P_ROW(perms);
if ((~fld->perms & perms) == perms)
return set_desc_error(desc, "HY091",
"Invalid descriptor field identifier",
MYERR_S1091);
}
/* get the src struct */
if (fld->loc == DESC_HDR)
src_struct= desc;
else
{
if (recnum < 1 || recnum > desc->rcount())
return set_desc_error(desc, "07009",
"Invalid descriptor index",
MYERR_07009);
src_struct= desc_get_rec(desc, recnum - 1, FALSE);
assert(src_struct);
}
if (fldid == SQL_DESC_COUNT)
{
/*
This requires special treatment because the field is dynamically
obtained from the function
*/
desc->rcount();
}
src= ((char *)src_struct) + fld->offset;
/* TODO checks when strings? */
if ((fld->data_type == SQL_IS_POINTER && buflen != SQL_IS_POINTER) ||
(fld->data_type != SQL_IS_POINTER && buflen == SQL_IS_POINTER))
return set_desc_error(desc, "HY015",
"Invalid parameter type",
MYERR_S1015);
switch (buflen)
{
case SQL_IS_SMALLINT:
if (fld->data_type == SQL_IS_SMALLINT)
*(SQLSMALLINT *)valptr= *(SQLSMALLINT *)src;
else if (fld->data_type == SQL_IS_USMALLINT)
*(SQLSMALLINT *)valptr= *(SQLUSMALLINT *)src;
else if (fld->data_type == SQL_IS_INTEGER)
*(SQLSMALLINT *)valptr= (SQLSMALLINT)(*(SQLINTEGER *)src);
else if (fld->data_type == SQL_IS_UINTEGER)
*(SQLSMALLINT *)valptr = (SQLSMALLINT)(*(SQLUINTEGER *)src);
else if (fld->data_type == SQL_IS_LEN)
*(SQLSMALLINT *)valptr = (SQLSMALLINT)(*(SQLLEN *)src);
else if (fld->data_type == SQL_IS_ULEN)
*(SQLSMALLINT *)valptr = (SQLSMALLINT)(*(SQLULEN *)src);
break;
case SQL_IS_USMALLINT:
if (fld->data_type == SQL_IS_SMALLINT)
*(SQLUSMALLINT *)valptr= *(SQLSMALLINT *)src;
else if (fld->data_type == SQL_IS_USMALLINT)
*(SQLUSMALLINT *)valptr= *(SQLUSMALLINT *)src;
else if (fld->data_type == SQL_IS_INTEGER)
*(SQLUSMALLINT *)valptr = (SQLUSMALLINT)(*(SQLINTEGER *)src);
else if (fld->data_type == SQL_IS_UINTEGER)
*(SQLUSMALLINT *)valptr = (SQLUSMALLINT)(*(SQLUINTEGER *)src);
else if (fld->data_type == SQL_IS_LEN)
*(SQLUSMALLINT *)valptr = (SQLUSMALLINT)(*(SQLLEN *)src);
else if (fld->data_type == SQL_IS_ULEN)
*(SQLUSMALLINT *)valptr = (SQLUSMALLINT)(*(SQLULEN *)src);
break;
case SQL_IS_INTEGER:
if (fld->data_type == SQL_IS_SMALLINT)
*(SQLINTEGER *)valptr= *(SQLSMALLINT *)src;
else if (fld->data_type == SQL_IS_USMALLINT)
*(SQLINTEGER *)valptr= *(SQLUSMALLINT *)src;
else if (fld->data_type == SQL_IS_INTEGER)
*(SQLINTEGER *)valptr= *(SQLINTEGER *)src;
else if (fld->data_type == SQL_IS_UINTEGER)
*(SQLINTEGER *)valptr= *(SQLUINTEGER *)src;
else if (fld->data_type == SQL_IS_LEN)
*(SQLINTEGER *)valptr = (SQLINTEGER)(*(SQLLEN *)src);
else if (fld->data_type == SQL_IS_ULEN)
*(SQLINTEGER *)valptr = (SQLINTEGER)(*(SQLULEN *)src);
break;
case SQL_IS_UINTEGER:
if (fld->data_type == SQL_IS_SMALLINT)
*(SQLUINTEGER *)valptr= *(SQLSMALLINT *)src;
else if (fld->data_type == SQL_IS_USMALLINT)
*(SQLUINTEGER *)valptr= *(SQLUSMALLINT *)src;
else if (fld->data_type == SQL_IS_INTEGER)
*(SQLUINTEGER *)valptr= *(SQLINTEGER *)src;
else if (fld->data_type == SQL_IS_UINTEGER)
*(SQLUINTEGER *)valptr= *(SQLUINTEGER *)src;
else if (fld->data_type == SQL_IS_LEN)
*(SQLUINTEGER *)valptr = (SQLUINTEGER)(*(SQLLEN *)src);
else if (fld->data_type == SQL_IS_ULEN)
*(SQLUINTEGER *)valptr = (SQLUINTEGER)(*(SQLULEN *)src);
break;
case SQL_IS_LEN:
if (fld->data_type == SQL_IS_SMALLINT)
*(SQLLEN *)valptr= *(SQLSMALLINT *)src;
else if (fld->data_type == SQL_IS_USMALLINT)
*(SQLLEN *)valptr= *(SQLUSMALLINT *)src;
else if (fld->data_type == SQL_IS_INTEGER)
*(SQLLEN *)valptr= *(SQLINTEGER *)src;
else if (fld->data_type == SQL_IS_UINTEGER)
*(SQLLEN *)valptr= *(SQLUINTEGER *)src;
else if (fld->data_type == SQL_IS_LEN)
*(SQLLEN *)valptr= *(SQLLEN *)src;
else if (fld->data_type == SQL_IS_ULEN)
*(SQLLEN *)valptr= *(SQLULEN *)src;
break;
case SQL_IS_ULEN:
if (fld->data_type == SQL_IS_SMALLINT)
*(SQLULEN *)valptr= *(SQLSMALLINT *)src;
else if (fld->data_type == SQL_IS_USMALLINT)
*(SQLULEN *)valptr= *(SQLUSMALLINT *)src;
else if (fld->data_type == SQL_IS_INTEGER)
*(SQLULEN *)valptr= *(SQLINTEGER *)src;
else if (fld->data_type == SQL_IS_UINTEGER)
*(SQLULEN *)valptr= *(SQLUINTEGER *)src;
else if (fld->data_type == SQL_IS_LEN)
*(SQLULEN *)valptr= *(SQLLEN *)src;
else if (fld->data_type == SQL_IS_ULEN)
*(SQLULEN *)valptr= *(SQLULEN *)src;
break;
case SQL_IS_POINTER:
*(SQLPOINTER *)valptr= *(SQLPOINTER *)src;
break;
default:
/* TODO it's an actual data length */
/* free/malloc to the field and copy it, etc, etc */
break;
}
return SQL_SUCCESS;
}
SQLRETURN DESC::set_error(char *state, const char *message, uint errcode)
{
error.sqlstate = state ? state : "";
error.message = std::string(stmt->dbc->st_error_prefix) + message;
error.native_error = errcode;
return SQL_ERROR;
}
DESC::DESC(STMT *p_stmt, SQLSMALLINT p_alloc_type,
desc_ref_type p_ref_type, desc_desc_type p_desc_type) :
alloc_type(p_alloc_type), array_size(1), array_status_ptr(nullptr),
bind_offset_ptr(nullptr), bind_type(SQL_BIND_BY_COLUMN),
count(0), bookmark_count(0),
rows_processed_ptr(nullptr), desc_type(p_desc_type), ref_type(p_ref_type),
stmt(p_stmt), dbc(nullptr)
{
if(stmt != nullptr)
dbc = p_stmt->dbc;
}
/*
@type : ODBC 3.0 API
@purpose : Set a field of a descriptor.
*/
SQLRETURN DESC::set_field(SQLSMALLINT recnum, SQLSMALLINT fldid,
SQLPOINTER val, SQLINTEGER buflen)
{
desc_field *fld = getfield(fldid);
void *dest_struct;
void *dest;
error.clear();
/* check for invalid IRD modification */
if (is_ird())
{
switch (fldid)
{
case SQL_DESC_ARRAY_STATUS_PTR:
case SQL_DESC_ROWS_PROCESSED_PTR:
break;
default:
return set_error("HY016",
"Cannot modify an implementation row descriptor",
MYERR_S1016);
}
}
if ((fld == NULL) ||
/* header permissions check */
(fld->loc == DESC_HDR &&
((ref_type == DESC_APP && (~fld->perms & P_WA)) ||
(ref_type == DESC_IMP && (~fld->perms & P_WI)))))
{
return set_error("HY091",
"Invalid descriptor field identifier",
MYERR_S1091);
}
else if (fld->loc == DESC_REC)
{
int perms= 0; /* needed perms to access */
if (ref_type == DESC_APP)
perms= P_WA;
else if (ref_type == DESC_IMP)
perms= P_WI;
if (desc_type == DESC_PARAM)
perms= P_PAR(perms);
else if (desc_type == DESC_ROW)
perms= P_ROW(perms);
if ((~fld->perms & perms) == perms)
return set_error("HY091",
"Invalid descriptor field identifier",
MYERR_S1091);
}
/* get the dest struct */
if (fld->loc == DESC_HDR)
dest_struct= this;
else
{
if (recnum < 1 && stmt->stmt_options.bookmarks == SQL_UB_OFF)
return set_error("07009",
"Invalid descriptor index",
MYERR_07009);
else
dest_struct= desc_get_rec(this, recnum - 1, TRUE);
}
dest= ((char *)dest_struct) + fld->offset;
/* some applications and even MSDN examples don't give a correct constant */
if (buflen == 0)
buflen= fld->data_type;
/* TODO checks when strings? */
if ((fld->data_type == SQL_IS_POINTER && buflen != SQL_IS_POINTER) ||
(fld->data_type != SQL_IS_POINTER && buflen == SQL_IS_POINTER))
return set_error("HY015",
"Invalid parameter type",
MYERR_S1015);
/* per-field checks/functionality */
switch (fldid)
{
case SQL_DESC_COUNT:
/* we just force the descriptor record count to expand */
(void)desc_get_rec(this, (int)((size_t)val - 1), TRUE);
break;
case SQL_DESC_NAME:
{
// dest_struct already points to the correct DESCREC*
DESCREC *name_rec = (DESCREC*)dest_struct;
// Add name as parameter data and zero terminating character
name_rec->par.add_param_data((char*)val, (unsigned long)strlen((char*)val) + 1);
// Get a pointer to allocated copy of the name
val = name_rec->par.val();
}
break;
/* We don't support named parameters, values stay as initialized */
//return set_desc_error(desc, "01S01",
// "Option value changed",
// MYERR_01S02);
case SQL_DESC_UNNAMED:
if ((size_t)val == SQL_NAMED)
return set_error("HY092",
"Invalid attribute/option identifier",
MYERR_S1092);
}
/* We have to unbind the value if not setting a buffer */
switch (fldid)
{
case SQL_DESC_DATA_PTR:
case SQL_DESC_OCTET_LENGTH_PTR:
case SQL_DESC_INDICATOR_PTR:
break;
default:
if (fld->loc == DESC_REC)
{
DESCREC *rec= (DESCREC *) dest_struct;
rec->data_ptr= NULL;
}
}
apply_desc_val(dest, fld->data_type, val, buflen);
/* post-set responsibilities */
/*http://msdn.microsoft.com/en-us/library/ms710963%28v=vs.85%29.aspx
"ParameterType Argument" sectiosn - basically IPD has to be heres as well with same rules
C and SQL types match. Thus we can use same function for calculation of type and dti code.
*/
if ((is_ard() || is_apd() || is_ipd()) && fld->loc == DESC_REC)
{
DESCREC *rec= (DESCREC *) dest_struct;
switch (fldid)
{
case SQL_DESC_TYPE:
rec->concise_type= rec->type;
rec->datetime_interval_code= 0;
break;
case SQL_DESC_CONCISE_TYPE:
rec->type= get_type_from_concise_type(rec->concise_type);
rec->datetime_interval_code=
get_dticode_from_concise_type(rec->concise_type);
break;
case SQL_DESC_DATETIME_INTERVAL_CODE: /* TODO validation for this value? */
/* SQL_DESC_TYPE has to have already been set */
if (rec->type == SQL_DATETIME)
rec->concise_type=
get_concise_type_from_datetime_code(rec->datetime_interval_code);
else
rec->concise_type=
get_concise_type_from_interval_code(rec->datetime_interval_code);
break;
}
switch (fldid)
{
case SQL_DESC_TYPE:
case SQL_DESC_CONCISE_TYPE:
/* setup type specific defaults (TODO others besides SQL_C_NUMERIC)? */
if (is_ard() && rec->type == SQL_C_NUMERIC)
{
rec->precision= 38;
rec->scale= 0;
}
}
}
/*
Set "real_param_done" for parameters if all fields needed to bind
a parameter are set.
*/
if (is_apd() && val != NULL && fld->loc == DESC_REC)
{
DESCREC *rec= (DESCREC *) dest_struct;
switch (fldid)
{
case SQL_DESC_DATA_PTR:
case SQL_DESC_OCTET_LENGTH_PTR:
case SQL_DESC_INDICATOR_PTR:
rec->par.real_param_done= TRUE;
break;
}
}
return SQL_SUCCESS;
}
/*
@type : ODBC 3.0 API
@purpose : Copy descriptor information from one descriptor to another.
Errors are placed in the TargetDescHandle.
*/
SQLRETURN MySQLCopyDesc(SQLHDESC SourceDescHandle, SQLHDESC TargetDescHandle)
{
DESC *src= (DESC *)SourceDescHandle;
DESC *dest= (DESC *)TargetDescHandle;
CLEAR_DESC_ERROR(dest);
if (IS_IRD(dest))
return set_desc_error(dest, "HY016",
"Cannot modify an implementation row descriptor",
MYERR_S1016);
if (IS_IRD(src) && src->stmt->state < ST_PREPARED)
return set_desc_error(dest, "HY007",
"Associated statement is not prepared",
MYERR_S1007);
/* copy the records, copy constructors should take care of everything */
*dest = *src;
/* TODO consistency check on target, if needed (apd) */
return SQL_SUCCESS;
}
/*
* Call SQLGetDescField in the "context" of a statement. This will copy
* any error from the descriptor to the statement.
*/
SQLRETURN
stmt_SQLGetDescField(STMT *stmt, DESC *desc, SQLSMALLINT recnum,
SQLSMALLINT fldid, SQLPOINTER valptr,
SQLINTEGER buflen, SQLINTEGER *outlen)
{
SQLRETURN rc;
if ((rc= MySQLGetDescField((SQLHANDLE)desc, recnum, fldid,
valptr, buflen, outlen)) != SQL_SUCCESS)
stmt->error = desc->error;
return rc;
}
/*
* Call SQLSetDescField in the "context" of a statement. This will copy
* any error from the descriptor to the statement.
*/
SQLRETURN
stmt_SQLSetDescField(STMT *stmt, DESC *desc, SQLSMALLINT recnum,
SQLSMALLINT fldid, SQLPOINTER val, SQLINTEGER buflen)
{
SQLRETURN rc;
CHECK_HANDLE(desc);
if ((rc= desc->set_field(recnum, fldid, val, buflen)) != SQL_SUCCESS)
stmt->error = desc->error;
return rc;
}
/*
* Call SQLCopyDesc in the "context" of a statement. This will copy
* any error from the descriptor to the statement.
*/
SQLRETURN stmt_SQLCopyDesc(STMT *stmt, DESC *src, DESC *dest)
{
SQLRETURN rc;
if ((rc= MySQLCopyDesc((SQLHANDLE)src, (SQLHANDLE)dest)) != SQL_SUCCESS)
stmt->error = dest->error;
return rc;
}
SQLRETURN SQL_API
SQLCopyDesc(SQLHDESC SourceDescHandle, SQLHDESC TargetDescHandle)
{
CHECK_HANDLE(SourceDescHandle);
CHECK_HANDLE(TargetDescHandle);
return MySQLCopyDesc(SourceDescHandle, TargetDescHandle);
}