SQLUSMALLINT fScope __attribute__()

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);
}