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