Skip to content

Commit 1bd24c4

Browse files
committed
Fix SQLNumResultCols for CALL, EXECUTE (1.4)
This is a backport of the PR duckdb#241 to `v1.4-andium` stable branch. This PR fixes the `SQLNumResultCols` function making it to return the actual number of columns for `CALL`, `EXECUTE` and `EXPLAIN` queries. Without that some ODBC clients were unable to fetch the result set for such queries. `EXPLAIN` output contains 1 row with 2 columns. First column is a constant `physical_plan` (or `logical_plan`) and the second column contains actual plan details as a `VARCHAR`. Testing: new test added. Fixes: duckdb#200
1 parent 52c9d8d commit 1bd24c4

File tree

3 files changed

+84
-1
lines changed

3 files changed

+84
-1
lines changed

src/prepared.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,13 @@ SQLRETURN SQL_API SQLNumResultCols(SQLHSTMT statement_handle, SQLSMALLINT *colum
7777
}
7878
*column_count_ptr = (SQLSMALLINT)hstmt->stmt->ColumnCount();
7979

80-
if (hstmt->stmt->data->statement_type != duckdb::StatementType::SELECT_STATEMENT) {
80+
switch (hstmt->stmt->data->statement_type) {
81+
case duckdb::StatementType::CALL_STATEMENT:
82+
case duckdb::StatementType::EXECUTE_STATEMENT:
83+
case duckdb::StatementType::EXPLAIN_STATEMENT:
84+
case duckdb::StatementType::SELECT_STATEMENT:
85+
break;
86+
default:
8187
*column_count_ptr = 0;
8288
}
8389
return SQL_SUCCESS;

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ add_executable(
3131
tests/test_allowed_paths.cpp
3232
tests/test_connect.cpp
3333
tests/test_long_data.cpp
34+
tests/test_num_result_cols.cpp
3435
tests/test_session_init.cpp
3536
tests/test_timestamp.cpp
3637
tests/test_unbound_params.cpp
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#include "odbc_test_common.h"
2+
3+
using namespace odbc_test;
4+
5+
TEST_CASE("Test EXPLAIN output", "[odbc]") {
6+
SQLHANDLE env;
7+
SQLHANDLE dbc;
8+
9+
HSTMT hstmt = SQL_NULL_HSTMT;
10+
11+
CONNECT_TO_DATABASE(env, dbc);
12+
EXECUTE_AND_CHECK("SQLAllocHandle (HSTMT)", hstmt, SQLAllocHandle, SQL_HANDLE_STMT, dbc, &hstmt);
13+
14+
EXECUTE_AND_CHECK("SQLExecDirect", hstmt, SQLExecDirect, hstmt, ConvertToSQLCHAR("EXPLAIN SELECT 42"), SQL_NTS);
15+
16+
EXECUTE_AND_CHECK("SQLFetch", hstmt, SQLFetch, hstmt);
17+
SQLSMALLINT cols = 0;
18+
EXECUTE_AND_CHECK("SQLNumResultCols", hstmt, SQLNumResultCols, hstmt, &cols);
19+
REQUIRE(cols == 2);
20+
DATA_CHECK(hstmt, 1, "physical_plan");
21+
22+
EXECUTE_AND_CHECK("SQLFreeStmt (HSTMT)", hstmt, SQLFreeStmt, hstmt, SQL_CLOSE);
23+
EXECUTE_AND_CHECK("SQLFreeHandle (HSTMT)", hstmt, SQLFreeHandle, SQL_HANDLE_STMT, hstmt);
24+
DISCONNECT_FROM_DATABASE(env, dbc);
25+
}
26+
27+
TEST_CASE("Test CALL output", "[odbc]") {
28+
SQLHANDLE env;
29+
SQLHANDLE dbc;
30+
31+
HSTMT hstmt = SQL_NULL_HSTMT;
32+
33+
CONNECT_TO_DATABASE(env, dbc);
34+
EXECUTE_AND_CHECK("SQLAllocHandle (HSTMT)", hstmt, SQLAllocHandle, SQL_HANDLE_STMT, dbc, &hstmt);
35+
36+
EXECUTE_AND_CHECK("SQLExecDirect", hstmt, SQLExecDirect, hstmt,
37+
ConvertToSQLCHAR("CREATE MACRO hello() AS TABLE SELECT 'Hello' AS column1, 'World' AS column2"),
38+
SQL_NTS);
39+
EXECUTE_AND_CHECK("SQLExecDirect", hstmt, SQLExecDirect, hstmt, ConvertToSQLCHAR("CALL hello()"), SQL_NTS);
40+
41+
EXECUTE_AND_CHECK("SQLFetch", hstmt, SQLFetch, hstmt);
42+
SQLSMALLINT cols = 0;
43+
EXECUTE_AND_CHECK("SQLNumResultCols", hstmt, SQLNumResultCols, hstmt, &cols);
44+
REQUIRE(cols == 2);
45+
DATA_CHECK(hstmt, 1, "Hello");
46+
DATA_CHECK(hstmt, 2, "World");
47+
48+
EXECUTE_AND_CHECK("SQLFreeStmt (HSTMT)", hstmt, SQLFreeStmt, hstmt, SQL_CLOSE);
49+
EXECUTE_AND_CHECK("SQLFreeHandle (HSTMT)", hstmt, SQLFreeHandle, SQL_HANDLE_STMT, hstmt);
50+
DISCONNECT_FROM_DATABASE(env, dbc);
51+
}
52+
53+
TEST_CASE("Test EXECUTE output", "[odbc]") {
54+
SQLHANDLE env;
55+
SQLHANDLE dbc;
56+
57+
HSTMT hstmt = SQL_NULL_HSTMT;
58+
59+
CONNECT_TO_DATABASE(env, dbc);
60+
EXECUTE_AND_CHECK("SQLAllocHandle (HSTMT)", hstmt, SQLAllocHandle, SQL_HANDLE_STMT, dbc, &hstmt);
61+
62+
EXECUTE_AND_CHECK("SQLExecDirect", hstmt, SQLExecDirect, hstmt,
63+
ConvertToSQLCHAR("PREPARE p AS SELECT 'Hello' AS column1, 'World' AS column2"), SQL_NTS);
64+
EXECUTE_AND_CHECK("SQLExecDirect", hstmt, SQLExecDirect, hstmt, ConvertToSQLCHAR("EXECUTE p"), SQL_NTS);
65+
66+
EXECUTE_AND_CHECK("SQLFetch", hstmt, SQLFetch, hstmt);
67+
SQLSMALLINT cols = 0;
68+
EXECUTE_AND_CHECK("SQLNumResultCols", hstmt, SQLNumResultCols, hstmt, &cols);
69+
REQUIRE(cols == 2);
70+
DATA_CHECK(hstmt, 1, "Hello");
71+
DATA_CHECK(hstmt, 2, "World");
72+
73+
EXECUTE_AND_CHECK("SQLFreeStmt (HSTMT)", hstmt, SQLFreeStmt, hstmt, SQL_CLOSE);
74+
EXECUTE_AND_CHECK("SQLFreeHandle (HSTMT)", hstmt, SQLFreeHandle, SQL_HANDLE_STMT, hstmt);
75+
DISCONNECT_FROM_DATABASE(env, dbc);
76+
}

0 commit comments

Comments
 (0)