1/*********************************************************************
2 *
3 * This is based on code created by Peter Harvey,
4 * (pharvey@codebydesign.com).
5 *
6 * Modified and extended by Nick Gorham
7 * (nick@lurcher.org).
8 *
9 * Any bugs or problems should be considered the fault of Nick and not
10 * Peter.
11 *
12 * copyright (c) 1999 Nick Gorham
13 *
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2 of the License, or (at your option) any later version.
18 *
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
23 *
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 *
28 **********************************************************************
29 *
30 * $Id: SQLError.c,v 1.11 2009/02/18 17:59:08 lurcher Exp $
31 *
32 * $Log: SQLError.c,v $
33 * Revision 1.11 2009/02/18 17:59:08 lurcher
34 * Shift to using config.h, the compile lines were making it hard to spot warnings
35 *
36 * Revision 1.10 2008/09/29 14:02:45 lurcher
37 * Fix missing dlfcn group option
38 *
39 * Revision 1.9 2008/05/20 13:43:47 lurcher
40 * Vms fixes
41 *
42 * Revision 1.8 2003/02/27 12:19:39 lurcher
43 *
44 * Add the A functions as well as the W
45 *
46 * Revision 1.7 2002/12/05 17:44:30 lurcher
47 *
48 * Display unknown return values in return logging
49 *
50 * Revision 1.6 2002/11/11 17:10:08 lurcher
51 *
52 * VMS changes
53 *
54 * Revision 1.5 2002/08/23 09:42:37 lurcher
55 *
56 * Fix some build warnings with casts, and a AIX linker mod, to include
57 * deplib's on the link line, but not the libtool generated ones
58 *
59 * Revision 1.4 2002/07/24 08:49:51 lurcher
60 *
61 * Alter UNICODE support to use iconv for UNICODE-ANSI conversion
62 *
63 * Revision 1.3 2002/02/27 11:27:14 lurcher
64 *
65 * Fix bug in error reporting
66 *
67 * Revision 1.2 2001/12/13 13:00:32 lurcher
68 *
69 * Remove most if not all warnings on 64 bit platforms
70 * Add support for new MS 3.52 64 bit changes
71 * Add override to disable the stopping of tracing
72 * Add MAX_ROWS support in postgres driver
73 *
74 * Revision 1.1.1.1 2001/10/17 16:40:05 lurcher
75 *
76 * First upload to SourceForge
77 *
78 * Revision 1.5 2001/07/03 09:30:41 nick
79 *
80 * Add ability to alter size of displayed message in the log
81 *
82 * Revision 1.4 2001/04/12 17:43:36 nick
83 *
84 * Change logging and added autotest to odbctest
85 *
86 * Revision 1.3 2001/01/06 15:00:12 nick
87 *
88 * Fix bug in SQLError introduced with UNICODE
89 *
90 * Revision 1.2 2000/12/31 20:30:54 nick
91 *
92 * Add UNICODE support
93 *
94 * Revision 1.1.1.1 2000/09/04 16:42:52 nick
95 * Imported Sources
96 *
97 * Revision 1.14 2000/06/23 16:11:35 ngorham
98 *
99 * Map ODBC 2 SQLSTATE values to ODBC 3
100 *
101 * Revision 1.13 1999/11/13 23:40:59 ngorham
102 *
103 * Alter the way DM logging works
104 * Upgrade the Postgres driver to 6.4.6
105 *
106 * Revision 1.12 1999/11/10 22:15:48 ngorham
107 *
108 * Fix some bugs with the DM and error reporting.
109 *
110 * Revision 1.11 1999/11/10 03:51:33 ngorham
111 *
112 * Update the error reporting in the DM to enable ODBC 3 and 2 calls to
113 * work at the same time
114 *
115 * Revision 1.10 1999/10/24 23:54:17 ngorham
116 *
117 * First part of the changes to the error reporting
118 *
119 * Revision 1.9 1999/10/14 06:49:24 ngorham
120 *
121 * Remove @all_includes@ from Drivers/MiniSQL/Makefile.am
122 *
123 * Revision 1.8 1999/09/21 22:34:24 ngorham
124 *
125 * Improve performance by removing unneeded logging calls when logging is
126 * disabled
127 *
128 * Revision 1.7 1999/08/03 21:47:39 shandyb
129 * Moving to automake: changed files in DriverManager
130 *
131 * Revision 1.6 1999/07/15 06:22:33 ngorham
132 *
133 * Fixed spelling mistake
134 *
135 * Revision 1.5 1999/07/14 19:46:04 ngorham
136 *
137 * Fix the error logging when SQLError or SQLGetDiagRec returns SQL_NO_DATA
138 *
139 * Revision 1.4 1999/07/12 19:42:05 ngorham
140 *
141 * Finished off SQLGetDiagField.c and fixed a but that caused SQLError to
142 * fail with Perl and PHP, connect errors were not being returned because
143 * I was checking to the environment being set, they were setting the
144 * statement and the environment. The order of checking has been changed.
145 *
146 * Revision 1.3 1999/07/04 21:05:07 ngorham
147 *
148 * Add LGPL Headers to code
149 *
150 * Revision 1.2 1999/06/30 23:56:54 ngorham
151 *
152 * Add initial thread safety code
153 *
154 * Revision 1.1.1.1 1999/05/29 13:41:06 sShandyb
155 * first go at it
156 *
157 * Revision 1.3 1999/06/02 20:12:10 ngorham
158 *
159 * Fixed botched log entry, and removed the dos \r from the sql header files.
160 *
161 * Revision 1.2 1999/06/02 19:57:20 ngorham
162 *
163 * Added code to check if a attempt is being made to compile with a C++
164 * Compiler, and issue a message.
165 * Start work on the ODBC2-3 conversions.
166 *
167 * Revision 1.1.1.1 1999/05/27 18:23:17 pharvey
168 * Imported sources
169 *
170 * Revision 1.3 1999/04/30 16:22:47 nick
171 * Another checkpoint
172 *
173 * Revision 1.2 1999/04/29 21:40:58 nick
174 * End of another night :-)
175 *
176 * Revision 1.1 1999/04/25 23:06:11 nick
177 * Initial revision
178 *
179 *
180 **********************************************************************/
181
182#include <config.h>
183#include "drivermanager.h"
184
185static char const rcsid[]= "$RCSfile: SQLError.c,v $ $Revision: 1.11 $";
186
187static SQLRETURN extract_sql_error( EHEAD *head,
188 SQLCHAR *sqlstate,
189 SQLINTEGER *native_error,
190 SQLCHAR *message_text,
191 SQLSMALLINT buffer_length,
192 SQLSMALLINT *text_length,
193 DMHDBC connection )
194{
195 ERROR *err;
196 SQLRETURN ret;
197 char *str;
198
199 if ( sqlstate )
200 strcpy((char*) sqlstate, "00000" );
201
202 if ( head -> sql_error_head.error_count < 1 )
203 {
204 return SQL_NO_DATA;
205 }
206
207 err = head -> sql_error_head.error_list_head;
208 head -> sql_error_head.error_list_head = err -> next;
209
210 /*
211 * is it the last
212 */
213
214 if ( head -> sql_error_head.error_list_tail == err )
215 head -> sql_error_head.error_list_tail = NULL;
216
217 /*
218 * not empty yet
219 */
220 if ( head -> sql_error_head.error_list_head )
221 {
222 head -> sql_error_head.error_list_head -> prev = NULL;
223 }
224
225 head -> sql_error_head.error_count --;
226
227 if ( sqlstate )
228 {
229 unicode_to_ansi_copy((char*) sqlstate, 6, err -> sqlstate, SQL_NTS, connection, NULL );
230 }
231
232 str = unicode_to_ansi_alloc( err -> msg, SQL_NTS, connection, NULL );
233
234 if ( message_text && buffer_length < strlen( str ) + 1 )
235 {
236 ret = SQL_SUCCESS_WITH_INFO;
237 }
238 else
239 {
240 ret = SQL_SUCCESS;
241 }
242
243 if ( message_text )
244 {
245 if ( ret == SQL_SUCCESS )
246 {
247 strcpy((char*) message_text, str );
248 }
249 else
250 {
251 memcpy( message_text, str, buffer_length );
252 message_text[ buffer_length - 1 ] = '\0';
253 }
254 }
255
256 if ( text_length )
257 {
258 *text_length = strlen( str );
259 }
260
261 if ( native_error )
262 {
263 *native_error = err -> native_error;
264 }
265
266 /*
267 * clean up
268 */
269
270 free( err -> msg );
271 free( err );
272
273 if ( str ) free( str );
274
275 /*
276 * map 3 to 2 if required
277 */
278
279 if ( SQL_SUCCEEDED( ret ) && sqlstate )
280 __map_error_state( (char *)sqlstate, __get_version( head ));
281
282 return ret;
283}
284
285SQLRETURN SQLErrorA( SQLHENV environment_handle,
286 SQLHDBC connection_handle,
287 SQLHSTMT statement_handle,
288 SQLCHAR *sqlstate,
289 SQLINTEGER *native_error,
290 SQLCHAR *message_text,
291 SQLSMALLINT buffer_length,
292 SQLSMALLINT *text_length )
293{
294 return SQLError( environment_handle,
295 connection_handle,
296 statement_handle,
297 sqlstate,
298 native_error,
299 message_text,
300 buffer_length,
301 text_length );
302}
303
304SQLRETURN SQLError( SQLHENV environment_handle,
305 SQLHDBC connection_handle,
306 SQLHSTMT statement_handle,
307 SQLCHAR *sqlstate,
308 SQLINTEGER *native_error,
309 SQLCHAR *message_text,
310 SQLSMALLINT buffer_length,
311 SQLSMALLINT *text_length )
312{
313 SQLRETURN ret;
314 SQLCHAR s0[ 32 ], s1[ 100 + LOG_MESSAGE_LEN ];
315 SQLCHAR s2[ 100 + LOG_MESSAGE_LEN ];
316
317 if ( statement_handle )
318 {
319 DMHSTMT statement = ( DMHSTMT ) statement_handle;
320
321 if ( !__validate_stmt( statement ))
322 {
323 dm_log_write( __FILE__,
324 __LINE__,
325 LOG_INFO,
326 LOG_INFO,
327 "Error: SQL_INVALID_HANDLE" );
328
329 return SQL_INVALID_HANDLE;
330 }
331
332 thread_protect( SQL_HANDLE_STMT, statement );
333
334 if ( log_info.log_flag )
335 {
336 sprintf( statement -> msg,
337 "\n\t\tEntry:\
338\n\t\t\tStatement = %p\
339\n\t\t\tSQLState = %p\
340\n\t\t\tNative = %p\
341\n\t\t\tMessage Text = %p\
342\n\t\t\tBuffer Length = %d\
343\n\t\t\tText Len Ptr = %p",
344 statement,
345 sqlstate,
346 native_error,
347 message_text,
348 buffer_length,
349 text_length );
350
351 dm_log_write( __FILE__,
352 __LINE__,
353 LOG_INFO,
354 LOG_INFO,
355 statement -> msg );
356 }
357
358 ret = extract_sql_error( &statement -> error,
359 sqlstate,
360 native_error,
361 message_text,
362 buffer_length,
363 text_length,
364 statement -> connection );
365
366 if ( log_info.log_flag )
367 {
368 if ( SQL_SUCCEEDED( ret ))
369 {
370 sprintf( statement -> msg,
371 "\n\t\tExit:[%s]\
372\n\t\t\tSQLState = %s\
373\n\t\t\tNative = %s\
374\n\t\t\tMessage Text = %s",
375 __get_return_status( ret, s2 ),
376 sqlstate,
377 __iptr_as_string( s0, native_error ),
378 __sdata_as_string( s1, SQL_CHAR,
379 text_length, message_text ));
380 }
381 else
382 {
383 sprintf( statement -> msg,
384 "\n\t\tExit:[%s]",
385 __get_return_status( ret, s2 ));
386 }
387
388 dm_log_write( __FILE__,
389 __LINE__,
390 LOG_INFO,
391 LOG_INFO,
392 statement -> msg );
393 }
394
395 thread_release( SQL_HANDLE_STMT, statement );
396
397 return ret;
398 }
399 else if ( connection_handle )
400 {
401 DMHDBC connection = ( DMHDBC ) connection_handle;
402
403 if ( !__validate_dbc( connection ))
404 {
405 dm_log_write( __FILE__,
406 __LINE__,
407 LOG_INFO,
408 LOG_INFO,
409 "Error: SQL_INVALID_HANDLE" );
410
411 return SQL_INVALID_HANDLE;
412 }
413
414 thread_protect( SQL_HANDLE_DBC, connection );
415
416 if ( log_info.log_flag )
417 {
418 sprintf( connection -> msg,
419 "\n\t\tEntry:\
420\n\t\t\tConnection = %p\
421\n\t\t\tSQLState = %p\
422\n\t\t\tNative = %p\
423\n\t\t\tMessage Text = %p\
424\n\t\t\tBuffer Length = %d\
425\n\t\t\tText Len Ptr = %p",
426 connection,
427 sqlstate,
428 native_error,
429 message_text,
430 buffer_length,
431 text_length );
432
433 dm_log_write( __FILE__,
434 __LINE__,
435 LOG_INFO,
436 LOG_INFO,
437 connection -> msg );
438 }
439
440 ret = extract_sql_error( &connection -> error,
441 sqlstate,
442 native_error,
443 message_text,
444 buffer_length,
445 text_length,
446 connection );
447
448 if ( log_info.log_flag )
449 {
450 if ( SQL_SUCCEEDED( ret ))
451 {
452 sprintf( connection -> msg,
453 "\n\t\tExit:[%s]\
454\n\t\t\tSQLState = %s\
455\n\t\t\tNative = %s\
456\n\t\t\tMessage Text = %s",
457 __get_return_status( ret, s2 ),
458 sqlstate,
459 __iptr_as_string( s0, native_error ),
460 __sdata_as_string( s1, SQL_CHAR,
461 text_length, message_text ));
462 }
463 else
464 {
465 sprintf( connection -> msg,
466 "\n\t\tExit:[%s]",
467 __get_return_status( ret, s2 ));
468 }
469
470 dm_log_write( __FILE__,
471 __LINE__,
472 LOG_INFO,
473 LOG_INFO,
474 connection -> msg );
475 }
476
477 thread_release( SQL_HANDLE_DBC, connection );
478 }
479 else if ( environment_handle )
480 {
481 DMHENV environment = ( DMHENV ) environment_handle;
482
483 if ( !__validate_env( environment ))
484 {
485 dm_log_write( __FILE__,
486 __LINE__,
487 LOG_INFO,
488 LOG_INFO,
489 "Error: SQL_INVALID_HANDLE" );
490
491 return SQL_INVALID_HANDLE;
492 }
493
494 thread_protect( SQL_HANDLE_ENV, environment );
495
496 if ( log_info.log_flag )
497 {
498 sprintf( environment -> msg,
499 "\n\t\tEntry:\
500\n\t\t\tEnvironment = %p\
501\n\t\t\tSQLState = %p\
502\n\t\t\tNative = %p\
503\n\t\t\tMessage Text = %p\
504\n\t\t\tBuffer Length = %d\
505\n\t\t\tText Len Ptr = %p",
506 environment,
507 sqlstate,
508 native_error,
509 message_text,
510 buffer_length,
511 text_length );
512
513 dm_log_write( __FILE__,
514 __LINE__,
515 LOG_INFO,
516 LOG_INFO,
517 environment -> msg );
518 }
519
520 ret = extract_sql_error( &environment -> error,
521 sqlstate,
522 native_error,
523 message_text,
524 buffer_length,
525 text_length,
526 NULL );
527
528 if ( log_info.log_flag )
529 {
530 if ( SQL_SUCCEEDED( ret ))
531 {
532 sprintf( environment -> msg,
533 "\n\t\tExit:[%s]\
534\n\t\t\tSQLState = %s\
535\n\t\t\tNative = %s\
536\n\t\t\tMessage Text = %s",
537 __get_return_status( ret, s2 ),
538 sqlstate,
539 __iptr_as_string( s0, native_error ),
540 __sdata_as_string( s1, SQL_CHAR,
541 text_length, message_text ));
542 }
543 else
544 {
545 sprintf( environment -> msg,
546 "\n\t\tExit:[%s]",
547 __get_return_status( ret, s2 ));
548 }
549
550 dm_log_write( __FILE__,
551 __LINE__,
552 LOG_INFO,
553 LOG_INFO,
554 environment -> msg );
555 }
556
557 thread_release( SQL_HANDLE_ENV, environment );
558 }
559 else
560 {
561 dm_log_write( __FILE__,
562 __LINE__,
563 LOG_INFO,
564 LOG_INFO,
565 "Error: SQL_INVALID_HANDLE" );
566
567 return SQL_INVALID_HANDLE;
568 }
569
570 return ret;
571}
572
573