1 #include "common.h"
2 
3 #include <common/test_assert.h>
4 
5 /* Test using array binding */
6 
7 static char software_version[] = "$Id: array.c 487476 2015-12-17 19:48:39Z ucko $";
8 static void *no_unused_var_warn[] = { software_version, no_unused_var_warn };
9 
10 static SQLTCHAR *test_query = NULL;
11 static int multiply = 90;
12 static int failure = 0;
13 
14 #define XMALLOC_N(t, n) (t*) ODBC_GET(n*sizeof(t))
15 
16 static void
query_test(int prepare,SQLRETURN expected,const char * expected_status)17 query_test(int prepare, SQLRETURN expected, const char *expected_status)
18 {
19 #define DESC_LEN 51
20 #define ARRAY_SIZE 10
21 
22 	SQLUINTEGER *ids = XMALLOC_N(SQLUINTEGER,ARRAY_SIZE);
23 	typedef SQLCHAR desc_t[DESC_LEN];
24 	desc_t *descs = XMALLOC_N(desc_t, ARRAY_SIZE);
25 	SQLLEN *id_lens = XMALLOC_N(SQLLEN,ARRAY_SIZE), *desc_lens = XMALLOC_N(SQLLEN,ARRAY_SIZE);
26 	SQLUSMALLINT i, *statuses = XMALLOC_N(SQLUSMALLINT,ARRAY_SIZE);
27 	unsigned *num_errors = XMALLOC_N(unsigned, ARRAY_SIZE);
28 	SQLULEN processed;
29 	RETCODE ret;
30 	char status[20];
31 	SQLTCHAR *err = ODBC_GET(sizeof(odbc_err)*sizeof(SQLTCHAR));
32 	SQLTCHAR *state = ODBC_GET(sizeof(odbc_sqlstate)*sizeof(SQLTCHAR));
33 
34 	assert(odbc_stmt != SQL_NULL_HSTMT);
35 	odbc_reset_statement();
36 
37 	odbc_command_with_result(odbc_stmt, "drop table #tmp1");
38 	odbc_command("create table #tmp1 (id tinyint, value char(20))");
39 
40 	SQLSetStmtAttr(odbc_stmt, SQL_ATTR_PARAM_BIND_TYPE, SQL_PARAM_BIND_BY_COLUMN, 0);
41 	SQLSetStmtAttr(odbc_stmt, SQL_ATTR_PARAMSET_SIZE, (void *) ARRAY_SIZE, 0);
42 	SQLSetStmtAttr(odbc_stmt, SQL_ATTR_PARAM_STATUS_PTR, statuses, 0);
43 	SQLSetStmtAttr(odbc_stmt, SQL_ATTR_PARAMS_PROCESSED_PTR, &processed, 0);
44 	SQLBindParameter(odbc_stmt, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER, 5, 0, ids, 0, id_lens);
45 	SQLBindParameter(odbc_stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, DESC_LEN - 1, 0, descs, DESC_LEN, desc_lens);
46 
47 	processed = ARRAY_SIZE + 1;
48 	for (i = 0; i < ARRAY_SIZE; i++) {
49 		statuses[i] = SQL_PARAM_DIAG_UNAVAILABLE;
50 		ids[i] = (i + 1) * multiply;
51 		sprintf((char *) descs[i], "data %d", i * 7);
52 		id_lens[i] = 0;
53 		desc_lens[i] = SQL_NTS;
54 		num_errors[i] = 0;
55 	}
56 	multiply = 90;
57 
58 	if (!prepare) {
59 		ret = SQLExecDirect(odbc_stmt, test_query, SQL_NTS);
60 	} else {
61 		SQLPrepare(odbc_stmt, test_query, SQL_NTS);
62 		ret = SQLExecute(odbc_stmt);
63 	}
64 
65 	if (processed > ARRAY_SIZE) {
66 		char buf[256];
67 
68 		sprintf(buf, "Invalid processed number: %d", (int) processed);
69 		ODBC_REPORT_ERROR(buf);
70 	}
71 
72 	for (i = 1; CHKGetDiagRec(SQL_HANDLE_STMT, odbc_stmt, i, state, NULL, err, sizeof(odbc_err), NULL, "SINo") != SQL_NO_DATA; ++i) {
73 		SQLINTEGER row;
74 
75 		strcpy(odbc_err, C(err));
76 		strcpy(odbc_sqlstate, C(state));
77 		CHKGetDiagField(SQL_HANDLE_STMT, odbc_stmt, i, SQL_DIAG_ROW_NUMBER, &row, sizeof(row), NULL, "S");
78 
79 		if (row == SQL_ROW_NUMBER_UNKNOWN) continue;
80 		if (row < 1 || row > ARRAY_SIZE) {
81 			fprintf(stderr, "invalid row %d returned reading error number %d\n", (int) row, i);
82 			exit(1);
83 		}
84 		++num_errors[row-1];
85 		printf("for row %2d returned '%s' %s\n", (int) row, odbc_sqlstate, odbc_err);
86 	}
87 
88 	for (i = 0; i < processed; ++i) {
89 		int has_diag = 0;
90 
91 		switch (statuses[i]) {
92 		case SQL_PARAM_SUCCESS_WITH_INFO:
93 			has_diag = 1;
94 		case SQL_PARAM_SUCCESS:
95 			status[i] = 'V';
96 			break;
97 
98 		case SQL_PARAM_ERROR:
99 			has_diag = 1;
100 			status[i] = '!';
101 			break;
102 
103 		case SQL_PARAM_UNUSED:
104 			status[i] = ' ';
105 			break;
106 
107 		case SQL_PARAM_DIAG_UNAVAILABLE:
108 			status[i] = '?';
109 			break;
110 		default:
111 			fprintf(stderr, "Invalid status returned %d\n", statuses[i]);
112 			exit(1);
113 		}
114 
115 		if (has_diag) {
116 			if (!num_errors[i]) {
117 				fprintf(stderr, "Diagnostics not returned for status %d\n", i);
118 				failure = 1;
119 			}
120 		} else {
121 			if (num_errors[i]) {
122 				fprintf(stderr, "Diagnostics returned for status %d\n", i);
123 				failure = 1;
124 			}
125 		}
126 	}
127 	status[i] = 0;
128 
129 	if (ret != expected || strcmp(expected_status, status) != 0) {
130 		fprintf(stderr, "Invalid result: got %d \"%s\" expected %d \"%s\" processed %d\n",
131 			ret, status, expected, expected_status, (int) processed);
132 		if (ret != SQL_SUCCESS)
133 			odbc_read_error();
134 		failure = 1;
135 		odbc_reset_statement();
136 		return;
137 	}
138 
139 	odbc_reset_statement();
140 }
141 
142 int
main(int argc,char * argv[])143 main(int argc, char *argv[])
144 {
145 	odbc_use_version3 = 1;
146 	odbc_connect();
147 
148 	if (odbc_db_is_microsoft()) {
149 		/* all successes */
150 		test_query = T("INSERT INTO #tmp1 (id, value) VALUES (?, ?)");
151 		multiply = 1;
152 		query_test(0, SQL_SUCCESS, "VVVVVVVVVV");
153 		multiply = 1;
154 		query_test(1, SQL_SUCCESS, "VVVVVVVVVV");
155 
156 		/* all errors */
157 		test_query = T("INSERT INTO #tmp1 (id, value) VALUES (?, ?)");
158 		multiply = 257;
159 		query_test(0, SQL_ERROR, "!!!!!!!!!!");
160 		multiply = 257;
161 		query_test(1, SQL_SUCCESS_WITH_INFO, "!!!!!!!!!!");
162 
163 		test_query = T("INSERT INTO #tmp1 (id, value) VALUES (?, ?)");
164 		query_test(0, SQL_ERROR, "VV!!!!!!!!");
165 		query_test(1, SQL_SUCCESS_WITH_INFO, "VV!!!!!!!!");
166 
167 		test_query = T("INSERT INTO #tmp1 (id, value) VALUES (900-?, ?)");
168 		query_test(0, SQL_SUCCESS_WITH_INFO, "!!!!!!!VVV");
169 		query_test(1, SQL_SUCCESS_WITH_INFO, "!!!!!!!VVV");
170 
171 		test_query = T("INSERT INTO #tmp1 (id) VALUES (?) UPDATE #tmp1 SET value = ?");
172 		query_test(0, SQL_SUCCESS_WITH_INFO, "VVVV!V!V!V");
173 		query_test(1, SQL_SUCCESS_WITH_INFO, "VV!!!!!!!!");
174 
175 #ifdef ENABLE_DEVELOPING
176 		/* with result, see how SQLMoreResult work */
177 		test_query = T("INSERT INTO #tmp1 (id) VALUES (?) SELECT * FROM #tmp1 UPDATE #tmp1 SET value = ?");
178 		/* IMHO our driver is better here -- freddy77 */
179 		query_test(0, SQL_SUCCESS, odbc_driver_is_freetds() ? "VVVVV!V!V!" : "VVVVVV!VVV");
180 		query_test(1, SQL_SUCCESS, "VVVVVVVVVV");
181 #endif
182 	} else {
183 		/* Sybase test for conversions before executing */
184 		test_query = T("INSERT INTO #tmp1 (id, value) VALUES (?/8, ?)");
185 		query_test(0, SQL_SUCCESS, "VVVVVVVVVV");
186 	}
187 
188 	/* TODO record binding, array fetch, sqlputdata */
189 
190 	odbc_disconnect();
191 
192 	printf(failure ? "Failed :(\n" : "Success!\n");
193 	return failure;
194 }
195 
196