Line data Source code
1 : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 : * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Brian Bruns
3 : * Copyright (C) 2005-2008 Frediano Ziglio
4 : *
5 : * This library is free software; you can redistribute it and/or
6 : * modify it under the terms of the GNU Library General Public
7 : * License as published by the Free Software Foundation; either
8 : * version 2 of the License, or (at your option) any later version.
9 : *
10 : * This library is distributed in the hope that it will be useful,
11 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 : * Library General Public License for more details.
14 : *
15 : * You should have received a copy of the GNU Library General Public
16 : * License along with this library; if not, write to the
17 : * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 : * Boston, MA 02111-1307, USA.
19 : */
20 :
21 : #include <config.h>
22 :
23 : #include <stdarg.h>
24 : #include <stdio.h>
25 : #include <assert.h>
26 :
27 : #if HAVE_STDLIB_H
28 : #include <stdlib.h>
29 : #endif /* HAVE_STDLIB_H */
30 :
31 : #if HAVE_STRING_H
32 : #include <string.h>
33 : #endif /* HAVE_STRING_H */
34 :
35 : #include <ctype.h>
36 :
37 : #include <freetds/odbc.h>
38 : #include <freetds/convert.h>
39 : #include <freetds/utils/string.h>
40 :
41 : #define TDS_ISSPACE(c) isspace((unsigned char) (c))
42 :
43 : static int
44 728 : prepared_rpc(struct _hstmt *stmt, bool compute_row)
45 : {
46 728 : int nparam = stmt->params ? stmt->params->num_cols : 0;
47 : const char *p;
48 728 : TDSCONNECTION *conn = stmt->dbc->tds_socket->conn;
49 :
50 1456 : if (stmt->prepared_pos > tds_dstr_len(&stmt->query))
51 : return SQL_ERROR;
52 :
53 1456 : p = tds_dstr_cstr(&stmt->query) + stmt->prepared_pos - 1;
54 :
55 310 : for (;;) {
56 : TDSPARAMINFO *temp_params;
57 : TDSCOLUMN *curcol;
58 : TDS_SERVER_TYPE type;
59 : const char *start;
60 :
61 1868 : while (TDS_ISSPACE(*++p))
62 830 : continue;
63 1038 : if (!*p)
64 728 : return SQL_SUCCESS;
65 :
66 : /* we have certainly a parameter */
67 1032 : if (!(temp_params = tds_alloc_param_result(stmt->params))) {
68 0 : odbc_errs_add(&stmt->errs, "HY001", NULL);
69 0 : return SQL_ERROR;
70 : }
71 1032 : stmt->params = temp_params;
72 1032 : curcol = temp_params->columns[nparam];
73 :
74 1032 : switch (*p) {
75 8 : case ',':
76 8 : if (IS_TDS7_PLUS(conn)) {
77 6 : tds_set_param_type(conn, curcol, SYBVOID);
78 6 : curcol->column_size = curcol->column_cur_size = 0;
79 : } else {
80 : /* TODO is there a better type ? */
81 2 : tds_set_param_type(conn, curcol, SYBINTN);
82 2 : curcol->column_size = curcol->on_server.column_size = 4;
83 2 : curcol->column_cur_size = -1;
84 : }
85 8 : if (compute_row)
86 8 : if (!tds_alloc_param_data(curcol)) {
87 0 : tds_free_param_result(temp_params);
88 0 : return SQL_ERROR;
89 : }
90 : --p;
91 : break;
92 72 : default:
93 : /* add next parameter to list */
94 72 : start = p;
95 :
96 72 : if (!(p = parse_const_param(p, &type))) {
97 0 : tds_free_param_result(temp_params);
98 0 : return SQL_ERROR;
99 : }
100 72 : tds_set_param_type(conn, curcol, type);
101 72 : switch (type) {
102 40 : case SYBVARCHAR:
103 40 : curcol->column_size = p - start;
104 40 : break;
105 8 : case SYBVARBINARY:
106 8 : curcol->column_size = (p - start) / 2 -1;
107 8 : break;
108 : default:
109 0 : assert(0);
110 : case SYBINT4:
111 : case SYBINT8:
112 : case SYBFLT8:
113 24 : curcol->column_cur_size = curcol->column_size;
114 24 : break;
115 : }
116 72 : curcol->on_server.column_size = curcol->column_size;
117 : /* TODO support other type other than VARCHAR, do not strip escape in prepare_call */
118 72 : if (compute_row) {
119 : char *dest;
120 : int len;
121 : CONV_RESULT cr;
122 :
123 72 : if (!tds_alloc_param_data(curcol)) {
124 0 : tds_free_param_result(temp_params);
125 0 : return SQL_ERROR;
126 : }
127 72 : dest = (char *) curcol->column_data;
128 72 : switch (type) {
129 40 : case SYBVARCHAR:
130 40 : if (*start != '\'') {
131 0 : memcpy(dest, start, p - start);
132 0 : curcol->column_cur_size = p - start;
133 : } else {
134 40 : ++start;
135 : for (;;) {
136 824 : if (*start == '\'')
137 40 : ++start;
138 432 : if (start >= p)
139 : break;
140 392 : *dest++ = *start++;
141 : }
142 40 : curcol->column_cur_size =
143 40 : dest - (char *) curcol->column_data;
144 : }
145 : break;
146 8 : case SYBVARBINARY:
147 8 : cr.cb.len = curcol->column_size;
148 8 : cr.cb.ib = dest;
149 8 : len = tds_convert(NULL, SYBVARCHAR, start, p - start, TDS_CONVERT_BINARY, &cr);
150 8 : if (len >= 0 && len <= curcol->column_size)
151 8 : curcol->column_cur_size = len;
152 : break;
153 8 : case SYBINT4:
154 8 : *((TDS_INT *) dest) = strtol(start, NULL, 10);
155 8 : break;
156 8 : case SYBINT8:
157 8 : *((TDS_INT8 *) dest) = tds_strtoll(start, NULL, 10);
158 8 : break;
159 8 : case SYBFLT8:
160 8 : *((TDS_FLOAT *) dest) = strtod(start, NULL);
161 8 : break;
162 : default:
163 : break;
164 : }
165 : }
166 72 : --p;
167 72 : break;
168 952 : case '?':
169 : /* find bound parameter */
170 952 : if (stmt->param_num > stmt->apd->header.sql_desc_count
171 952 : || stmt->param_num > stmt->ipd->header.sql_desc_count) {
172 0 : tds_free_param_result(temp_params);
173 : /* TODO set error */
174 0 : return SQL_ERROR;
175 : }
176 :
177 1904 : switch (odbc_sql2tds
178 952 : (stmt, &stmt->ipd->records[stmt->param_num - 1], &stmt->apd->records[stmt->param_num - 1],
179 : curcol, compute_row, stmt->apd, stmt->curr_param_row)) {
180 : case SQL_ERROR:
181 : return SQL_ERROR;
182 0 : case SQL_NEED_DATA:
183 0 : return SQL_NEED_DATA;
184 : }
185 936 : ++stmt->param_num;
186 936 : break;
187 : }
188 1016 : ++nparam;
189 :
190 2760 : while (TDS_ISSPACE(*++p))
191 728 : continue;
192 1016 : if (!*p || *p != ',')
193 : return SQL_SUCCESS;
194 620 : stmt->prepared_pos = p + 1 - tds_dstr_cstr(&stmt->query);
195 : }
196 : }
197 :
198 : int
199 25222 : parse_prepared_query(struct _hstmt *stmt, bool compute_row)
200 : {
201 : /* try setting this parameter */
202 : TDSPARAMINFO *temp_params;
203 25222 : int nparam = stmt->params ? stmt->params->num_cols : 0;
204 :
205 25222 : if (stmt->prepared_pos > 0)
206 728 : return prepared_rpc(stmt, compute_row);
207 :
208 24494 : tdsdump_log(TDS_DBG_FUNC, "parsing %d parameters\n", nparam);
209 :
210 5588 : for (; stmt->param_num <= stmt->param_count; ++nparam, ++stmt->param_num) {
211 : /* find bound parameter */
212 5854 : if (stmt->param_num > stmt->apd->header.sql_desc_count || stmt->param_num > stmt->ipd->header.sql_desc_count) {
213 6 : tdsdump_log(TDS_DBG_FUNC, "parse_prepared_query: logic_error: parameter out of bounds: "
214 : "%d > %d || %d > %d\n",
215 : stmt->param_num, stmt->apd->header.sql_desc_count,
216 0 : stmt->param_num, stmt->ipd->header.sql_desc_count);
217 : return SQL_ERROR;
218 : }
219 :
220 : /* add a column to parameters */
221 5848 : if (!(temp_params = tds_alloc_param_result(stmt->params))) {
222 0 : odbc_errs_add(&stmt->errs, "HY001", NULL);
223 0 : return SQL_ERROR;
224 : }
225 5848 : stmt->params = temp_params;
226 :
227 23392 : switch (odbc_sql2tds
228 5848 : (stmt, &stmt->ipd->records[stmt->param_num - 1], &stmt->apd->records[stmt->param_num - 1],
229 11696 : stmt->params->columns[nparam], compute_row, stmt->apd, stmt->curr_param_row)) {
230 : case SQL_ERROR:
231 : return SQL_ERROR;
232 260 : case SQL_NEED_DATA:
233 260 : return SQL_NEED_DATA;
234 : }
235 : }
236 : return SQL_SUCCESS;
237 : }
238 :
239 : int
240 24962 : start_parse_prepared_query(struct _hstmt *stmt, bool compute_row)
241 : {
242 : /* TODO should be NULL already ?? */
243 24962 : tds_free_param_results(stmt->params);
244 24962 : stmt->params = NULL;
245 : stmt->param_num = 0;
246 :
247 24962 : stmt->param_num = stmt->prepared_query_is_func ? 2 : 1;
248 24962 : return parse_prepared_query(stmt, compute_row);
249 : }
250 :
251 : static TDS_INT
252 48 : odbc_wchar2hex(TDS_CHAR *dest, TDS_UINT destlen, const SQLWCHAR * src, TDS_UINT srclen)
253 : {
254 : unsigned int i;
255 48 : SQLWCHAR hex1, c = 0;
256 :
257 : /* if srclen if odd we must add a "0" before ... */
258 48 : i = 0; /* number where to start converting */
259 48 : if (srclen & 1) {
260 0 : ++srclen;
261 0 : i = 1;
262 0 : --src;
263 : }
264 320016 : for (; i < srclen; ++i) {
265 320016 : hex1 = src[i];
266 :
267 320016 : if ('0' <= hex1 && hex1 <= '9')
268 276928 : hex1 &= 0x0f;
269 : else {
270 43088 : hex1 &= (SQLWCHAR) ~0x20u; /* mask off 0x20 to ensure upper case */
271 43088 : if ('A' <= hex1 && hex1 <= 'F') {
272 43088 : hex1 -= ('A' - 10);
273 : } else {
274 0 : tdsdump_log(TDS_DBG_INFO1,
275 : "error_handler: attempt to convert data stopped by syntax error in source field \n");
276 : return TDS_CONVERT_SYNTAX;
277 : }
278 : }
279 320016 : assert(hex1 < 0x10);
280 :
281 320016 : if ((i/2u) >= destlen)
282 0 : continue;
283 :
284 320016 : if (i & 1)
285 160008 : dest[i / 2u] = c | hex1;
286 : else
287 160008 : c = hex1 << 4;
288 : }
289 48 : return srclen / 2u;
290 : }
291 :
292 :
293 : int
294 652 : continue_parse_prepared_query(struct _hstmt *stmt, SQLPOINTER DataPtr, SQLLEN StrLen_or_Ind)
295 : {
296 : struct _drecord *drec_apd, *drec_ipd;
297 : SQLLEN len;
298 : int need_bytes;
299 : TDSCOLUMN *curcol;
300 : TDSBLOB *blob;
301 : int sql_src_type;
302 :
303 652 : assert(stmt);
304 :
305 652 : tdsdump_log(TDS_DBG_FUNC, "continue_parse_prepared_query with parameter %d\n", stmt->param_num);
306 :
307 652 : if (!stmt->params) {
308 0 : tdsdump_log(TDS_DBG_FUNC, "error? continue_parse_prepared_query: no parameters provided");
309 : return SQL_ERROR;
310 : }
311 :
312 652 : if (stmt->param_num > stmt->apd->header.sql_desc_count || stmt->param_num > stmt->ipd->header.sql_desc_count)
313 : return SQL_ERROR;
314 652 : drec_apd = &stmt->apd->records[stmt->param_num - 1];
315 652 : drec_ipd = &stmt->ipd->records[stmt->param_num - 1];
316 :
317 652 : curcol = stmt->params->columns[stmt->param_num - (stmt->prepared_query_is_func ? 2 : 1)];
318 652 : blob = NULL;
319 652 : if (is_blob_col(curcol))
320 652 : blob = (TDSBLOB *) curcol->column_data;
321 652 : assert(curcol->column_cur_size <= curcol->column_size);
322 652 : need_bytes = curcol->column_size - curcol->column_cur_size;
323 :
324 652 : if (DataPtr == NULL) {
325 0 : switch(StrLen_or_Ind) {
326 : case SQL_NULL_DATA:
327 : case SQL_DEFAULT_PARAM:
328 : break; /* OK */
329 0 : default:
330 0 : odbc_errs_add(&stmt->errs, "HY009", NULL); /* Invalid use of null pointer */
331 0 : return SQL_ERROR;
332 : }
333 652 : }
334 :
335 : /* get C type */
336 652 : sql_src_type = drec_apd->sql_desc_concise_type;
337 652 : if (sql_src_type == SQL_C_DEFAULT)
338 0 : sql_src_type = odbc_sql_to_c_type_default(drec_ipd->sql_desc_concise_type);
339 :
340 652 : switch(StrLen_or_Ind) {
341 0 : case SQL_NTS:
342 0 : if (sql_src_type == SQL_C_WCHAR)
343 0 : len = sqlwcslen((SQLWCHAR *) DataPtr);
344 : else
345 0 : len = strlen((char *) DataPtr);
346 : break;
347 : case SQL_NULL_DATA:
348 : len = 0;
349 : break;
350 0 : case SQL_DEFAULT_PARAM:
351 : /* FIXME: use the default if the parameter has one. */
352 0 : odbc_errs_add(&stmt->errs, "07S01", NULL); /* Invalid use of default parameter */
353 0 : return SQL_ERROR;
354 652 : default:
355 652 : if (DataPtr && StrLen_or_Ind < 0) {
356 : /*
357 : * "The argument DataPtr was not a null pointer, and
358 : * the argument StrLen_or_Ind was less than 0
359 : * but not equal to SQL_NTS or SQL_NULL_DATA."
360 : */
361 0 : odbc_errs_add(&stmt->errs, "HY090", NULL);
362 0 : return SQL_ERROR;
363 : }
364 : len = StrLen_or_Ind;
365 : break;
366 : }
367 :
368 652 : if (!blob && len > need_bytes)
369 0 : len = need_bytes;
370 :
371 : /* copy to destination */
372 652 : if (blob) {
373 : TDS_CHAR *p;
374 652 : int binary_convert = 0;
375 652 : SQLLEN orig_len = len;
376 :
377 652 : if (sql_src_type == SQL_C_CHAR || sql_src_type == SQL_C_WCHAR) {
378 512 : TDS_SERVER_TYPE type = tds_get_conversion_type(curcol->column_type, curcol->column_size);
379 512 : if (is_binary_type(type)) {
380 64 : if (len && sql_src_type == SQL_C_CHAR && !*((char*)DataPtr+len-1))
381 0 : --len;
382 :
383 64 : if (sql_src_type == SQL_C_WCHAR)
384 32 : len /= sizeof(SQLWCHAR);
385 :
386 64 : if (!len)
387 : return SQL_SUCCESS;
388 :
389 64 : binary_convert = 1;
390 64 : orig_len = len;
391 64 : len = len / 2u + 1u;
392 : }
393 : }
394 :
395 652 : if (!len)
396 : return SQL_SUCCESS;
397 :
398 628 : assert(blob->textvalue || curcol->column_cur_size == 0);
399 628 : p = (TDS_CHAR *) TDS_RESIZE(blob->textvalue, len + curcol->column_cur_size);
400 628 : if (!p) {
401 0 : odbc_errs_add(&stmt->errs, "HY001", NULL); /* Memory allocation error */
402 0 : return SQL_ERROR;
403 : }
404 :
405 628 : p += curcol->column_cur_size;
406 628 : if (binary_convert) {
407 : int res;
408 :
409 64 : len = orig_len;
410 :
411 64 : if (curcol->column_cur_size > 0
412 32 : && curcol->column_text_sqlputdatainfo) {
413 : SQLWCHAR data[2];
414 16 : data[0] = curcol->column_text_sqlputdatainfo;
415 16 : data[1] = (sql_src_type == SQL_C_CHAR) ? *(unsigned char*)DataPtr : *(SQLWCHAR*)DataPtr;
416 :
417 16 : res = odbc_wchar2hex(p, 1, data, 2);
418 16 : if (res < 0) {
419 0 : odbc_convert_err_set(&stmt->errs, res);
420 0 : return SQL_ERROR;
421 : }
422 16 : p += res;
423 :
424 16 : DataPtr = (SQLPOINTER) (((char*)DataPtr) +
425 16 : (sql_src_type == SQL_C_CHAR ? 1 : sizeof(SQLWCHAR)));
426 16 : --len;
427 : }
428 :
429 64 : if (len&1) {
430 16 : --len;
431 16 : curcol->column_text_sqlputdatainfo = (sql_src_type == SQL_C_CHAR) ? ((char*)DataPtr)[len] : ((SQLWCHAR*)DataPtr)[len];
432 : }
433 :
434 64 : res = (sql_src_type == SQL_C_CHAR) ?
435 96 : tds_char2hex(p, len / 2u, (const TDS_CHAR*) DataPtr, len):
436 32 : odbc_wchar2hex(p, len / 2u, (const SQLWCHAR*) DataPtr, len);
437 64 : if (res < 0) {
438 0 : odbc_convert_err_set(&stmt->errs, res);
439 0 : return SQL_ERROR;
440 : }
441 64 : p += res;
442 :
443 64 : len = p - (blob->textvalue + curcol->column_cur_size);
444 : } else {
445 564 : memcpy(blob->textvalue + curcol->column_cur_size, DataPtr, len);
446 : }
447 : } else {
448 0 : memcpy(curcol->column_data + curcol->column_cur_size, DataPtr, len);
449 : }
450 :
451 628 : curcol->column_cur_size += len;
452 :
453 628 : if (blob && curcol->column_cur_size > curcol->column_size)
454 408 : curcol->column_size = curcol->column_cur_size;
455 :
456 : return SQL_SUCCESS;
457 : }
|