LCOV - code coverage report
Current view: top level - clients/odbc/driver - SQLProcedureColumns.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 50 99 50.5 %
Date: 2024-12-19 20:05:57 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             :  * SQLProcedureColumns()
      25             :  * CLI Compliance: ODBC (Microsoft)
      26             :  *
      27             :  * Author: Sjoerd Mullender
      28             :  * Date  : 28 Feb 2018
      29             :  *
      30             :  **********************************************************************/
      31             : 
      32             : #include "ODBCGlobal.h"
      33             : #include "ODBCStmt.h"
      34             : #include "ODBCUtil.h"
      35             : #include "ODBCQueries.h"
      36             : 
      37             : 
      38             : static SQLRETURN
      39           4 : MNDBProcedureColumns(ODBCStmt *stmt,
      40             :                      const SQLCHAR *CatalogName,
      41             :                      SQLSMALLINT NameLength1,
      42             :                      const SQLCHAR *SchemaName,
      43             :                      SQLSMALLINT NameLength2,
      44             :                      const SQLCHAR *ProcName,
      45             :                      SQLSMALLINT NameLength3,
      46             :                      const SQLCHAR *ColumnName,
      47             :                      SQLSMALLINT NameLength4)
      48             : {
      49           4 :         RETCODE rc;
      50             : 
      51             :         /* buffer for the constructed query to do meta data retrieval */
      52           4 :         char *query = NULL;
      53           4 :         size_t querylen;
      54           4 :         size_t pos = 0;
      55           4 :         char *sch = NULL, *prc = NULL, *col = NULL;
      56             : 
      57           4 :         fixODBCstring(CatalogName, NameLength1, SQLSMALLINT,
      58             :                       addStmtError, stmt, return SQL_ERROR);
      59           4 :         fixODBCstring(SchemaName, NameLength2, SQLSMALLINT,
      60             :                       addStmtError, stmt, return SQL_ERROR);
      61           4 :         fixODBCstring(ProcName, NameLength3, SQLSMALLINT,
      62             :                       addStmtError, stmt, return SQL_ERROR);
      63           4 :         fixODBCstring(ColumnName, NameLength4, SQLSMALLINT,
      64             :                       addStmtError, stmt, return SQL_ERROR);
      65             : 
      66             : #ifdef ODBCDEBUG
      67           4 :         ODBCLOG("\"%.*s\" \"%.*s\" \"%.*s\" \"%.*s\"\n",
      68             :                 (int) NameLength1, CatalogName ? (char *) CatalogName : "",
      69             :                 (int) NameLength2, SchemaName ? (char *) SchemaName : "",
      70             :                 (int) NameLength3, ProcName ? (char *) ProcName : "",
      71             :                 (int) NameLength4, ColumnName ? (char *) ColumnName : "");
      72             : #endif
      73             : 
      74           4 :         if (stmt->Dbc->sql_attr_metadata_id == SQL_FALSE) {
      75           4 :                 if (NameLength2 > 0) {
      76           4 :                         sch = ODBCParsePV("s", "name",
      77             :                                           (const char *) SchemaName,
      78             :                                           (size_t) NameLength2,
      79             :                                           stmt->Dbc);
      80           4 :                         if (sch == NULL)
      81           0 :                                 goto nomem;
      82             :                 }
      83           4 :                 if (NameLength3 > 0) {
      84           8 :                         prc = ODBCParsePV("p", "name",
      85             :                                           (const char *) ProcName,
      86             :                                           (size_t) NameLength3,
      87           4 :                                           stmt->Dbc);
      88           4 :                         if (prc == NULL)
      89           0 :                                 goto nomem;
      90             :                 }
      91           4 :                 if (NameLength4 > 0) {
      92           8 :                         col = ODBCParsePV("a", "name",
      93             :                                           (const char *) ColumnName,
      94             :                                           (size_t) NameLength4,
      95           4 :                                           stmt->Dbc);
      96           4 :                         if (col == NULL)
      97           0 :                                 goto nomem;
      98             :                 }
      99             :         } else {
     100           0 :                 if (NameLength2 > 0) {
     101           0 :                         sch = ODBCParseID("s", "name",
     102             :                                           (const char *) SchemaName,
     103             :                                           (size_t) NameLength2);
     104           0 :                         if (sch == NULL)
     105           0 :                                 goto nomem;
     106             :                 }
     107           0 :                 if (NameLength3 > 0) {
     108           0 :                         prc = ODBCParseID("p", "name",
     109             :                                           (const char *) ProcName,
     110             :                                           (size_t) NameLength3);
     111           0 :                         if (prc == NULL)
     112           0 :                                 goto nomem;
     113             :                 }
     114           0 :                 if (NameLength4 > 0) {
     115           0 :                         col = ODBCParseID("a", "name",
     116             :                                           (const char *) ColumnName,
     117             :                                           (size_t) NameLength4);
     118           0 :                         if (col == NULL)
     119           0 :                                 goto nomem;
     120             :                 }
     121             :         }
     122             : 
     123             :         /* construct the query now */
     124           4 :         querylen = 6700 + (sch ? strlen(sch) : 0) + (prc ? strlen(prc) : 0) +
     125           4 :                 (col ? strlen(col) : 0);
     126           4 :         query = malloc(querylen);
     127           4 :         if (query == NULL)
     128           0 :                 goto nomem;
     129             : 
     130             :         /* SQLProcedureColumns returns a table with the following columns:
     131             :            VARCHAR      PROCEDURE_CAT
     132             :            VARCHAR      PROCEDURE_SCHEM
     133             :            VARCHAR      PROCEDURE_NAME NOT NULL
     134             :            VARCHAR      COLUMN_NAME NOT NULL
     135             :            SMALLINT     COLUMN_TYPE NOT NULL
     136             :            SMALLINT     DATA_TYPE NOT NULL
     137             :            VARCHAR      TYPE_NAME NOT NULL
     138             :            INTEGER      COLUMN_SIZE
     139             :            INTEGER      BUFFER_LENGTH
     140             :            SMALLINT     DECIMAL_DIGITS
     141             :            SMALLINT     NUM_PREC_RADIX
     142             :            SMALLINT     NULLABLE NOT NULL
     143             :            VARCHAR      REMARKS
     144             :            VARCHAR      COLUMN_DEF
     145             :            SMALLINT     SQL_DATA_TYPE NOT NULL
     146             :            SMALLINT     SQL_DATETIME_SUB
     147             :            INTEGER      CHAR_OCTET_LENGTH
     148             :            INTEGER      ORDINAL_POSITION NOT NULL
     149             :            VARCHAR      IS_NULLABLE
     150             :            VARCHAR      SPECIFIC_NAME   (Note this is a MonetDB extension, needed to differentiate between overloaded procedure/function names)
     151             :                                         (similar to JDBC DatabaseMetaData methods getProcedureColumns() and getFunctionColumns())
     152             :          */
     153             : 
     154             : /* see sql/include/sql_catalog.h */
     155             : #define F_FUNC 1
     156             : #define F_PROC 2
     157             : #define F_UNION 5
     158           4 :         pos += snprintf(query + pos, querylen - pos,
     159             :                 "select cast(null as varchar(1)) as \"PROCEDURE_CAT\", "
     160             :                        "s.name as \"PROCEDURE_SCHEM\", "
     161             :                        "p.name as \"PROCEDURE_NAME\", "
     162             :                        "a.name as \"COLUMN_NAME\", "
     163             :                        "cast(case when a.inout = 1 then %d "
     164             :                             "when p.type = %d then %d "
     165             :                             "else %d "
     166             :                        "end as smallint) as \"COLUMN_TYPE\", "
     167             :                 DATA_TYPE(a) ", "
     168             :                 TYPE_NAME(a) ", "
     169             :                 COLUMN_SIZE(a) ", "
     170             :                 BUFFER_LENGTH(a) ", "
     171             :                 DECIMAL_DIGITS(a) ", "
     172             :                 NUM_PREC_RADIX(a) ", "
     173             :                        "cast(%d as smallint) as \"NULLABLE\", "
     174             :                        "%s as \"REMARKS\", "
     175             :                        "cast(null as varchar(1)) as \"COLUMN_DEF\", "
     176             :                 SQL_DATA_TYPE(a) ", "
     177             :                 SQL_DATETIME_SUB(a) ", "
     178             :                 CHAR_OCTET_LENGTH(a) ", "
     179             :                        "cast(case when p.type = 5 and a.inout = 0 then a.number + 1 "
     180             :                             "when p.type = 5 and a.inout = 1 then a.number - x.maxout "
     181             :                             "when p.type = 2 and a.inout = 1 then a.number + 1 "
     182             :                             "when a.inout = 0 then 0 "
     183             :                             "else a.number "
     184             :                        "end as integer) as \"ORDINAL_POSITION\", "
     185             :                        "'' as \"IS_NULLABLE\", "
     186             :                         /* Only the id value uniquely identifies a specific procedure.
     187             :                            Include it to be able to differentiate between multiple
     188             :                            overloaded procedures with the same name and schema */
     189             :                         "cast(p.id as varchar(10)) AS \"SPECIFIC_NAME\" "
     190             :                 "from sys.schemas s, "
     191             :                      "sys.functions p left outer join (select func_id, max(number) as maxout from sys.args where inout = 0 group by func_id) as x on p.id = x.func_id, "
     192             :                      "sys.args a%s "
     193             :                 "where s.id = p.schema_id and "
     194             :                       "p.id = a.func_id and "
     195             :                       "p.type in (%d, %d, %d)",
     196             :                 /* column_type: */
     197             :                 SQL_PARAM_INPUT, F_UNION, SQL_RESULT_COL, SQL_RETURN_VALUE,
     198             : #ifdef DATA_TYPE_ARGS
     199             :                 DATA_TYPE_ARGS,
     200             : #endif
     201             : #ifdef TYPE_NAME_ARGS
     202             :                 TYPE_NAME_ARGS,
     203             : #endif
     204             : #ifdef COLUMN_SIZE_ARGS
     205             :                 COLUMN_SIZE_ARGS,
     206             : #endif
     207             : #ifdef BUFFER_LENGTH_ARGS
     208             :                 BUFFER_LENGTH_ARGS,
     209             : #endif
     210             : #ifdef DECIMAL_DIGITS_ARGS
     211             :                 DECIMAL_DIGITS_ARGS,
     212             : #endif
     213             : #ifdef NUM_PREC_RADIX_ARGS
     214             :                 NUM_PREC_RADIX_ARGS,
     215             : #endif
     216             :                 /* nullable: */
     217             :                 SQL_NULLABLE_UNKNOWN,
     218             :                 /* remarks: */
     219             :                 stmt->Dbc->has_comment ? "c.remark" : "cast(null as varchar(1))",
     220             : #ifdef SQL_DATA_TYPE_ARGS
     221             :                 SQL_DATA_TYPE_ARGS,
     222             : #endif
     223             : #ifdef SQL_DATETIME_SUB_ARGS
     224             :                 SQL_DATETIME_SUB_ARGS,
     225             : #endif
     226             : #ifdef CHAR_OCTET_LENGTH_ARGS
     227             :                 CHAR_OCTET_LENGTH_ARGS,
     228             : #endif
     229             :                 /* from clause: */
     230           4 :                 stmt->Dbc->has_comment ? " left outer join sys.comments c on c.id = a.id" : "",
     231             :                 /* where clause: */
     232             :                 F_FUNC, F_PROC, F_UNION);
     233             : 
     234             :         /* depending on the input parameter values we must add a
     235             :            variable selection condition dynamically */
     236             : 
     237             :         /* Construct the selection condition query part */
     238           4 :         if (NameLength1 > 0 && CatalogName != NULL) {
     239             :                 /* filtering requested on catalog name */
     240           0 :                 if (strcmp((char *) CatalogName, msetting_string(stmt->Dbc->settings, MP_DATABASE)) != 0) {
     241             :                         /* catalog name does not match the database name, so return no rows */
     242           0 :                         pos += snprintf(query + pos, querylen - pos, " and 1=2");
     243             :                 }
     244             :         }
     245           4 :         if (sch) {
     246             :                 /* filtering requested on schema name */
     247           4 :                 pos += snprintf(query + pos, querylen - pos, " and %s", sch);
     248           4 :                 free(sch);
     249             :         }
     250           4 :         if (prc) {
     251             :                 /* filtering requested on procedure name */
     252           4 :                 pos += snprintf(query + pos, querylen - pos, " and %s", prc);
     253           4 :                 free(prc);
     254             :         }
     255           4 :         if (col) {
     256             :                 /* filtering requested on column name */
     257           4 :                 pos += snprintf(query + pos, querylen - pos, " and %s", col);
     258           4 :                 free(col);
     259             :         }
     260             : 
     261             :         /* add the ordering (exclude procedure_cat as it is the same for all rows) */
     262           4 :         pos += strcpy_len(query + pos, " order by \"PROCEDURE_SCHEM\", \"PROCEDURE_NAME\", \"SPECIFIC_NAME\", \"COLUMN_TYPE\", \"ORDINAL_POSITION\"", querylen - pos);
     263           4 :         if (pos >= querylen)
     264           0 :                 fprintf(stderr, "pos >= querylen, %zu > %zu\n", pos, querylen);
     265           4 :         assert(pos < querylen);
     266             : 
     267             :         /* debug: fprintf(stdout, "SQLProcedureColumns query (pos: %zu, len: %zu):\n%s\n\n", pos, strlen(query), query); */
     268             : 
     269             :         /* query the MonetDB data dictionary tables */
     270           4 :         rc = MNDBExecDirect(stmt, (SQLCHAR *) query, (SQLINTEGER) pos);
     271             : 
     272           4 :         free(query);
     273             : 
     274           4 :         return rc;
     275             : 
     276           0 :   nomem:
     277             :         /* note that query must be NULL when we get here */
     278           0 :         if (sch)
     279           0 :                 free(sch);
     280           0 :         if (prc)
     281           0 :                 free(prc);
     282           0 :         if (col)
     283           0 :                 free(col);
     284             :         /* Memory allocation error */
     285           0 :         addStmtError(stmt, "HY001", NULL, 0);
     286           0 :         return SQL_ERROR;
     287             : }
     288             : 
     289             : SQLRETURN SQL_API
     290             : SQLProcedureColumns(SQLHSTMT StatementHandle,
     291             :                     SQLCHAR *CatalogName,
     292             :                     SQLSMALLINT NameLength1,
     293             :                     SQLCHAR *SchemaName,
     294             :                     SQLSMALLINT NameLength2,
     295             :                     SQLCHAR *ProcName,
     296             :                     SQLSMALLINT NameLength3,
     297             :                     SQLCHAR *ColumnName,
     298             :                     SQLSMALLINT NameLength4)
     299             : {
     300           4 :         ODBCStmt *stmt = (ODBCStmt *) StatementHandle;
     301             : 
     302             : #ifdef ODBCDEBUG
     303           4 :         ODBCLOG("SQLProcedureColumns %p ", StatementHandle);
     304             : #endif
     305             : 
     306           4 :         if (!isValidStmt(stmt))
     307             :                  return SQL_INVALID_HANDLE;
     308             : 
     309           4 :         clearStmtErrors(stmt);
     310             : 
     311           4 :         return MNDBProcedureColumns(stmt,
     312             :                                     CatalogName, NameLength1,
     313             :                                     SchemaName, NameLength2,
     314             :                                     ProcName, NameLength3,
     315             :                                     ColumnName, NameLength4);
     316             : }
     317             : 
     318             : SQLRETURN SQL_API
     319             : SQLProcedureColumnsA(SQLHSTMT StatementHandle,
     320             :                      SQLCHAR *CatalogName,
     321             :                      SQLSMALLINT NameLength1,
     322             :                      SQLCHAR *SchemaName,
     323             :                      SQLSMALLINT NameLength2,
     324             :                      SQLCHAR *ProcName,
     325             :                      SQLSMALLINT NameLength3,
     326             :                      SQLCHAR *ColumnName,
     327             :                      SQLSMALLINT NameLength4)
     328             : {
     329           0 :         return SQLProcedureColumns(StatementHandle,
     330             :                                    CatalogName, NameLength1,
     331             :                                    SchemaName, NameLength2,
     332             :                                    ProcName, NameLength3,
     333             :                                    ColumnName, NameLength4);
     334             : }
     335             : 
     336             : SQLRETURN SQL_API
     337             : SQLProcedureColumnsW(SQLHSTMT StatementHandle,
     338             :                      SQLWCHAR *CatalogName,
     339             :                      SQLSMALLINT NameLength1,
     340             :                      SQLWCHAR *SchemaName,
     341             :                      SQLSMALLINT NameLength2,
     342             :                      SQLWCHAR *ProcName,
     343             :                      SQLSMALLINT NameLength3,
     344             :                      SQLWCHAR *ColumnName,
     345             :                      SQLSMALLINT NameLength4)
     346             : {
     347           0 :         ODBCStmt *stmt = (ODBCStmt *) StatementHandle;
     348           0 :         SQLRETURN rc = SQL_ERROR;
     349           0 :         SQLCHAR *catalog = NULL, *schema = NULL, *proc = NULL, *column = NULL;
     350             : 
     351             : #ifdef ODBCDEBUG
     352           0 :         ODBCLOG("SQLProcedureColumnsW %p ", StatementHandle);
     353             : #endif
     354             : 
     355           0 :         if (!isValidStmt(stmt))
     356             :                  return SQL_INVALID_HANDLE;
     357             : 
     358           0 :         clearStmtErrors(stmt);
     359             : 
     360           0 :         fixWcharIn(CatalogName, NameLength1, SQLCHAR, catalog,
     361             :                    addStmtError, stmt, goto bailout);
     362           0 :         fixWcharIn(SchemaName, NameLength2, SQLCHAR, schema,
     363             :                    addStmtError, stmt, goto bailout);
     364           0 :         fixWcharIn(ProcName, NameLength3, SQLCHAR, proc,
     365             :                    addStmtError, stmt, goto bailout);
     366           0 :         fixWcharIn(ColumnName, NameLength4, SQLCHAR, column,
     367             :                    addStmtError, stmt, goto bailout);
     368             : 
     369           0 :         rc = MNDBProcedureColumns(stmt,
     370             :                                   catalog, SQL_NTS,
     371             :                                   schema, SQL_NTS,
     372             :                                   proc, SQL_NTS,
     373             :                                   column, SQL_NTS);
     374             : 
     375           0 :       bailout:
     376           0 :         if (catalog)
     377           0 :                 free(catalog);
     378           0 :         if (schema)
     379           0 :                 free(schema);
     380           0 :         if (proc)
     381           0 :                 free(proc);
     382           0 :         if (column)
     383           0 :                 free(column);
     384             : 
     385             :         return rc;
     386             : }

Generated by: LCOV version 1.14