LCOV - code coverage report
Current view: top level - clients/odbc/driver - SQLStatistics.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 70 124 56.5 %
Date: 2024-11-14 20:04:02 Functions: 1 1 100.0 %

          Line data    Source code
       1             : /*
       2             :  * SPDX-License-Identifier: MPL-2.0
       3             :  *
       4             :  * This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       7             :  *
       8             :  * Copyright 2024 MonetDB Foundation;
       9             :  * Copyright August 2008 - 2023 MonetDB B.V.;
      10             :  * Copyright 1997 - July 2008 CWI.
      11             :  */
      12             : 
      13             : /*
      14             :  * This code was created by Peter Harvey (mostly during Christmas 98/99).
      15             :  * This code is LGPL. Please ensure that this message remains in future
      16             :  * distributions and uses of this code (that's about all I get out of it).
      17             :  * - Peter Harvey pharvey@codebydesign.com
      18             :  *
      19             :  * This file has been modified for the MonetDB project.  See the file
      20             :  * Copyright in this directory for more information.
      21             :  */
      22             : 
      23             : /**********************************************************************
      24             :  * SQLStatistics()
      25             :  * CLI Compliance: ISO 92
      26             :  *
      27             :  * Author: Martin van Dinther, Sjoerd Mullender
      28             :  * Date  : 30 aug 2002
      29             :  *
      30             :  **********************************************************************/
      31             : 
      32             : #include "ODBCGlobal.h"
      33             : #include "ODBCStmt.h"
      34             : #include "ODBCUtil.h"
      35             : 
      36             : 
      37             : #ifdef ODBCDEBUG
      38             : static char *
      39          14 : translateUnique(SQLUSMALLINT Unique)
      40             : {
      41          14 :         switch (Unique) {
      42             :         case SQL_INDEX_ALL:
      43             :                 return "SQL_INDEX_ALL";
      44           7 :         case SQL_INDEX_UNIQUE:
      45           7 :                 return "SQL_INDEX_UNIQUE";
      46           0 :         default:
      47           0 :                 return "unknown";
      48             :         }
      49             : }
      50             : 
      51             : static char *
      52          14 : translateReserved(SQLUSMALLINT Reserved)
      53             : {
      54          14 :         switch (Reserved) {
      55             :         case SQL_ENSURE:
      56             :                 return "SQL_ENSURE";
      57           7 :         case SQL_QUICK:
      58           7 :                 return "SQL_QUICK";
      59           0 :         default:
      60           0 :                 return "unknown";
      61             :         }
      62             : }
      63             : #endif
      64             : 
      65             : static SQLRETURN
      66          14 : MNDBStatistics(ODBCStmt *stmt,
      67             :                const SQLCHAR *CatalogName,
      68             :                SQLSMALLINT NameLength1,
      69             :                const SQLCHAR *SchemaName,
      70             :                SQLSMALLINT NameLength2,
      71             :                const SQLCHAR *TableName,
      72             :                SQLSMALLINT NameLength3,
      73             :                SQLUSMALLINT Unique,
      74             :                SQLUSMALLINT Reserved)
      75             : {
      76          14 :         RETCODE rc;
      77             : 
      78             :         /* buffer for the constructed query to do meta data retrieval */
      79          14 :         char *query = NULL;
      80          14 :         size_t querylen;
      81          14 :         size_t pos = 0;
      82          14 :         char *sch = NULL, *tab = NULL;
      83          14 :         bool addTmpQuery = false;
      84             : 
      85          14 :         fixODBCstring(TableName, NameLength3, SQLSMALLINT,
      86             :                       addStmtError, stmt, return SQL_ERROR);
      87          14 :         fixODBCstring(SchemaName, NameLength2, SQLSMALLINT,
      88             :                       addStmtError, stmt, return SQL_ERROR);
      89          14 :         fixODBCstring(CatalogName, NameLength1, SQLSMALLINT,
      90             :                       addStmtError, stmt, return SQL_ERROR);
      91             : 
      92             : #ifdef ODBCDEBUG
      93          28 :         ODBCLOG("\"%.*s\" \"%.*s\" \"%.*s\" %s %s\n",
      94             :                 (int) NameLength1, CatalogName ? (char *) CatalogName : "",
      95             :                 (int) NameLength2, SchemaName ? (char *) SchemaName : "",
      96             :                 (int) NameLength3, TableName ? (char *) TableName : "",
      97             :                 translateUnique(Unique), translateReserved(Reserved));
      98             : #endif
      99             : 
     100             :         /* check for valid Unique argument */
     101          14 :         switch (Unique) {
     102             :         case SQL_INDEX_ALL:
     103             :         case SQL_INDEX_UNIQUE:
     104          14 :                 break;
     105           0 :         default:
     106             :                 /* Uniqueness option type out of range */
     107           0 :                 addStmtError(stmt, "HY100", NULL, 0);
     108           0 :                 return SQL_ERROR;
     109             :         }
     110             : 
     111             :         /* check for valid Reserved argument */
     112          14 :         switch (Reserved) {
     113             :         case SQL_ENSURE:
     114             :         case SQL_QUICK:
     115          14 :                 break;
     116           0 :         default:
     117             :                 /* Accuracy option type out of range */
     118           0 :                 addStmtError(stmt, "HY101", NULL, 0);
     119           0 :                 return SQL_ERROR;
     120             :         }
     121             : 
     122             :         /* check if a valid (non null, not empty) table name is supplied */
     123          14 :         if (TableName == NULL) {
     124             :                 /* Invalid use of null pointer */
     125           0 :                 addStmtError(stmt, "HY009", NULL, 0);
     126           0 :                 return SQL_ERROR;
     127             :         }
     128          14 :         if (NameLength3 == 0) {
     129             :                 /* Invalid string or buffer length */
     130           0 :                 addStmtError(stmt, "HY090", NULL, 0);
     131           0 :                 return SQL_ERROR;
     132             :         }
     133             : 
     134          14 :         if (stmt->Dbc->sql_attr_metadata_id == SQL_FALSE) {
     135          14 :                 if (NameLength2 > 0) {
     136          14 :                         sch = ODBCParseOA("s", "name",
     137             :                                           (const char *) SchemaName,
     138             :                                           (size_t) NameLength2);
     139          14 :                         if (sch == NULL)
     140           0 :                                 goto nomem;
     141             :                 }
     142          14 :                 if (NameLength3 > 0) {
     143          14 :                         tab = ODBCParseOA("t", "name",
     144             :                                           (const char *) TableName,
     145             :                                           (size_t) NameLength3);
     146          14 :                         if (tab == NULL)
     147           0 :                                 goto nomem;
     148             :                 }
     149             :         } else {
     150           0 :                 if (NameLength2 > 0) {
     151           0 :                         sch = ODBCParseID("s", "name",
     152             :                                           (const char *) SchemaName,
     153             :                                           (size_t) NameLength2);
     154           0 :                         if (sch == NULL)
     155           0 :                                 goto nomem;
     156             :                 }
     157           0 :                 if (NameLength3 > 0) {
     158           0 :                         tab = ODBCParseID("t", "name",
     159             :                                           (const char *) TableName,
     160             :                                           (size_t) NameLength3);
     161           0 :                         if (tab == NULL)
     162           0 :                                 goto nomem;
     163             :                 }
     164             :         }
     165             : 
     166             :         /* determine if we need to add a query against the tmp.* tables */
     167          28 :         addTmpQuery = (SchemaName == NULL)
     168          14 :                    || (SchemaName != NULL
     169          14 :                         && (strcmp((const char *) SchemaName, "tmp") == 0
     170           6 :                          || strchr((const char *) SchemaName, '%') != NULL
     171           6 :                          || strchr((const char *) SchemaName, '_') != NULL));
     172             : 
     173             :         /* construct the query */
     174          14 :         querylen = 1200 + (sch ? strlen(sch) : 0) + (tab ? strlen(tab) : 0);
     175          14 :         if (addTmpQuery)
     176           8 :                 querylen *= 2;
     177          14 :         query = malloc(querylen);
     178          14 :         if (query == NULL)
     179           0 :                 goto nomem;
     180             : 
     181             :         /* SQLStatistics returns a table with the following columns:
     182             :            VARCHAR      TABLE_CAT
     183             :            VARCHAR      TABLE_SCHEM
     184             :            VARCHAR      TABLE_NAME NOT NULL
     185             :            SMALLINT     NON_UNIQUE
     186             :            VARCHAR      INDEX_QUALIFIER
     187             :            VARCHAR      INDEX_NAME
     188             :            SMALLINT     TYPE NOT NULL
     189             :            SMALLINT     ORDINAL_POSITION
     190             :            VARCHAR      COLUMN_NAME
     191             :            CHAR(1)      ASC_OR_DESC
     192             :            INTEGER      CARDINALITY
     193             :            INTEGER      PAGES
     194             :            VARCHAR      FILTER_CONDITION
     195             :          */
     196          14 :         pos += snprintf(query + pos, querylen - pos,
     197             :                 "select cast(null as varchar(1)) as \"TABLE_CAT\", "
     198             :                        "s.name as \"TABLE_SCHEM\", "
     199             :                        "t.name as \"TABLE_NAME\", "
     200             :                        "cast(sys.ifthenelse(k.name is null,1,0) as smallint) as \"NON_UNIQUE\", "
     201             :                        "cast(null as varchar(1)) as \"INDEX_QUALIFIER\", "
     202             :                        "i.name as \"INDEX_NAME\", "
     203             :                        "cast(sys.ifthenelse(i.type = 0, %d, %d) as smallint) as \"TYPE\", "
     204             :                        "cast(kc.nr + 1 as smallint) as \"ORDINAL_POSITION\", "
     205             :                        "c.name as \"COLUMN_NAME\", "
     206             :                        "cast(null as char(1)) as \"ASC_OR_DESC\", "
     207             :                        "cast(sys.ifthenelse(k.name is null,NULL,st.count) as integer) as \"CARDINALITY\", "
     208             :                        "cast(null as integer) as \"PAGES\", "
     209             :                        "cast(null as varchar(1)) as \"FILTER_CONDITION\" "
     210             :                 "from sys.idxs i "
     211             :                 "join sys._tables t on i.table_id = t.id "
     212             :                 "join sys.schemas s on t.schema_id = s.id "
     213             :                 "join sys.objects kc on i.id = kc.id "
     214             :                 "join sys._columns c on (t.id = c.table_id and kc.name = c.name) "
     215             :                 "%sjoin sys.keys k on (k.name = i.name and i.table_id = k.table_id and k.type in (0, 1, 3)) "
     216             :                 "join sys.storage() st on (st.schema = s.name and st.table = t.name and st.column = c.name) "
     217             :                 "where 1=1",
     218             :                 SQL_INDEX_HASHED, SQL_INDEX_OTHER,
     219             :                 (Unique == SQL_INDEX_UNIQUE) ? "" : "left outer ");
     220             :                 /* by using left outer join we also get indices for tables
     221             :                    which have no primary key or unique constraints, so no rows in sys.keys */
     222          14 :         assert(pos < 1000);
     223             : 
     224             :         /* Construct the selection condition query part */
     225          14 :         if (NameLength1 > 0 && CatalogName != NULL) {
     226             :                 /* filtering requested on catalog name */
     227           0 :                 if (strcmp((char *) CatalogName, msetting_string(stmt->Dbc->settings, MP_DATABASE)) != 0) {
     228             :                         /* catalog name does not match the database name, so return no rows */
     229           0 :                         pos += snprintf(query + pos, querylen - pos, " and 1=2");
     230             :                 }
     231             :         }
     232          14 :         if (sch) {
     233             :                 /* filtering requested on schema name */
     234          14 :                 pos += snprintf(query + pos, querylen - pos, " and %s", sch);
     235             :         }
     236          14 :         if (tab) {
     237             :                 /* filtering requested on table name */
     238          14 :                 pos += snprintf(query + pos, querylen - pos, " and %s", tab);
     239             :         }
     240             : 
     241          14 :         if (addTmpQuery) {
     242             :                 /* we must also include the indexes of local temporary tables
     243             :                    which are stored in tmp.idxs, tmp._tables, tmp._columns, tmp.objects and tmp.keys */
     244           8 :                 pos += snprintf(query + pos, querylen - pos,
     245             :                         " UNION ALL "
     246             :                         "select cast(null as varchar(1)) as \"TABLE_CAT\", "
     247             :                                "s.name as \"TABLE_SCHEM\", "
     248             :                                "t.name as \"TABLE_NAME\", "
     249             :                                "cast(sys.ifthenelse(k.name is null,1,0) as smallint) as \"NON_UNIQUE\", "
     250             :                                "cast(null as varchar(1)) as \"INDEX_QUALIFIER\", "
     251             :                                "i.name as \"INDEX_NAME\", "
     252             :                                "cast(sys.ifthenelse(i.type = 0, %d, %d) as smallint) as \"TYPE\", "
     253             :                                "cast(kc.nr + 1 as smallint) as \"ORDINAL_POSITION\", "
     254             :                                "c.name as \"COLUMN_NAME\", "
     255             :                                "cast(null as char(1)) as \"ASC_OR_DESC\", "
     256             :                                "cast(sys.ifthenelse(k.name is null,NULL,st.count) as integer) as \"CARDINALITY\", "
     257             :                                "cast(null as integer) as \"PAGES\", "
     258             :                                "cast(null as varchar(1)) as \"FILTER_CONDITION\" "
     259             :                         "from tmp.idxs i "
     260             :                         "join tmp._tables t on i.table_id = t.id "
     261             :                         "join sys.schemas s on t.schema_id = s.id "
     262             :                         "join tmp.objects kc on i.id = kc.id "
     263             :                         "join tmp._columns c on (t.id = c.table_id and kc.name = c.name) "
     264             :                         "%sjoin tmp.keys k on (k.name = i.name and i.table_id = k.table_id and k.type in (0, 1, 3))"
     265             :                         "left outer join sys.storage() st on (st.schema = s.name and st.table = t.name and st.column = c.name) "
     266             :                         "where 1=1",
     267             :                         SQL_INDEX_HASHED, SQL_INDEX_OTHER,
     268             :                         (Unique == SQL_INDEX_UNIQUE) ? "" : "left outer ");
     269             : 
     270             :                 /* Construct the selection condition query part */
     271           8 :                 if (NameLength1 > 0 && CatalogName != NULL) {
     272             :                         /* filtering requested on catalog name */
     273           0 :                         if (strcmp((char *) CatalogName, msetting_string(stmt->Dbc->settings, MP_DATABASE)) != 0) {
     274             :                                 /* catalog name does not match the database name, so return no rows */
     275           0 :                                 pos += snprintf(query + pos, querylen - pos, " and 1=2");
     276             :                         }
     277             :                 }
     278           8 :                 if (sch) {
     279             :                         /* filtering requested on schema name */
     280           8 :                         pos += snprintf(query + pos, querylen - pos, " and %s", sch);
     281             :                 }
     282           8 :                 if (tab) {
     283             :                         /* filtering requested on table name */
     284           8 :                         pos += snprintf(query + pos, querylen - pos, " and %s", tab);
     285             :                 }
     286             :         }
     287             : 
     288          14 :         if (sch)
     289          14 :                 free(sch);
     290          14 :         if (tab)
     291          14 :                 free(tab);
     292             : 
     293             :         /* add the ordering */
     294          14 :         pos += strcpy_len(query + pos, " order by \"NON_UNIQUE\", \"TYPE\", \"INDEX_QUALIFIER\", \"INDEX_NAME\", \"ORDINAL_POSITION\"", querylen - pos);
     295          14 :         assert(pos < querylen);
     296             : 
     297             :         /* debug: fprintf(stdout, "SQLStatistics query (pos: %zu, len: %zu):\n%s\n\n", pos, strlen(query), query); */
     298             : 
     299             :         /* query the MonetDB data dictionary tables */
     300          14 :         rc = MNDBExecDirect(stmt, (SQLCHAR *) query, (SQLINTEGER) pos);
     301             : 
     302          14 :         free(query);
     303             : 
     304          14 :         return rc;
     305             : 
     306           0 :   nomem:
     307             :         /* note that query must be NULL when we get here */
     308           0 :         if (sch)
     309           0 :                 free(sch);
     310           0 :         if (tab)
     311           0 :                 free(tab);
     312             :         /* Memory allocation error */
     313           0 :         addStmtError(stmt, "HY001", NULL, 0);
     314           0 :         return SQL_ERROR;
     315             : }
     316             : 
     317             : SQLRETURN SQL_API
     318             : SQLStatistics(SQLHSTMT StatementHandle,
     319             :               SQLCHAR *CatalogName,
     320             :               SQLSMALLINT NameLength1,
     321             :               SQLCHAR *SchemaName,
     322             :               SQLSMALLINT NameLength2,
     323             :               SQLCHAR *TableName,
     324             :               SQLSMALLINT NameLength3,
     325             :               SQLUSMALLINT Unique,
     326             :               SQLUSMALLINT Reserved)
     327             : {
     328          14 :         ODBCStmt *stmt = (ODBCStmt *) StatementHandle;
     329             : 
     330             : #ifdef ODBCDEBUG
     331          14 :         ODBCLOG("SQLStatistics %p ", StatementHandle);
     332             : #endif
     333             : 
     334          14 :         if (!isValidStmt(stmt))
     335             :                  return SQL_INVALID_HANDLE;
     336             : 
     337          14 :         clearStmtErrors(stmt);
     338             : 
     339          14 :         return MNDBStatistics(stmt,
     340             :                               CatalogName, NameLength1,
     341             :                               SchemaName, NameLength2,
     342             :                               TableName, NameLength3,
     343             :                               Unique,
     344             :                               Reserved);
     345             : }
     346             : 
     347             : SQLRETURN SQL_API
     348             : SQLStatisticsA(SQLHSTMT StatementHandle,
     349             :                SQLCHAR *CatalogName,
     350             :                SQLSMALLINT NameLength1,
     351             :                SQLCHAR *SchemaName,
     352             :                SQLSMALLINT NameLength2,
     353             :                SQLCHAR *TableName,
     354             :                SQLSMALLINT NameLength3,
     355             :                SQLUSMALLINT Unique,
     356             :                SQLUSMALLINT Reserved)
     357             : {
     358           0 :         return SQLStatistics(StatementHandle,
     359             :                              CatalogName, NameLength1,
     360             :                              SchemaName, NameLength2,
     361             :                              TableName, NameLength3,
     362             :                              Unique,
     363             :                              Reserved);
     364             : }
     365             : 
     366             : SQLRETURN SQL_API
     367             : SQLStatisticsW(SQLHSTMT StatementHandle,
     368             :                SQLWCHAR *CatalogName,
     369             :                SQLSMALLINT NameLength1,
     370             :                SQLWCHAR *SchemaName,
     371             :                SQLSMALLINT NameLength2,
     372             :                SQLWCHAR *TableName,
     373             :                SQLSMALLINT NameLength3,
     374             :                SQLUSMALLINT Unique,
     375             :                SQLUSMALLINT Reserved)
     376             : {
     377           0 :         ODBCStmt *stmt = (ODBCStmt *) StatementHandle;
     378           0 :         SQLRETURN rc = SQL_ERROR;
     379           0 :         SQLCHAR *catalog = NULL, *schema = NULL, *table = NULL;
     380             : 
     381             : #ifdef ODBCDEBUG
     382           0 :         ODBCLOG("SQLStatisticsW %p ", StatementHandle);
     383             : #endif
     384             : 
     385           0 :         if (!isValidStmt(stmt))
     386             :                  return SQL_INVALID_HANDLE;
     387             : 
     388           0 :         clearStmtErrors(stmt);
     389             : 
     390           0 :         fixWcharIn(CatalogName, NameLength1, SQLCHAR, catalog,
     391             :                    addStmtError, stmt, goto bailout);
     392           0 :         fixWcharIn(SchemaName, NameLength2, SQLCHAR, schema,
     393             :                    addStmtError, stmt, goto bailout);
     394           0 :         fixWcharIn(TableName, NameLength3, SQLCHAR, table,
     395             :                    addStmtError, stmt, goto bailout);
     396             : 
     397           0 :         rc = MNDBStatistics(stmt,
     398             :                             catalog, SQL_NTS,
     399             :                             schema, SQL_NTS,
     400             :                             table, SQL_NTS,
     401             :                             Unique,
     402             :                             Reserved);
     403             : 
     404           0 :       bailout:
     405           0 :         if (catalog)
     406           0 :                 free(catalog);
     407           0 :         if (schema)
     408           0 :                 free(schema);
     409           0 :         if (table)
     410           0 :                 free(table);
     411             : 
     412             :         return rc;
     413             : }

Generated by: LCOV version 1.14