1 #include "common.h"
2
3 #include <common/test_assert.h>
4
5 /* test RAISERROR in a store procedure, from Tom Rogers tests */
6
7 /* TODO add support for Sybase */
8
9 #define SP_TEXT "{?=call #tmp1(?,?,?)}"
10 #define OUTSTRING_LEN 20
11 #define INVALID_RETURN (-12345)
12
13 static const char create_proc[] =
14 "CREATE PROCEDURE #tmp1\n"
15 " @InParam int,\n"
16 " @OutParam int OUTPUT,\n"
17 " @OutString varchar(20) OUTPUT\n"
18 "AS\n"
19 "%s"
20 " SET @OutParam = @InParam\n"
21 " SET @OutString = 'This is bogus!'\n"
22 " SELECT 'Here is the first row' AS FirstResult\n"
23 " RAISERROR('An error occurred.', @InParam, 1)\n"
24 "%s"
25 " RETURN (0)";
26
27 static SQLSMALLINT ReturnCode;
28 static char OutString[OUTSTRING_LEN];
29 static int g_nocount, g_second_select;
30
31 #ifdef TDS_NO_DM
32 static const int tds_no_dm = 1;
33 #else
34 static const int tds_no_dm = 0;
35 #endif
36
37 static void
TestResult(SQLRETURN result0,int level,const char * func)38 TestResult(SQLRETURN result0, int level, const char *func)
39 {
40 ODBC_BUF *odbc_buf = NULL;
41 SQLTCHAR SqlState[6];
42 SQLINTEGER NativeError;
43 SQLTCHAR MessageText[1000];
44 SQLSMALLINT TextLength;
45 SQLRETURN result = result0, rc;
46
47 if (result == SQL_NO_DATA && strcmp(func, "SQLFetch") == 0)
48 result = SQL_SUCCESS_WITH_INFO;
49
50 if ((level <= 10 && result != SQL_SUCCESS_WITH_INFO) || (level > 10 && result != SQL_ERROR) || ReturnCode != INVALID_RETURN) {
51 fprintf(stderr, "%s failed!\n", func);
52 exit(1);
53 }
54
55 /*
56 * unixODBC till 2.2.11 do not support getting error if SQL_NO_DATA
57 */
58 if (!tds_no_dm && result0 == SQL_NO_DATA && strcmp(func, "SQLFetch") == 0)
59 return;
60
61 SqlState[0] = 0;
62 MessageText[0] = 0;
63 NativeError = 0;
64 rc = CHKGetDiagRec(SQL_HANDLE_STMT, odbc_stmt, 1, SqlState, &NativeError, MessageText, ODBC_VECTOR_SIZE(MessageText), &TextLength, "SI");
65 printf("Func=%s Result=%d DIAG REC 1: State=%s Error=%d: %s\n", func, (int) rc, C(SqlState), (int) NativeError, C(MessageText));
66
67 if (strstr(C(MessageText), "An error occurred") == NULL) {
68 fprintf(stderr, "Wrong error returned!\n");
69 fprintf(stderr, "Error returned: %s\n", C(MessageText));
70 exit(1);
71 }
72 ODBC_FREE();
73 }
74
75 #define MY_ERROR(msg) odbc_report_error(msg, line, __FILE__)
76
77 static void
CheckData(const char * s,int line)78 CheckData(const char *s, int line)
79 {
80 char buf[80];
81 SQLLEN ind;
82 SQLRETURN rc;
83
84 rc = CHKGetData(1, SQL_C_CHAR, buf, sizeof(buf), &ind, "SE");
85
86 if (rc == SQL_ERROR) {
87 buf[0] = 0;
88 ind = 0;
89 }
90
91 if (strlen(s) != ind || strcmp(buf, s) != 0)
92 MY_ERROR("Invalid result");
93 }
94
95 #define CheckData(s) CheckData(s, __LINE__)
96
97 static void
CheckReturnCode(SQLRETURN result,SQLSMALLINT expected,int line)98 CheckReturnCode(SQLRETURN result, SQLSMALLINT expected, int line)
99 {
100 if (ReturnCode == expected && (expected != INVALID_RETURN || strcmp(OutString, "Invalid!") == 0)
101 && (expected == INVALID_RETURN || strcmp(OutString, "This is bogus!") == 0))
102 return;
103
104 printf("SpDateTest Output:\n");
105 printf(" Result = %d\n", (int) result);
106 printf(" Return Code = %d\n", (int) ReturnCode);
107 printf(" OutString = \"%s\"\n", OutString);
108 MY_ERROR("Invalid ReturnCode");
109 }
110
111 #define CheckReturnCode(res, exp) CheckReturnCode(res, exp, __LINE__)
112
113 static void
Test(int level)114 Test(int level)
115 {
116 SQLRETURN result;
117 SQLSMALLINT InParam = level;
118 SQLSMALLINT OutParam = 1;
119 SQLLEN cbReturnCode = 0, cbInParam = 0, cbOutParam = 0;
120 SQLLEN cbOutString = SQL_NTS;
121
122 char sql[80];
123
124 printf("ODBC %d nocount %s select %s level %d\n", odbc_use_version3 ? 3 : 2,
125 g_nocount ? "yes" : "no", g_second_select ? "yes" : "no", level);
126
127 ReturnCode = INVALID_RETURN;
128 memset(&OutString, 0, sizeof(OutString));
129
130 /* test with SQLExecDirect */
131 sprintf(sql, "RAISERROR('An error occurred.', %d, 1)", level);
132 result = odbc_command_with_result(odbc_stmt, sql);
133
134 TestResult(result, level, "SQLExecDirect");
135
136 /* test with SQLPrepare/SQLExecute */
137 if ( !SQL_SUCCEEDED(SQLPrepare(odbc_stmt, T(SP_TEXT),
138 (SQLINTEGER) strlen(SP_TEXT))) ) {
139 fprintf(stderr, "SQLPrepare failure!\n");
140 exit(1);
141 }
142
143 SQLBindParameter(odbc_stmt, 1, SQL_PARAM_OUTPUT, SQL_C_SSHORT, SQL_INTEGER, 0, 0, &ReturnCode, 0, &cbReturnCode);
144 SQLBindParameter(odbc_stmt, 2, SQL_PARAM_INPUT, SQL_C_SSHORT, SQL_INTEGER, 0, 0, &InParam, 0, &cbInParam);
145 SQLBindParameter(odbc_stmt, 3, SQL_PARAM_OUTPUT, SQL_C_SSHORT, SQL_INTEGER, 0, 0, &OutParam, 0, &cbOutParam);
146 strcpy(OutString, "Invalid!");
147 SQLBindParameter(odbc_stmt, 4, SQL_PARAM_OUTPUT, SQL_C_CHAR, SQL_VARCHAR, OUTSTRING_LEN, 0, OutString, OUTSTRING_LEN,
148 &cbOutString);
149
150 CHKExecute("S");
151
152 /* first select, check data are returned.
153 * SET statements before does not affect results
154 */
155 CheckData("");
156 CHKFetch("S");
157 CheckData("Here is the first row");
158
159 result = SQLFetch(odbc_stmt);
160 if (odbc_use_version3) {
161 SQLTCHAR SqlState[6];
162 SQLINTEGER NativeError;
163 SQLTCHAR MessageText[1000];
164 SQLSMALLINT TextLength;
165 SQLRETURN expected;
166 SQLLEN rows;
167
168 if (result != SQL_NO_DATA)
169 ODBC_REPORT_ERROR("SQLFetch should return NO DATA");
170 CHKGetDiagRec(SQL_HANDLE_STMT, odbc_stmt, 1, SqlState, &NativeError, MessageText,
171 ODBC_VECTOR_SIZE(MessageText), &TextLength, "No");
172 result = SQLMoreResults(odbc_stmt);
173 expected = level > 10 ? SQL_ERROR : SQL_SUCCESS_WITH_INFO;
174 if (result != expected)
175 ODBC_REPORT_ERROR("SQLMoreResults returned unexpected result");
176 if (!g_second_select && g_nocount) {
177 if (ReturnCode == INVALID_RETURN) {
178 result = SQLMoreResults(odbc_stmt);
179 } else {
180 CheckReturnCode(result, 0);
181 ReturnCode = INVALID_RETURN;
182 TestResult(result, level, "SQLMoreResults");
183 ReturnCode = 0;
184 }
185 } else {
186 TestResult(result, level, "SQLMoreResults");
187 }
188
189 /* a recordset with only warnings/errors do not contains rows */
190 if (CHKRowCount(&rows, "SE") == SQL_SUCCESS && rows != -1)
191 ODBC_REPORT_ERROR("SQLRowCount returned some rows");
192 } else {
193 /* in ODBC 2 errors/warnings are not handled as different recordset */
194 TestResult(result, level, "SQLFetch");
195 }
196
197 if (odbc_driver_is_freetds())
198 CheckData("");
199
200 if (!g_second_select) {
201 SQLLEN rows;
202
203 if (CHKRowCount(&rows, "SE") == SQL_SUCCESS && rows != -1)
204 ODBC_REPORT_ERROR("SQLRowCount returned some rows");
205 CheckReturnCode(result, g_nocount ? 0 : INVALID_RETURN);
206
207 result = SQLMoreResults(odbc_stmt);
208 #ifdef ENABLE_DEVELOPING
209 if (result != SQL_NO_DATA)
210 ODBC_REPORT_ERROR("SQLMoreResults should return NO DATA");
211
212 ODBC_CHECK_ROWS(-2);
213 #endif
214 CheckReturnCode(result, 0);
215 return;
216 }
217
218 if (!odbc_use_version3 || !g_nocount) {
219 /* mssql 2008 return SUCCESS_WITH_INFO with previous error */
220 CHKMoreResults("S");
221 result = SQL_SUCCESS;
222 }
223
224 CheckReturnCode(result, INVALID_RETURN);
225
226 CheckData("");
227 if (g_nocount && odbc_use_version3 && g_second_select && level >= 10) {
228 if (CHKFetch("SE") == SQL_ERROR) {
229 SQLMoreResults(odbc_stmt);
230 CHKFetch("S");
231 }
232 } else {
233 CHKFetch("S");
234 }
235 CheckData("Here is the last row");
236
237 CHKFetch("No");
238 CheckData("");
239
240 if (!odbc_use_version3 || g_nocount)
241 CheckReturnCode(result, 0);
242 #ifdef ENABLE_DEVELOPING
243 else
244 CheckReturnCode(result, INVALID_RETURN);
245 #endif
246
247 /* FIXME how to handle return in store procedure ?? */
248 result = SQLMoreResults(odbc_stmt);
249 #ifdef ENABLE_DEVELOPING
250 if (result != SQL_NO_DATA)
251 ODBC_REPORT_ERROR("SQLMoreResults return other data");
252 #endif
253
254 CheckReturnCode(result, 0);
255
256 CheckData("");
257 ODBC_FREE();
258 }
259
260 static void
Test2(int nocount,int second_select)261 Test2(int nocount, int second_select)
262 {
263 char sql[512];
264
265 g_nocount = nocount;
266 g_second_select = second_select;
267
268 /* this test do not work with Sybase */
269 if (!odbc_db_is_microsoft())
270 return;
271
272 sprintf(sql, create_proc, nocount ? " SET NOCOUNT ON\n" : "",
273 second_select ? " SELECT 'Here is the last row' AS LastResult\n" : "");
274 odbc_command(sql);
275
276 Test(5);
277
278 Test(11);
279
280 odbc_command("DROP PROC #tmp1");
281 }
282
283 int
main(int argc,char * argv[])284 main(int argc, char *argv[])
285 {
286 odbc_connect();
287
288 Test2(0, 1);
289
290 Test2(1, 1);
291
292 odbc_disconnect();
293
294 odbc_use_version3 = 1;
295
296 odbc_connect();
297
298 Test2(0, 1);
299 Test2(1, 1);
300
301 Test2(0, 0);
302 Test2(1, 0);
303
304 odbc_disconnect();
305
306 printf("Done.\n");
307 return 0;
308 }
309