in driver/catalog.cc [1304:1497]
SQLUSMALLINT fScope __attribute__((unused)),
SQLUSMALLINT fNullable __attribute__((unused)))
{
STMT *stmt = (STMT*)hstmt;
tempBuf temp(1024);
/* Reset the statement in order to avoid memory leaks when working with ADODB */
my_SQLFreeStmt(hstmt, FREE_STMT_RESET);
// The catalog query result set will have two extra columns for internal
// use that are not reported to the user in the function result.
ODBC_CATALOG ocat(stmt, SQLSPECIALCOLUMNS_FIELDS,
"information_schema.COLUMNS c",
catalog, catalog_len, schema, schema_len, table, table_len);
ocat.add_column("COLUMN_NAME");
ocat.add_column("DATA_TYPE");
ocat.add_column("COLUMN_TYPE as TYPE_NAME");
ocat.add_column(
"IF(ISNULL(CHARACTER_MAXIMUM_LENGTH),"
" CASE c.DATA_TYPE"
" WHEN 'bit' THEN CAST((NUMERIC_PRECISION + 7) / 8 AS UNSIGNED)"
" WHEN 'json' THEN 1073741823"
" ELSE NUMERIC_PRECISION"
" END, CHARACTER_MAXIMUM_LENGTH) as "
"COLUMN_SIZE");
ocat.add_column(
"CASE DATA_TYPE"
" WHEN 'json' THEN 4294967295"
" WHEN 'decimal' THEN NUMERIC_PRECISION + NUMERIC_SCALE + IF(COLUMN_TYPE LIKE '%unsigned', 1, 2) "
" ELSE CHARACTER_OCTET_LENGTH"
" END as "
"BUFFER_LENGTH");
ocat.add_column("NUMERIC_SCALE as DECIMAL_DIGITS");
ocat.add_column(
" CASE COLUMN_KEY "
" WHEN 'PRI' THEN 1"
" WHEN 'UNI' THEN 2"
" ELSE 0"
" END as KEY_INFO");
ocat.add_column(
"IF (EXTRA LIKE '%on update CURRENT_TIMESTAMP%', 1, 0) as INTERNAL_1");
ocat.set_join("LEFT JOIN information_schema.CHARACTER_SETS cs ON c.CHARACTER_SET_NAME = cs.CHARACTER_SET_NAME ");
ocat.set_where("(COLUMN_KEY <> '' OR EXTRA LIKE '%on update CURRENT_TIMESTAMP%')");
std::string db = get_database_name(stmt, catalog, catalog_len,
schema, schema_len, false);
try
{
ocat.execute();
}
catch (const MYERROR &e)
{
return e.retcode;
}
size_t rows = ocat.num_rows();
// We will use the ROW_STORAGE here
stmt->m_row_storage.set_size(rows, SQLSPECIALCOLUMNS_FIELDS);
////////////////////////////////////////////////////////////////////////
auto lambda_fill_data = [&stmt, &ocat, rows](SQLSMALLINT colType)
{
auto &data = stmt->m_row_storage;
data.first_row();
size_t rnum = 1;
bool pk_found = false;
MYSQL_ROW mysql_row = nullptr;
if(colType == SQL_BEST_ROWID)
{
// Determine if primary key info is present in the resultset.
while(mysql_row = ocat.fetch_row())
{
if (mysql_row[6][0] == '1')
pk_found = true;
}
}
// Reset the seek position to the beginning of resultset.
ocat.data_seek(0);
while(mysql_row = ocat.fetch_row())
{
// Lengths are stored internally as well, they will be needed
// to check for NULL values.
ocat.get_lengths();
if(colType == SQL_ROWVER)
{
if (strcmp(mysql_row[1], "timestamp"))
continue;
// Column 8: '1' if 'on update CURRENT_TIMESTAMP'
if (mysql_row[7][0] != '1')
continue;
/* SCOPE */
data[0] = nullptr;
}
else
{
/*
* The optimal set of columns for identifing a row is either
* the primary key, or if there is no primary key, then
* all the fields (if SQLSPECIALCOLUMNS_RETURN_ALL_COLUMNS is set).
*/
// Column 7: '1' for Primary Key
if (pk_found && mysql_row[6][0] != '1')
continue;
#ifndef SQLSPECIALCOLUMNS_RETURN_ALL_COLUMNS
/* Returning all columns when there is no primary key is not always
* correct because it is possible that even all columns do not identify
* rows of the table uniquely.
* If `SQLSPECIALCOLUMNS_RETURN_ALL_COLUMNS` is not defined then we
* return empty set of columns in absence of a primary key
*/
if (!pk_found)
continue;
#endif
/* SCOPE */
data[0] = SQL_SCOPE_SESSION;
}
/* COLUMN_NAME */
data[1] = mysql_row[0];
/* DATA_TYPE */
SQLSMALLINT odbc_sql_type =
get_sql_data_type_from_str(mysql_row[1]);
data[2] = odbc_sql_type;
/* TYPE_NAME */
const char *type_name = mysql_row[2];
data[3] = type_name;
size_t col_size = ocat.is_null_value(3) ? 0 :
get_column_size_from_str(stmt, mysql_row[3]);
/* COLUMN_SIZE */
#if _WIN32 && !_WIN64
#define COL_SIZE_VAL (int)col_size
#else
#define COL_SIZE_VAL col_size
#endif
if (ocat.is_null_value(3))
data[4] = nullptr;
else
data[4] = COL_SIZE_VAL;
/* BUFFER_LENGTH */
data[5] = get_buffer_length(type_name, mysql_row[3],
mysql_row[4], odbc_sql_type, col_size, ocat.is_null_value(4));
/* DECIMAL_DIGITS */
data[6] = (char *)(ocat.is_null_value(5) ? nullptr : mysql_row[5]);
data[7] = SQL_PC_NOT_PSEUDO;
if(rnum < rows)
data.next_row();
++rnum;
}
if (rnum > 1)
{
stmt->result_array = (MYSQL_ROW)data.data();
create_fake_resultset(stmt, stmt->result_array, rnum - 1,
SQLSPECIALCOLUMNS_fields, SQLSPECIALCOLUMNS_FIELDS, false);
}
else
{
create_empty_fake_resultset(stmt, SQLSPECIALCOLUMNS_values,
SQLSPECIALCOLUMNS_fields, SQLSPECIALCOLUMNS_FIELDS);
}
return SQL_SUCCESS;
};
if (fColType != SQL_ROWVER && fColType != SQL_BEST_ROWID)
{
return stmt->set_error( MYERR_S1000,
"Unsupported argument to SQLSpecialColumns", 4000);
}
return lambda_fill_data(fColType);
}