driver/catalog_no_i_s.cc (846 lines of code) (raw):
// Copyright (c) 2000, 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 catalog_no_i_s.c
@brief Catalog functions not using I_S.
@remark All functions suppose that parameters specifying other parameters lenthes can't SQL_NTS.
caller should take care of that.
*/
#include "driver.h"
#include "catalog.h"
/*
Helper function to deduce the MySQL database name from catalog,
schema or current database taking into account NO_CATALOG and NO_SCHEMA
options.
*/
std::string get_database_name(STMT *stmt,
SQLCHAR *catalog, SQLINTEGER catalog_len,
SQLCHAR *schema, SQLINTEGER schema_len,
bool try_reget)
{
std::string db;
if (!stmt->dbc->ds.opt_NO_CATALOG && catalog && catalog_len)
{
// Catalog parameter can be used
db = (catalog_len != SQL_NTS ? std::string((char *)catalog, catalog_len) :
std::string((char *)catalog));
}
else if(!stmt->dbc->ds.opt_NO_SCHEMA && schema && schema_len)
{
// Schema parameter can be used
db = (schema_len != SQL_NTS ? std::string((char*)schema, schema_len) :
std::string((char *)schema));
}
else if (!stmt->dbc->ds.opt_NO_CATALOG || !stmt->dbc->ds.opt_NO_SCHEMA)
{
if (!try_reget)
return db;
reget_current_catalog(stmt->dbc);
db = !stmt->dbc->database.empty() ? stmt->dbc->database : "null";
}
return db;
}
/*
@type : internal
@purpose : validate for give table type from the list
*/
static my_bool check_table_type(const SQLCHAR *TableType,
const char *req_type,
uint len)
{
char req_type_quoted[NAME_LEN+2], req_type_quoted1[NAME_LEN+2];
char *type, *table_type= (char *)TableType;
my_bool found= 0;
if ( !TableType || !TableType[0] )
return found;
/*
Check and return only 'user' tables from current DB and
don't return any tables when its called with
SYSTEM TABLES.
Expected Types:
"TABLE", "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY",
"LOCAL TEMPORARY", "ALIAS", "SYNONYM",
*/
type= strstr(table_type,",");
sprintf(req_type_quoted,"'%s'",req_type);
sprintf(req_type_quoted1,"`%s`",req_type);
while ( type )
{
while ( isspace(*(table_type)) ) ++table_type;
if ( !myodbc_casecmp(table_type,req_type,len) ||
!myodbc_casecmp(table_type,req_type_quoted,len+2) ||
!myodbc_casecmp(table_type,req_type_quoted1,len+2) )
{
found= 1;
break;
}
table_type= ++type;
type= strstr(table_type,",");
}
if ( !found )
{
while ( isspace(*(table_type)) ) ++table_type;
if ( !myodbc_casecmp(table_type,req_type,len) ||
!myodbc_casecmp(table_type,req_type_quoted,len+2) ||
!myodbc_casecmp(table_type,req_type_quoted1,len+2) )
found= 1;
}
return found;
}
/*
@type : internal
@purpose : returns columns from a particular table, NULL on error
*/
static MYSQL_RES *server_list_dbkeys(STMT *stmt,
SQLCHAR *catalog,
SQLSMALLINT catalog_len,
SQLCHAR *table,
SQLSMALLINT table_len)
{
DBC *dbc = stmt->dbc;
MYSQL *mysql= dbc->mysql;
char tmpbuff[1024];
std::string query;
query.reserve(1024);
size_t cnt = 0;
query = "SHOW KEYS FROM `";
if (catalog_len)
{
cnt = myodbc_escape_string(stmt, tmpbuff, (ulong)sizeof(tmpbuff),
(char *)catalog, catalog_len, true, true);
query.append(tmpbuff, cnt);
query.append("`.`");
}
cnt = myodbc_escape_string(stmt, tmpbuff, (ulong)sizeof(tmpbuff),
(char *)table, table_len, true, true);
query.append(tmpbuff, cnt);
query.append("`");
MYLOG_DBC_QUERY(dbc, query.c_str());
if (exec_stmt_query(stmt, query.c_str(), query.length(), FALSE))
return NULL;
return mysql_store_result(mysql);
}
/*
@type : internal
@purpose : returns a table privileges result, NULL on error. Uses mysql pk_db tables
*/
static MYSQL_RES *table_privs_raw_data( STMT * stmt,
SQLCHAR * catalog,
SQLSMALLINT catalog_len,
SQLCHAR * table,
SQLSMALLINT table_len)
{
DBC *dbc= stmt->dbc;
MYSQL *mysql= dbc->mysql;
char tmpbuff[1024];
std::string query;
size_t cnt = 0;
query.reserve(1024);
query = "SELECT Db,User,Table_name,Grantor,Table_priv "
"FROM mysql.tables_priv WHERE Table_name LIKE '";
cnt = myodbc_escape_string(stmt, tmpbuff, sizeof(tmpbuff),
(char*)table, table_len);
query.append(tmpbuff, cnt);
query.append("' AND Db = ");
if (catalog_len)
{
query.append("'");
cnt = myodbc_escape_string(stmt, tmpbuff, sizeof(tmpbuff),
(char*)catalog, catalog_len);
query.append(tmpbuff, cnt);
query.append("'");
}
else
query.append("DATABASE()");
query.append(" ORDER BY Db, Table_name, Table_priv, User");
MYLOG_DBC_QUERY(dbc, query.c_str());
if (exec_stmt_query(stmt, query.c_str(), query.length(), FALSE))
return NULL;
return mysql_store_result(mysql);
}
#define MY_MAX_TABPRIV_COUNT 21
#define MY_MAX_COLPRIV_COUNT 3
const char *SQLTABLES_priv_values[]=
{
NULL,"",NULL,NULL,NULL,NULL,NULL
};
MYSQL_FIELD SQLTABLES_priv_fields[]=
{
MYODBC_FIELD_NAME("TABLE_CAT", 0),
MYODBC_FIELD_NAME("TABLE_SCHEM", 0),
MYODBC_FIELD_NAME("TABLE_NAME", NOT_NULL_FLAG),
MYODBC_FIELD_NAME("GRANTOR", 0),
MYODBC_FIELD_NAME("GRANTEE", NOT_NULL_FLAG),
MYODBC_FIELD_NAME("PRIVILEGE", NOT_NULL_FLAG),
MYODBC_FIELD_NAME("IS_GRANTABLE", 0),
};
const uint SQLTABLES_PRIV_FIELDS = (uint)array_elements(SQLTABLES_priv_values);
/*
****************************************************************************
SQLColumnPrivileges
****************************************************************************
*/
/*
@type : internal
@purpose : returns a column privileges result, NULL on error
*/
static MYSQL_RES *column_privs_raw_data(STMT * stmt,
SQLCHAR * catalog,
SQLSMALLINT catalog_len,
SQLCHAR * table,
SQLSMALLINT table_len,
SQLCHAR * column,
SQLSMALLINT column_len)
{
DBC *dbc = stmt->dbc;
MYSQL *mysql = dbc->mysql;
char tmpbuff[1024];
std::string query;
size_t cnt = 0;
query.reserve(1024);
query = "SELECT c.Db, c.User, c.Table_name, c.Column_name,"
"t.Grantor, c.Column_priv, t.Table_priv "
"FROM mysql.columns_priv AS c, mysql.tables_priv AS t "
"WHERE c.Table_name = '";
cnt = myodbc_escape_string(stmt, tmpbuff, sizeof(tmpbuff),
(char*)table, table_len);
query.append(tmpbuff, cnt);
query.append("' AND c.Db = ");
if (catalog_len)
{
query.append("'");
cnt = myodbc_escape_string(stmt, tmpbuff, sizeof(tmpbuff),
(char*)catalog, catalog_len);
query.append(tmpbuff, cnt);
query.append("'");
}
else
query.append("DATABASE()");
query.append("AND c.Column_name LIKE '");
cnt = myodbc_escape_string(stmt, tmpbuff, sizeof(tmpbuff),
(char*)column, column_len);
query.append(tmpbuff, cnt);
query.append("' AND c.Table_name = t.Table_name "
"ORDER BY c.Db, c.Table_name, c.Column_name, c.Column_priv");
if (exec_stmt_query(stmt, query.c_str(), query.length(), FALSE))
return NULL;
return mysql_store_result(mysql);
}
char *SQLCOLUMNS_priv_values[]=
{
NULL,"",NULL,NULL,NULL,NULL,NULL,NULL
};
MYSQL_FIELD SQLCOLUMNS_priv_fields[]=
{
MYODBC_FIELD_NAME("TABLE_CAT", 0),
MYODBC_FIELD_NAME("TABLE_SCHEM", 0),
MYODBC_FIELD_NAME("TABLE_NAME", NOT_NULL_FLAG),
MYODBC_FIELD_NAME("COLUMN_NAME", NOT_NULL_FLAG),
MYODBC_FIELD_NAME("GRANTOR", 0),
MYODBC_FIELD_NAME("GRANTEE", NOT_NULL_FLAG),
MYODBC_FIELD_NAME("PRIVILEGE", NOT_NULL_FLAG),
MYODBC_FIELD_NAME("IS_GRANTABLE", 0),
};
const uint SQLCOLUMNS_PRIV_FIELDS = (uint)array_elements(SQLCOLUMNS_priv_values);
/**
Get the CREATE TABLE statement for the given table.
Lengths may not be SQL_NTS.
@param[in] stmt Handle to statement
@param[in] catalog Catalog (database) of table, @c NULL for current
@param[in] catalog_length Length of catalog name
@param[in] table Name of table
@param[in] table_length Length of table name
@return Result of SHOW CREATE TABLE , or NULL if there is an error
or empty result (check mysql_errno(stmt->dbc->mysql) != 0)
*/
MYSQL_RES *server_show_create_table(STMT *stmt,
SQLCHAR *catalog,
SQLSMALLINT catalog_length,
SQLCHAR *table,
SQLSMALLINT table_length)
{
MYSQL *mysql= stmt->dbc->mysql;
std::string query;
size_t cnt = 0;
query.reserve(1024);
query = "SHOW CREATE TABLE ";
if (catalog && *catalog)
{
query.append(" `").append((char *)catalog).append("`.");
}
/* Empty string won't match anything. */
if (!*table)
return NULL;
if (table && *table)
{
query.append(" `").append((char *)table).append("`");
}
MYLOG_QUERY(stmt, query.c_str());
if (mysql_real_query(mysql, query.c_str(),(unsigned long)query.length()))
{
return NULL;
}
return mysql_store_result(mysql);
}
/*
****************************************************************************
SQLForeignKeys
****************************************************************************
*/
MYSQL_FIELD SQLFORE_KEYS_fields[]=
{
MYODBC_FIELD_NAME("PKTABLE_CAT", 0),
MYODBC_FIELD_NAME("PKTABLE_SCHEM", 0),
MYODBC_FIELD_NAME("PKTABLE_NAME", NOT_NULL_FLAG),
MYODBC_FIELD_NAME("PKCOLUMN_NAME", NOT_NULL_FLAG),
MYODBC_FIELD_NAME("FKTABLE_CAT", 0),
MYODBC_FIELD_NAME("FKTABLE_SCHEM", 0),
MYODBC_FIELD_NAME("FKTABLE_NAME", NOT_NULL_FLAG),
MYODBC_FIELD_NAME("FKCOLUMN_NAME", NOT_NULL_FLAG),
MYODBC_FIELD_SHORT("KEY_SEQ", NOT_NULL_FLAG),
MYODBC_FIELD_SHORT("UPDATE_RULE", 0),
MYODBC_FIELD_SHORT("DELETE_RULE", 0),
MYODBC_FIELD_NAME("FK_NAME", 0),
MYODBC_FIELD_NAME("PK_NAME", 0),
MYODBC_FIELD_SHORT("DEFERRABILITY", 0),
};
const uint SQLFORE_KEYS_FIELDS = (uint)array_elements(SQLFORE_KEYS_fields);
/* Multiple array of Struct to store and sort SQLForeignKeys field */
typedef struct SQL_FOREIGN_KEY_FIELD
{
char PKTABLE_CAT[NAME_LEN + 1];
char PKTABLE_SCHEM[NAME_LEN + 1];
char PKTABLE_NAME[NAME_LEN + 1];
char PKCOLUMN_NAME[NAME_LEN + 1];
char FKTABLE_CAT[NAME_LEN + 1];
char FKTABLE_SCHEM[NAME_LEN + 1];
char FKTABLE_NAME[NAME_LEN + 1];
char FKCOLUMN_NAME[NAME_LEN + 1];
int KEY_SEQ;
int UPDATE_RULE;
int DELETE_RULE;
char FK_NAME[NAME_LEN + 1];
char PK_NAME[NAME_LEN + 1];
int DEFERRABILITY;
} MY_FOREIGN_KEY_FIELD;
char *SQLFORE_KEYS_values[]= {
NULL,"",NULL,NULL,
NULL,"",NULL,NULL,
0,0,0,NULL,NULL,0
};
/*
* Get a record from the array if exists otherwise allocate a new
* record and return.
*
* @param records MY_FOREIGN_KEY_FIELD record
* @param recnum 0-based record number
*
* @return The requested record or NULL if it doesn't exist
* (and isn't created).
*/
MY_FOREIGN_KEY_FIELD *fk_get_rec(std::vector<MY_FOREIGN_KEY_FIELD> &records, unsigned int recnum)
{
while(records.size() <= recnum)
records.push_back(MY_FOREIGN_KEY_FIELD());
return &records[recnum];
}
/*
****************************************************************************
SQLPrimaryKeys
****************************************************************************
*/
MYSQL_FIELD SQLPRIM_KEYS_fields[]=
{
MYODBC_FIELD_NAME("TABLE_CAT", 0),
MYODBC_FIELD_NAME("TABLE_SCHEM", 0),
MYODBC_FIELD_NAME("TABLE_NAME", NOT_NULL_FLAG),
MYODBC_FIELD_NAME("COLUMN_NAME", NOT_NULL_FLAG),
MYODBC_FIELD_SHORT("KEY_SEQ", NOT_NULL_FLAG),
MYODBC_FIELD_STRING("PK_NAME", 128, 0),
};
const uint SQLPRIM_KEYS_FIELDS = (uint)array_elements(SQLPRIM_KEYS_fields);
const long SQLPRIM_LENGTHS[]= {0, 0, 1, 5, 4, -7};
char *SQLPRIM_KEYS_values[]= {
NULL,"",NULL,NULL,0,NULL
};
/*
@purpose : returns the column names that make up the primary key for a table.
The driver returns the information as a result set. This function
does not support returning primary keys from multiple tables in
a single call
*/
SQLRETURN
primary_keys_no_i_s(SQLHSTMT hstmt,
SQLCHAR *catalog, SQLSMALLINT catalog_len,
SQLCHAR *schema, SQLSMALLINT schema_len,
SQLCHAR *table, SQLSMALLINT table_len)
{
STMT *stmt= (STMT *) hstmt;
MYSQL_ROW row;
uint row_count;
ODBC_RESULTSET odbc_rs;
assert(stmt);
LOCK_DBC(stmt->dbc);
auto db = get_database_name(stmt, catalog, catalog_len,
schema, schema_len);
if (!(odbc_rs = server_list_dbkeys(stmt, (SQLCHAR*)db.c_str(),
(SQLSMALLINT)db.length(),
table, table_len)))
{
SQLRETURN rc= handle_connection_error(stmt);
return rc;
}
// Free if result data was not in row storage.
stmt->reset_result_array();
// We will use the ROW_STORAGE here
stmt->m_row_storage.set_size(odbc_rs->row_count,
SQLPRIM_KEYS_FIELDS);
auto &data = stmt->m_row_storage;
row_count= 0;
while ( (row= mysql_fetch_row(odbc_rs)) )
{
if ( row[1][0] == '0' ) /* If unique index */
{
if ( row_count && !strcmp(row[3],"1") )
break; /* Already found unique key */
/* TABLE_CAT and TABLE_SCHEMA */
CAT_SCHEMA_SET(data[0], data[1], db);
/* TABLE_NAME */
data[2] = row[0];
/* COLUMN_NAME */
data[3] = row[4];
/* KEY_SEQ */
data[4] = row[3];
/* PK_NAME */
data[5] = "PRIMARY";
data.next_row();
++row_count;
}
}
stmt->result_array = (MYSQL_ROW)data.data();
create_fake_resultset(stmt, stmt->result_array, row_count,
SQLPRIM_KEYS_fields, SQLPRIM_KEYS_FIELDS, false);
return SQL_SUCCESS;
}
/*
****************************************************************************
SQLProcedure Columns
****************************************************************************
*/
char *SQLPROCEDURECOLUMNS_values[]= {
"", "", NullS, NullS, "", "", "",
"", "", "", "10", "",
"MySQL column", "", "", NullS, "",
NullS, ""
};
/* TODO make LONGLONG fields just LONG if SQLLEN is 4 bytes */
MYSQL_FIELD SQLPROCEDURECOLUMNS_fields[]=
{
MYODBC_FIELD_NAME("PROCEDURE_CAT", 0),
MYODBC_FIELD_NAME("PROCEDURE_SCHEM", 0),
MYODBC_FIELD_NAME("PROCEDURE_NAME", NOT_NULL_FLAG),
MYODBC_FIELD_NAME("COLUMN_NAME", NOT_NULL_FLAG),
MYODBC_FIELD_SHORT ("COLUMN_TYPE", NOT_NULL_FLAG),
MYODBC_FIELD_SHORT ("DATA_TYPE", NOT_NULL_FLAG),
MYODBC_FIELD_STRING("TYPE_NAME", 20, NOT_NULL_FLAG),
MYODBC_FIELD_LONGLONG("COLUMN_SIZE", 0),
MYODBC_FIELD_LONGLONG("BUFFER_LENGTH", 0),
MYODBC_FIELD_SHORT ("DECIMAL_DIGITS", 0),
MYODBC_FIELD_SHORT ("NUM_PREC_RADIX", 0),
MYODBC_FIELD_SHORT ("NULLABLE", NOT_NULL_FLAG),
MYODBC_FIELD_NAME("REMARKS", 0),
MYODBC_FIELD_NAME("COLUMN_DEF", 0),
MYODBC_FIELD_SHORT ("SQL_DATA_TYPE", NOT_NULL_FLAG),
MYODBC_FIELD_SHORT ("SQL_DATETIME_SUB", 0),
MYODBC_FIELD_LONGLONG("CHAR_OCTET_LENGTH", 0),
MYODBC_FIELD_LONG ("ORDINAL_POSITION", NOT_NULL_FLAG),
MYODBC_FIELD_STRING("IS_NULLABLE", 3, 0),
};
const uint SQLPROCEDURECOLUMNS_FIELDS =
(uint)array_elements(SQLPROCEDURECOLUMNS_fields);
/*
@type : internal
@purpose : returns procedure params as resultset
*/
static MYSQL_RES *server_list_proc_params(STMT *stmt,
SQLCHAR *catalog,
SQLSMALLINT catalog_len,
SQLCHAR *proc_name,
SQLSMALLINT proc_name_len,
SQLCHAR *par_name,
SQLSMALLINT par_name_len)
{
DBC *dbc = stmt->dbc;
MYSQL *mysql= dbc->mysql;
char tmpbuf[1024];
std::string qbuff;
qbuff.reserve(2048);
auto append_escaped_string = [&mysql, &tmpbuf, &stmt](std::string &outstr,
SQLCHAR* str,
SQLSMALLINT len)
{
tmpbuf[0] = '\0';
outstr.append("'");
auto cnt = myodbc_escape_string(stmt, tmpbuf, sizeof(tmpbuf),
(char*)str, len);
outstr.append(tmpbuf, cnt).append("'");
};
if((is_minimum_version(dbc->mysql->server_version, "5.7")))
{
qbuff = "select SPECIFIC_NAME, (IF(ISNULL(PARAMETER_NAME), "
"concat('OUT RETURN_VALUE ', DTD_IDENTIFIER), "
"concat(PARAMETER_MODE, ' `', PARAMETER_NAME, '` ', DTD_IDENTIFIER)) "
") as PARAMS_LIST, SPECIFIC_SCHEMA, ROUTINE_TYPE, ORDINAL_POSITION "
"FROM information_schema.parameters "
"WHERE SPECIFIC_SCHEMA LIKE ";
if (catalog_len)
append_escaped_string(qbuff, catalog, catalog_len);
else
qbuff.append("DATABASE()");
if (proc_name_len)
{
qbuff.append(" AND SPECIFIC_NAME LIKE ");
append_escaped_string(qbuff, proc_name, proc_name_len);
}
if (par_name_len)
{
qbuff.append(" AND (PARAMETER_NAME LIKE ");
append_escaped_string(qbuff, par_name, par_name_len);
qbuff.append(" OR ISNULL(PARAMETER_NAME))");
}
qbuff.append(" ORDER BY SPECIFIC_SCHEMA, SPECIFIC_NAME, ORDINAL_POSITION ASC");
}
else
{
qbuff = "SELECT name, CONCAT(IF(length(returns)>0, "
"CONCAT('RETURN_VALUE ', returns, if(length(param_list)>0, ',', '')),''), param_list),"
"db, type FROM mysql.proc WHERE Db=";
if (catalog_len)
append_escaped_string(qbuff, catalog, catalog_len);
else
qbuff.append("DATABASE()");
if (proc_name_len)
{
qbuff.append(" AND name LIKE ");
append_escaped_string(qbuff, proc_name, proc_name_len);
}
qbuff.append(" ORDER BY Db, name");
}
MYLOG_DBC_QUERY(dbc, qbuff.c_str());
if (exec_stmt_query(stmt, qbuff.c_str(), qbuff.length(), FALSE))
return NULL;
return mysql_store_result(mysql);
}
/*
@type : ODBC 1.0 API
@purpose : returns the list of input and output parameters, as well as
the columns that make up the result set for the specified
procedures. The driver returns the information as a result
set on the specified statement
*/
SQLRETURN
procedure_columns_no_i_s(SQLHSTMT hstmt,
SQLCHAR *catalog, SQLSMALLINT catalog_len,
SQLCHAR *schema, SQLSMALLINT schema_len,
SQLCHAR *proc, SQLSMALLINT proc_len,
SQLCHAR *column, SQLSMALLINT column_len)
{
STMT *stmt= (STMT *)hstmt;
SQLRETURN nReturn= SQL_SUCCESS;
MYSQL_ROW row;
ODBC_RESULTSET proc_list_res;
int params_num = 0;
unsigned int total_params_num = 0;
std::string db;
assert(stmt);
/* get procedures list */
LOCK_DBC(stmt->dbc);
try
{
db = get_database_name(stmt, catalog, catalog_len,
schema, schema_len, false);
// TODO: Extend param info fetching from I_S
if (!(proc_list_res= server_list_proc_params(stmt,
(SQLCHAR*)db.c_str(), (SQLSMALLINT)db.length(), proc, proc_len,
column, column_len)))
{
nReturn= stmt->set_error(MYERR_S1000);
throw ODBCEXCEPTION();
}
// Free if result data was not in row storage.
stmt->reset_result_array();
// We will use the ROW_STORAGE here
stmt->m_row_storage.set_size(proc_list_res->row_count,
SQLPROCEDURECOLUMNS_FIELDS);
auto &data = stmt->m_row_storage;
while ((row= mysql_fetch_row(proc_list_res)))
{
char *token;
char *param_str;
char *param_str_end;
param_str= row[1];
if(!param_str[0])
continue;
param_str_end= param_str + strlen(param_str);
token = proc_param_tokenize(param_str, ¶ms_num);
if (params_num == 0)
{
throw ODBCEXCEPTION(EXCEPTION_TYPE::EMPTY_SET);
}
while (token != NULL)
{
SQLSMALLINT ptype= 0;
int sql_type_index;
unsigned int flags= 0;
SQLCHAR param_name[NAME_LEN]= "\0";
SQLCHAR param_dbtype[1024]= "\0";
SQLCHAR param_size_buf[21]= "\0";
SQLCHAR param_buffer_len[21]= "\0";
SQLTypeMap *type_map;
SQLSMALLINT dec;
SQLULEN param_size= 0;
token= proc_get_param_type(token, (int)strlen(token), &ptype);
token= proc_get_param_name(token, (int)strlen(token), (char*)param_name);
token= proc_get_param_dbtype(token, (int)strlen(token), (char*)param_dbtype);
/* param_dbtype is lowercased in the proc_get_param_dbtype */
if (strstr((const char*)param_dbtype, "unsigned"))
flags |= UNSIGNED_FLAG;
sql_type_index= proc_get_param_sql_type_index((const char*)param_dbtype, (int)strlen((const char*)param_dbtype));
type_map= proc_get_param_map_by_index(sql_type_index);
param_size= proc_get_param_size(param_dbtype, (int)strlen((const char*)param_dbtype), sql_type_index, &dec);
proc_get_param_octet_len(stmt, sql_type_index, param_size, dec, flags, (char*)param_buffer_len);
/* PROCEDURE_CAT and PROCEDURE_SCHEMA */
CAT_SCHEMA_SET(data[mypcPROCEDURE_CAT], data[mypcPROCEDURE_SCHEM], row[2]);
data[mypcPROCEDURE_NAME] = row[0];
data[mypcCOLUMN_NAME] = (const char*)param_name;
/* The ordinal position can start with "0" only for return values */
if (row[4][0] == '0')
{
ptype= SQL_RETURN_VALUE;
}
data[mypcCOLUMN_TYPE] = ptype;
if (!myodbc_strcasecmp((const char*)type_map->type_name, "bit") && param_size > 1)
data[mypcDATA_TYPE] = SQL_BINARY;
else
data[mypcDATA_TYPE] = type_map->sql_type;
if (!myodbc_strcasecmp((const char*)type_map->type_name, "set") ||
!myodbc_strcasecmp((const char*)type_map->type_name, "enum"))
{
data[mypcTYPE_NAME] = "char";
}
else
{
data[mypcTYPE_NAME]= (const char*)type_map->type_name;
}
proc_get_param_col_len(stmt, sql_type_index, param_size, dec, flags, (char*)param_size_buf);
data[mypcCOLUMN_SIZE] = (const char*)param_size_buf;
data[mypcBUFFER_LENGTH] = (const char*)param_buffer_len;
if (dec != SQL_NO_TOTAL)
{
data[mypcDECIMAL_DIGITS] = dec;
data[mypcNUM_PREC_RADIX] = "10";
}
else
{
data[mypcDECIMAL_DIGITS] = nullptr;
data[mypcNUM_PREC_RADIX] = nullptr;
}
data[mypcNULLABLE] = "1"; /* NULLABLE */
data[mypcREMARKS] = ""; /* REMARKS */
data[mypcCOLUMN_DEF] = nullptr; /* COLUMN_DEF */
if(type_map->sql_type == SQL_TYPE_DATE ||
type_map->sql_type == SQL_TYPE_TIME ||
type_map->sql_type == SQL_TYPE_TIMESTAMP)
{
data[mypcSQL_DATA_TYPE] = SQL_DATETIME;
data[mypcSQL_DATETIME_SUB] = data[mypcDATA_TYPE];
}
else
{
data[mypcSQL_DATA_TYPE] = data[mypcDATA_TYPE];
data[mypcSQL_DATETIME_SUB] = nullptr;
}
if (is_char_sql_type(type_map->sql_type) || is_wchar_sql_type(type_map->sql_type) ||
is_binary_sql_type(type_map->sql_type))
{
data[mypcCHAR_OCTET_LENGTH] = data[mypcBUFFER_LENGTH];
}
else
{
data[mypcCHAR_OCTET_LENGTH] = nullptr;
}
data[mypcORDINAL_POSITION] = row[4];
data[mypcIS_NULLABLE] = "YES"; /* IS_NULLABLE */
++total_params_num;
data.next_row();
token = proc_param_next_token(token, param_str_end);
}
}
stmt->result_array = data.is_valid() ? (MYSQL_ROW)data.data() : nullptr;
create_fake_resultset(stmt, stmt->result_array, total_params_num,
SQLPROCEDURECOLUMNS_fields, SQLPROCEDURECOLUMNS_FIELDS, false);
}
catch(ODBCEXCEPTION &ex)
{
switch(ex.m_type)
{
case EXCEPTION_TYPE::EMPTY_SET:
nReturn = create_empty_fake_resultset(
(STMT*)hstmt, SQLPROCEDURECOLUMNS_values,
SQLPROCEDURECOLUMNS_fields,
SQLPROCEDURECOLUMNS_FIELDS);
break;
case EXCEPTION_TYPE::GENERAL:
break;
}
}
return nReturn;
}
/*
****************************************************************************
SQLStatistics
****************************************************************************
*/
char SS_type[10];
char *SQLSTAT_values[]={NullS,NullS,"","",NullS,"",SS_type,"","","","",NullS,NullS};
MYSQL_FIELD SQLSTAT_fields[]=
{
MYODBC_FIELD_NAME("TABLE_CAT", 0),
MYODBC_FIELD_NAME("TABLE_SCHEM", 0),
MYODBC_FIELD_NAME("TABLE_NAME", NOT_NULL_FLAG),
MYODBC_FIELD_SHORT("NON_UNIQUE", 0),
MYODBC_FIELD_NAME("INDEX_QUALIFIER", 0),
MYODBC_FIELD_NAME("INDEX_NAME", 0),
MYODBC_FIELD_SHORT("TYPE", NOT_NULL_FLAG),
MYODBC_FIELD_SHORT("ORDINAL_POSITION", 0),
MYODBC_FIELD_NAME("COLUMN_NAME", 0),
MYODBC_FIELD_STRING("ASC_OR_DESC", 1, 0),
MYODBC_FIELD_LONG("CARDINALITY", 0),
MYODBC_FIELD_LONG("PAGES", 0),
MYODBC_FIELD_STRING("FILTER_CONDITION", 10, 0),
};
const uint SQLSTAT_FIELDS = (uint)array_elements(SQLSTAT_fields);
/*
@purpose : retrieves a list of statistics about a single table and the
indexes associated with the table. The driver returns the
information as a result set.
*/
SQLRETURN
statistics_no_i_s(SQLHSTMT hstmt,
SQLCHAR *catalog, SQLSMALLINT catalog_len,
SQLCHAR *schema, SQLSMALLINT schema_len,
SQLCHAR *table, SQLSMALLINT table_len,
SQLUSMALLINT fUnique,
SQLUSMALLINT fAccuracy)
{
STMT *stmt= (STMT *)hstmt;
assert(stmt);
MYSQL *mysql= stmt->dbc->mysql;
DBC *dbc= stmt->dbc;
char *db_val = nullptr;
std::string db;
MYSQL_ROW mysql_row;
LOCK_DBC(stmt->dbc);
if (table_len)
{
db = get_database_name(stmt, catalog, catalog_len, schema, schema_len, false);
auto mysql_res = server_list_dbkeys(stmt, (SQLCHAR*)db.c_str(),
(SQLSMALLINT)db.length(),
table, table_len);
if (!mysql_res)
{
SQLRETURN rc= handle_connection_error(stmt);
return rc;
}
// Free if result data was not in row storage.
stmt->reset_result_array();
size_t rows = mysql_num_rows(mysql_res);
stmt->m_row_storage.set_size(rows, SQLSTAT_FIELDS);
auto &data = stmt->m_row_storage;
data.first_row();
size_t rnum = 0;
while ((mysql_row = mysql_fetch_row(mysql_res))) {
SQLSMALLINT non_unique = (mysql_row[1][0] == '1' ? 1 : 0);
// Skip non-unique indexes if only unique are requested
if (fUnique == SQL_INDEX_UNIQUE && non_unique)
continue;
CAT_SCHEMA_SET(data[0], data[1], db);
/* TABLE_NAME */
data[2] = mysql_row[0];
/* NON_UNIQUE */
data[3] = non_unique;
/* INDEX_QUALIFIER - not supported */
data[4] = nullptr;
/* INDEX_NAME */
data[5] = mysql_row[2];
/* TYPE */
SQLSMALLINT idx_type = SQL_INDEX_OTHER;
data[6] = idx_type;
/* ORDINAL_POSITION */
SQLSMALLINT ordinal_pos =
(SQLSMALLINT)std::strtol(mysql_row[3], nullptr, 10);
data[7] = ordinal_pos;
/* COLUMN_NAME */
data[8] = mysql_row[4];
/* ASC_OR_DESC */
if (mysql_row[5])
data[9] = mysql_row[5];
else
data[9] = nullptr;
/* CARDINALITY */
SQLINTEGER cardinality =
(SQLINTEGER)std::strtol(mysql_row[6], nullptr, 10);
data[10] = cardinality;
/* PAGES - not supported */
data[11] = nullptr;
/* FILTER_CONDITION - not supported */
data[12] = nullptr;
if (rnum < rows)
data.next_row();
++rnum;
}
if (mysql_res)
mysql_free_result(mysql_res);
if (rnum) {
stmt->result_array = (MYSQL_ROW)data.data();
create_fake_resultset(stmt, stmt->result_array, rnum,
SQLSTAT_fields, SQLSTAT_FIELDS, false);
return SQL_SUCCESS;
}
}
return create_empty_fake_resultset(stmt, SQLSTAT_values,
SQLSTAT_fields, SQLSTAT_FIELDS);
}
/*
****************************************************************************
SQLTables
****************************************************************************
*/
uint SQLTABLES_qualifier_order[]= {0};
const char *SQLTABLES_values[] = {"","",NULL,"TABLE","MySQL table"};
const char *SQLTABLES_qualifier_values[] = {"",NULL,NULL,NULL,NULL};
const char *SQLTABLES_owner_values[] = {NULL,"",NULL,NULL,NULL};
const char *SQLTABLES_type_values[] = {
NULL,NULL,NULL,"TABLE",NULL,
NULL,NULL,NULL,"SYSTEM TABLE",NULL,
NULL,NULL,NULL,"VIEW",NULL
};
MYSQL_FIELD SQLTABLES_fields[]=
{
MYODBC_FIELD_NAME("TABLE_CAT", 0),
MYODBC_FIELD_NAME("TABLE_SCHEM", 0),
MYODBC_FIELD_NAME("TABLE_NAME", 0),
MYODBC_FIELD_NAME("TABLE_TYPE", 0),
/*
Table remark length is 80 characters
*/
MYODBC_FIELD_STRING("REMARKS", 80, 0),
};
const uint SQLTABLES_FIELDS = (uint)array_elements(SQLTABLES_values);
SQLRETURN
tables_no_i_s(SQLHSTMT hstmt,
SQLCHAR *catalog, SQLSMALLINT catalog_len,
SQLCHAR *schema, SQLSMALLINT schema_len,
SQLCHAR *table, SQLSMALLINT table_len,
SQLCHAR *type, SQLSMALLINT type_len)
{
STMT *stmt= (STMT *)hstmt;
my_bool user_tables, views;
my_ulonglong row_count= 0;
MYSQL_ROW db_row = nullptr;
unsigned long count= 0;
SQLRETURN rc = SQL_SUCCESS;
try
{
ODBC_RESULTSET db_res;
assert(stmt);
LOCK_DBC(stmt->dbc);
stmt->result = nullptr;
bool all_dbs =
((catalog_len && !schema_len) ||
(schema_len && !catalog_len)) &&
!table_len && table && !type_len;
if (// Empty catalog, empty schema, empty table, type is "%"
!catalog_len && catalog && !schema_len && schema &&
!table_len && table && type && !strncmp((char *)type, "%", 2))
{
/* Return set of TableType qualifiers */
rc = create_fake_resultset(stmt, (MYSQL_ROW)SQLTABLES_type_values,
3, // Number of pre-set rows
SQLTABLES_fields, SQLTABLES_FIELDS,
true);
return rc;
}
/*
Empty (but non-NULL) schema and table returns catalog list.
Empty (but non-NULL) catalog and table returns schema list.
*/
if (all_dbs)
{
/* Determine the database */
std::string db = get_database_name(stmt, catalog, catalog_len,
schema, schema_len, false);
db_res = db_status(stmt, db);
if (!db_res)
{
return handle_connection_error(stmt);
}
}
else if (!catalog_len && catalog && // Empty catalog, schema and table
!schema_len && schema &&
!table_len && table &&
!type_len && type)
{
rc = create_fake_resultset(stmt, (MYSQL_ROW)SQLTABLES_owner_values,
1, SQLTABLES_fields, SQLTABLES_FIELDS,
true);
return rc;
}
/* any other use of catalog="" or schema="" returns an empty result */
if ((catalog || schema) && !catalog_len && !schema)
{
throw ODBCEXCEPTION(EXCEPTION_TYPE::EMPTY_SET);
}
user_tables = check_table_type(type, "TABLE", 5);
views = check_table_type(type, "VIEW", 4);
/* If no types specified, we want tables and views. */
if (!user_tables && !views)
{
if (!type_len)
user_tables = views = 1;
}
/* Return empty set if unknown TableType */
if (type_len && !views && !user_tables)
{
throw ODBCEXCEPTION(EXCEPTION_TYPE::EMPTY_SET);
}
// Free if result data was not in row storage.
stmt->reset_result_array();
auto &data = stmt->m_row_storage;
/*
If database result set (catalog_res) was produced loop
through all database to fetch table list inside database
*/
while (true)
{
if (db_res)
stmt->result = db_res.release();
else
{
std::string db = db_row ? db_row[3] :
get_database_name(stmt, catalog, catalog_len,
schema, schema_len, false);
stmt->result = table_status(stmt, (SQLCHAR*)db.c_str(),
(SQLSMALLINT)db.length(),
table, table_len, TRUE,
user_tables, views);
}
if (!stmt->result && mysql_errno(stmt->dbc->mysql))
{
/* unknown DB will return empty set from SQLTables */
switch (mysql_errno(stmt->dbc->mysql))
{
case ER_BAD_DB_ERROR:
throw ODBCEXCEPTION(EXCEPTION_TYPE::EMPTY_SET);
default:
return handle_connection_error(stmt);
}
}
if (!stmt->result)
throw ODBCEXCEPTION(EXCEPTION_TYPE::EMPTY_SET);
/* assemble final result set */
{
MYSQL_ROW row;
std::string db= "";
row_count += stmt->result->row_count;
if (!stmt->result->row_count)
{
if (stmt->result)
{
mysql_free_result(stmt->result);
stmt->result = nullptr;
}
//if (is_info_schema)
break;
}
// We will use the ROW_STORAGE here
stmt->m_row_storage.set_size(row_count, SQLTABLES_FIELDS);
int name_index = 0;
while ((row= mysql_fetch_row(stmt->result)))
{
int type_index = 2;
int comment_index = 1;
int cat_index= 3;
my_bool view;
db = row[cat_index];
view= (myodbc_casecmp(row[type_index], "VIEW", 4) == 0);
if ((view && !views) || (!view && !user_tables))
{
--row_count;
continue;
}
// Table Catalog & Schema
CAT_SCHEMA_SET(data[0], data[1], db);
// Table Name
data[2] = row[name_index];
// Table Type
data[3] = row[type_index];
// Comment
data[4] = row[comment_index];
data.next_row();
}
}
/*
If i_s then loop only once to fetch database name,
as mysql_table_status_i_s handles SQL_ALL_CATALOGS (%)
functionality and all databases with tables are returned
in one result set and so no further loops are required.
*/
//if (is_info_schema)
break;
}
stmt->result_array = (MYSQL_ROW)data.data();
if (!row_count)
{
throw ODBCEXCEPTION(EXCEPTION_TYPE::EMPTY_SET);
}
}
catch(ODBCEXCEPTION &ex)
{
switch(ex.m_type)
{
case EXCEPTION_TYPE::EMPTY_SET:
return create_empty_fake_resultset(stmt, (MYSQL_ROW)SQLTABLES_values,
SQLTABLES_fields, SQLTABLES_FIELDS);
}
}
create_fake_resultset(stmt, stmt->result_array, row_count,
SQLTABLES_fields, SQLTABLES_FIELDS, false);
return SQL_SUCCESS;
}