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: SQLColAttribute.c,v 1.19 2009/02/18 17:59:08 lurcher Exp $
31 *
32 * $Log: SQLColAttribute.c,v $
33 * Revision 1.19 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.18 2009/02/17 09:47:44 lurcher
37 * Clear up a number of bugs
38 *
39 * Revision 1.17 2008/09/29 14:02:43 lurcher
40 * Fix missing dlfcn group option
41 *
42 * Revision 1.16 2007/04/02 10:50:17 lurcher
43 * Fix some 64bit problems (only when sizeof(SQLLEN) == 8 )
44 *
45 * Revision 1.15 2006/03/08 09:18:41 lurcher
46 * fix silly typo that was using sizeof( SQL_WCHAR ) instead of SQLWCHAR
47 *
48 * Revision 1.14 2004/11/22 17:02:48 lurcher
49 * Fix unicode/ansi conversion in the SQLGet functions
50 *
51 * Revision 1.13 2004/10/30 20:19:21 peteralexharvey
52 * ODBC spec says last arg for SQLColAttribute() is SQLPOINTER not (SQLEN*).
53 * So switched back to SQLPOINTER.
54 *
55 * Revision 1.12 2004/10/29 10:00:35 lurcher
56 * Fix SQLColAttribute protype
57 *
58 * Revision 1.11 2003/10/30 18:20:45 lurcher
59 *
60 * Fix broken thread protection
61 * Remove SQLNumResultCols after execute, lease S4/S% to driver
62 * Fix string overrun in SQLDriverConnect
63 * Add initial support for Interix
64 *
65 * Revision 1.10 2003/04/10 13:45:51 lurcher
66 *
67 * Alter the way that SQLDataSources returns the description field (again)
68 *
69 * Revision 1.9 2003/04/09 08:42:18 lurcher
70 *
71 * Allow setting of odbcinstQ lib from odbcinst.ini and Environment
72 *
73 * Revision 1.8 2003/02/27 12:19:39 lurcher
74 *
75 * Add the A functions as well as the W
76 *
77 * Revision 1.7 2002/12/05 17:44:30 lurcher
78 *
79 * Display unknown return values in return logging
80 *
81 * Revision 1.6 2002/11/11 17:10:06 lurcher
82 *
83 * VMS changes
84 *
85 * Revision 1.5 2002/08/19 09:11:49 lurcher
86 *
87 * Fix Maxor ineffiecny in Postgres Drivers, and fix a return state
88 *
89 * Revision 1.4 2002/07/24 08:49:51 lurcher
90 *
91 * Alter UNICODE support to use iconv for UNICODE-ANSI conversion
92 *
93 * Revision 1.3 2002/04/25 15:16:46 lurcher
94 *
95 * Fix bug with SQLCOlAttribute(s)(W) where a column of zero could not be
96 * used to get the count value
97 *
98 * Revision 1.2 2001/11/16 11:39:17 lurcher
99 *
100 * Add mapping between ODBC 2 and ODBC 3 types for SQLColAttribute(s)(W)
101 *
102 * Revision 1.1.1.1 2001/10/17 16:40:05 lurcher
103 *
104 * First upload to SourceForge
105 *
106 * Revision 1.6 2001/07/03 09:30:41 nick
107 *
108 * Add ability to alter size of displayed message in the log
109 *
110 * Revision 1.5 2001/07/02 17:09:37 nick
111 *
112 * Add some portability changes
113 *
114 * Revision 1.4 2001/04/12 17:43:35 nick
115 *
116 * Change logging and added autotest to odbctest
117 *
118 * Revision 1.3 2001/04/03 16:34:12 nick
119 *
120 * Add support for strangly broken unicode drivers
121 *
122 * Revision 1.2 2000/12/31 20:30:54 nick
123 *
124 * Add UNICODE support
125 *
126 * Revision 1.1.1.1 2000/09/04 16:42:52 nick
127 * Imported Sources
128 *
129 * Revision 1.8 2000/06/20 13:30:07 ngorham
130 *
131 * Fix problems when using bookmarks
132 *
133 * Revision 1.7 1999/11/13 23:40:58 ngorham
134 *
135 * Alter the way DM logging works
136 * Upgrade the Postgres driver to 6.4.6
137 *
138 * Revision 1.6 1999/10/24 23:54:17 ngorham
139 *
140 * First part of the changes to the error reporting
141 *
142 * Revision 1.5 1999/10/09 00:56:16 ngorham
143 *
144 * Added Manush's patch to map ODBC 3-2 datetime values
145 *
146 * Revision 1.4 1999/09/21 22:34:24 ngorham
147 *
148 * Improve performance by removing unneeded logging calls when logging is
149 * disabled
150 *
151 * Revision 1.3 1999/07/10 21:10:15 ngorham
152 *
153 * Adjust error sqlstate from driver manager, depending on requested
154 * version (ODBC2/3)
155 *
156 * Revision 1.2 1999/07/04 21:05:07 ngorham
157 *
158 * Add LGPL Headers to code
159 *
160 * Revision 1.1.1.1 1999/05/29 13:41:05 sShandyb
161 * first go at it
162 *
163 * Revision 1.2 1999/06/03 22:20:25 ngorham
164 *
165 * Finished off the ODBC3-2 mapping
166 *
167 * Revision 1.1.1.1 1999/05/27 18:23:17 pharvey
168 * Imported sources
169 *
170 * Revision 1.4 1999/05/03 19:50:43 nick
171 * Another check point
172 *
173 * Revision 1.3 1999/04/30 16:22:47 nick
174 * Another checkpoint
175 *
176 * Revision 1.2 1999/04/29 20:47:37 nick
177 * Another checkpoint
178 *
179 * Revision 1.1 1999/04/25 23:06:11 nick
180 * Initial revision
181 *
182 *
183 **********************************************************************/
184
185#include <config.h>
186#include "drivermanager.h"
187
188static char const rcsid[]= "$RCSfile: SQLColAttribute.c,v $ $Revision: 1.19 $";
189
190SQLINTEGER map_ca_odbc3_to_2( SQLINTEGER field_identifier )
191{
192 switch( field_identifier )
193 {
194 case SQL_DESC_COUNT:
195 field_identifier = SQL_COLUMN_COUNT;
196 break;
197
198 case SQL_DESC_TYPE:
199 field_identifier = SQL_COLUMN_TYPE;
200 break;
201
202 case SQL_DESC_LENGTH:
203 field_identifier = SQL_COLUMN_LENGTH;
204 break;
205
206 case SQL_DESC_PRECISION:
207 field_identifier = SQL_COLUMN_PRECISION;
208 break;
209
210 case SQL_DESC_SCALE:
211 field_identifier = SQL_COLUMN_SCALE;
212 break;
213
214 case SQL_DESC_NULLABLE:
215 field_identifier = SQL_COLUMN_NULLABLE;
216 break;
217
218 case SQL_DESC_NAME:
219 field_identifier = SQL_COLUMN_NAME;
220 break;
221
222 default:
223 break;
224 }
225
226 return field_identifier;
227}
228
229SQLRETURN SQLColAttributeA( SQLHSTMT statement_handle,
230 SQLSMALLINT column_number,
231 SQLSMALLINT field_identifier,
232 SQLPOINTER character_attribute,
233 SQLSMALLINT buffer_length,
234 SQLSMALLINT *string_length,
235 SQLLEN *numeric_attribute )
236{
237 return SQLColAttribute( statement_handle,
238 (SQLUSMALLINT) column_number,
239 (SQLUSMALLINT) field_identifier,
240 character_attribute,
241 buffer_length,
242 string_length,
243 numeric_attribute );
244}
245
246SQLRETURN SQLColAttribute ( SQLHSTMT statement_handle,
247 SQLUSMALLINT column_number,
248 SQLUSMALLINT field_identifier,
249 SQLPOINTER character_attribute,
250 SQLSMALLINT buffer_length,
251 SQLSMALLINT *string_length,
252 SQLLEN *numeric_attribute )
253{
254 DMHSTMT statement = (DMHSTMT) statement_handle;
255 SQLRETURN ret = 0;
256 SQLCHAR s1[ 100 + LOG_MESSAGE_LEN ];
257 int isStringAttr;
258
259 /*
260 * check statement
261 */
262 if ( !__validate_stmt( statement ))
263 {
264 dm_log_write( __FILE__,
265 __LINE__,
266 LOG_INFO,
267 LOG_INFO,
268 "Error: SQL_INVALID_HANDLE" );
269
270 return SQL_INVALID_HANDLE;
271 }
272
273 function_entry( statement );
274
275 if ( log_info.log_flag )
276 {
277 sprintf( statement -> msg, "\n\t\tEntry:\
278\n\t\t\tStatement = %p\
279\n\t\t\tColumn Number = %d\
280\n\t\t\tField Identifier = %s\
281\n\t\t\tCharacter Attr = %p\
282\n\t\t\tBuffer Length = %d\
283\n\t\t\tString Length = %p\
284\n\t\t\tNumeric Attribute = %p",
285 statement,
286 column_number,
287 __col_attr_as_string( s1, field_identifier ),
288 character_attribute,
289 buffer_length,
290 string_length,
291 numeric_attribute );
292
293 dm_log_write( __FILE__,
294 __LINE__,
295 LOG_INFO,
296 LOG_INFO,
297 statement -> msg );
298 }
299
300 thread_protect( SQL_HANDLE_STMT, statement );
301
302 if ( column_number == 0 &&
303 statement -> bookmarks_on == SQL_UB_OFF && statement -> connection -> bookmarks_on == SQL_UB_OFF &&
304 field_identifier != SQL_DESC_COUNT )
305 {
306 dm_log_write( __FILE__,
307 __LINE__,
308 LOG_INFO,
309 LOG_INFO,
310 "Error: 07009" );
311
312 __post_internal_error_api( &statement -> error,
313 ERROR_07009, NULL,
314 statement -> connection -> environment -> requested_version,
315 SQL_API_SQLCOLATTRIBUTE );
316
317 return function_return_nodrv( SQL_HANDLE_STMT, statement, SQL_ERROR );
318 }
319
320 /*
321 * Commented out for now because most drivers can not calc num cols
322 * before Execute (they have no parse). - PAH
323 *
324
325 if ( field_identifier != SQL_DESC_COUNT &&
326 statement -> numcols < column_number )
327 {
328 __post_internal_error( &statement -> error,
329 ERROR_07009, NULL,
330 statement -> connection -> environment -> requested_version );
331 return function_return( statement, SQL_ERROR );
332 }
333
334 */
335
336 /*
337 * check states
338 */
339 if ( statement -> state == STATE_S1 )
340 {
341 dm_log_write( __FILE__,
342 __LINE__,
343 LOG_INFO,
344 LOG_INFO,
345 "Error: HY010" );
346
347 __post_internal_error( &statement -> error,
348 ERROR_HY010, NULL,
349 statement -> connection -> environment -> requested_version );
350
351 return function_return_nodrv( SQL_HANDLE_STMT, statement, SQL_ERROR );
352 }
353 /* MS Driver manager passes this to driver
354 else if ( statement -> state == STATE_S2 &&
355 field_identifier != SQL_DESC_COUNT )
356 {
357 dm_log_write( __FILE__,
358 __LINE__,
359 LOG_INFO,
360 LOG_INFO,
361 "Error: 07005" );
362
363 __post_internal_error( &statement -> error,
364 ERROR_07005, NULL,
365 statement -> connection -> environment -> requested_version );
366
367 return function_return( SQL_HANDLE_STMT, statement, SQL_ERROR );
368 }
369 */
370 else if ( statement -> state == STATE_S4 )
371 {
372 dm_log_write( __FILE__,
373 __LINE__,
374 LOG_INFO,
375 LOG_INFO,
376 "Error: 24000" );
377
378 __post_internal_error( &statement -> error,
379 ERROR_24000, NULL,
380 statement -> connection -> environment -> requested_version );
381
382 return function_return_nodrv( SQL_HANDLE_STMT, statement, SQL_ERROR );
383 }
384 else if ( statement -> state == STATE_S8 ||
385 statement -> state == STATE_S9 ||
386 statement -> state == STATE_S10 ||
387 statement -> state == STATE_S13 ||
388 statement -> state == STATE_S14 ||
389 statement -> state == STATE_S15 )
390 {
391 dm_log_write( __FILE__,
392 __LINE__,
393 LOG_INFO,
394 LOG_INFO,
395 "Error: HY010" );
396
397 __post_internal_error( &statement -> error,
398 ERROR_HY010, NULL,
399 statement -> connection -> environment -> requested_version );
400
401 return function_return_nodrv( SQL_HANDLE_STMT, statement, SQL_ERROR );
402 }
403
404 if ( statement -> state == STATE_S11 ||
405 statement -> state == STATE_S12 )
406 {
407 if ( statement -> interupted_func != SQL_API_SQLCOLATTRIBUTE )
408 {
409 dm_log_write( __FILE__,
410 __LINE__,
411 LOG_INFO,
412 LOG_INFO,
413 "Error: HY010" );
414
415 __post_internal_error( &statement -> error,
416 ERROR_HY010, NULL,
417 statement -> connection -> environment -> requested_version );
418
419 return function_return_nodrv( SQL_HANDLE_STMT, statement, SQL_ERROR );
420 }
421 }
422
423 switch( field_identifier )
424 {
425 case SQL_DESC_AUTO_UNIQUE_VALUE:
426 case SQL_DESC_CASE_SENSITIVE:
427 case SQL_DESC_CONCISE_TYPE:
428 case SQL_DESC_COUNT:
429 case SQL_DESC_DISPLAY_SIZE:
430 case SQL_DESC_FIXED_PREC_SCALE:
431 case SQL_DESC_LENGTH:
432 case SQL_DESC_NULLABLE:
433 case SQL_DESC_NUM_PREC_RADIX:
434 case SQL_DESC_OCTET_LENGTH:
435 case SQL_DESC_PRECISION:
436 case SQL_DESC_SCALE:
437 case SQL_DESC_SEARCHABLE:
438 case SQL_DESC_TYPE:
439 case SQL_DESC_UNNAMED:
440 case SQL_DESC_UNSIGNED:
441 case SQL_DESC_UPDATABLE:
442 isStringAttr = 0;
443 break;
444 case SQL_COLUMN_QUALIFIER_NAME:
445 case SQL_COLUMN_NAME:
446 case SQL_COLUMN_LABEL:
447 case SQL_COLUMN_OWNER_NAME:
448 case SQL_COLUMN_TABLE_NAME:
449 case SQL_COLUMN_TYPE_NAME:
450 case SQL_DESC_BASE_COLUMN_NAME:
451 case SQL_DESC_BASE_TABLE_NAME:
452 case SQL_DESC_LITERAL_PREFIX:
453 case SQL_DESC_LITERAL_SUFFIX:
454 case SQL_DESC_LOCAL_TYPE_NAME:
455 case SQL_DESC_NAME:
456 if ( buffer_length < 0 && buffer_length != SQL_NTS )
457 {
458 __post_internal_error( &statement -> error,
459 ERROR_HY090, NULL,
460 statement -> connection -> environment -> requested_version );
461
462 return function_return_nodrv( SQL_HANDLE_STMT, statement, SQL_ERROR );
463 }
464 default:
465 isStringAttr = buffer_length >= 0;
466 break;
467 }
468
469 if ( statement -> connection -> unicode_driver )
470 {
471 if ( !CHECK_SQLCOLATTRIBUTEW( statement -> connection ))
472 {
473 if ( CHECK_SQLCOLATTRIBUTESW( statement -> connection ))
474 {
475 SQLWCHAR *s1 = NULL;
476
477 /*
478 * map to the ODBC2 function
479 */
480
481 field_identifier = map_ca_odbc3_to_2( field_identifier );
482
483 switch( field_identifier )
484 {
485 case SQL_COLUMN_QUALIFIER_NAME:
486 case SQL_COLUMN_NAME:
487 case SQL_COLUMN_LABEL:
488 case SQL_COLUMN_OWNER_NAME:
489 case SQL_COLUMN_TABLE_NAME:
490 case SQL_COLUMN_TYPE_NAME:
491 case SQL_DESC_BASE_COLUMN_NAME:
492 case SQL_DESC_BASE_TABLE_NAME:
493 case SQL_DESC_LITERAL_PREFIX:
494 case SQL_DESC_LITERAL_SUFFIX:
495 case SQL_DESC_LOCAL_TYPE_NAME:
496 case SQL_DESC_NAME:
497 if ( SQL_SUCCEEDED( ret ) && character_attribute && buffer_length > 0 )
498 {
499 s1 = calloc( sizeof( SQLWCHAR ) * ( buffer_length + 1 ), 1);
500 }
501 break;
502
503 default:
504 break;
505 }
506
507 ret = SQLCOLATTRIBUTESW( statement -> connection,
508 statement -> driver_stmt,
509 column_number,
510 field_identifier,
511 s1 ? s1 : character_attribute,
512 buffer_length,
513 string_length,
514 numeric_attribute );
515
516 switch( field_identifier )
517 {
518 case SQL_COLUMN_QUALIFIER_NAME:
519 case SQL_COLUMN_NAME:
520 case SQL_COLUMN_LABEL:
521 case SQL_COLUMN_OWNER_NAME:
522 case SQL_COLUMN_TABLE_NAME:
523 case SQL_COLUMN_TYPE_NAME:
524 case SQL_DESC_BASE_COLUMN_NAME:
525 case SQL_DESC_BASE_TABLE_NAME:
526 case SQL_DESC_LITERAL_PREFIX:
527 case SQL_DESC_LITERAL_SUFFIX:
528 case SQL_DESC_LOCAL_TYPE_NAME:
529 case SQL_DESC_NAME:
530 if ( SQL_SUCCEEDED( ret ) && character_attribute && s1 )
531 {
532 unicode_to_ansi_copy( character_attribute, buffer_length, s1, SQL_NTS, statement -> connection, NULL );
533 }
534 if ( SQL_SUCCEEDED( ret ) && string_length )
535 {
536 *string_length /= sizeof( SQLWCHAR );
537 }
538 break;
539
540 default:
541 break;
542 }
543
544 if ( s1 )
545 {
546 free( s1 );
547 }
548 }
549 else
550 {
551 dm_log_write( __FILE__,
552 __LINE__,
553 LOG_INFO,
554 LOG_INFO,
555 "Error: IM001" );
556
557 __post_internal_error( &statement -> error,
558 ERROR_IM001, NULL,
559 statement -> connection -> environment -> requested_version );
560
561 return function_return_nodrv( SQL_HANDLE_STMT, statement, SQL_ERROR );
562 }
563 }
564 else
565 {
566 SQLWCHAR *s1 = NULL;
567
568 SQLSMALLINT unibuf_len;
569 if ( isStringAttr && character_attribute && buffer_length > 0 )
570 {
571 s1 = calloc( sizeof( SQLWCHAR ) * ( buffer_length + 1 ), 1);
572 /* Do not overflow, since SQLSMALLINT can only hold -32768 <= x <= 32767 */
573 unibuf_len = buffer_length > 16383 ? buffer_length : sizeof( SQLWCHAR ) * buffer_length;
574 }
575
576 ret = SQLCOLATTRIBUTEW( statement -> connection,
577 statement -> driver_stmt,
578 column_number,
579 field_identifier,
580 s1 ? s1 : character_attribute,
581 s1 ? unibuf_len : buffer_length,
582 string_length,
583 numeric_attribute );
584
585 if ( SQL_SUCCEEDED( ret ) && isStringAttr && buffer_length > 0 )
586 {
587 if ( character_attribute && s1 )
588 {
589 unicode_to_ansi_copy( character_attribute, buffer_length, s1, SQL_NTS, statement -> connection, NULL );
590 }
591 /*
592 BUGBUG: Windows DM returns the number of bytes for the Unicode string
593 but only for certain ODBC-defined string fields, and when truncating
594 */
595 switch ( field_identifier )
596 {
597 case SQL_COLUMN_QUALIFIER_NAME:
598 case SQL_COLUMN_NAME:
599 case SQL_COLUMN_LABEL:
600 case SQL_COLUMN_OWNER_NAME:
601 case SQL_COLUMN_TABLE_NAME:
602 case SQL_COLUMN_TYPE_NAME:
603 case SQL_DESC_BASE_COLUMN_NAME:
604 case SQL_DESC_BASE_TABLE_NAME:
605 case SQL_DESC_LITERAL_PREFIX:
606 case SQL_DESC_LITERAL_SUFFIX:
607 case SQL_DESC_LOCAL_TYPE_NAME:
608 case SQL_DESC_NAME:
609 if ( ret == SQL_SUCCESS && string_length )
610 {
611 *string_length /= sizeof( SQLWCHAR );
612 }
613 break;
614 default:
615 if ( string_length )
616 {
617 *string_length /= sizeof( SQLWCHAR );
618 }
619 }
620 }
621
622 if ( s1 )
623 {
624 free( s1 );
625 }
626 }
627 }
628 else
629 {
630 if ( !CHECK_SQLCOLATTRIBUTE( statement -> connection ))
631 {
632 /*
633 * map ODBC 3 types to ODBC 2
634 */
635
636 if ( CHECK_SQLCOLATTRIBUTES( statement -> connection ))
637 {
638 /*
639 * map to the ODBC2 function
640 */
641
642 field_identifier = map_ca_odbc3_to_2( field_identifier );
643
644 ret = SQLCOLATTRIBUTES( statement -> connection,
645 statement -> driver_stmt,
646 column_number,
647 field_identifier,
648 character_attribute,
649 buffer_length,
650 string_length,
651 numeric_attribute );
652 }
653 else
654 {
655 dm_log_write( __FILE__,
656 __LINE__,
657 LOG_INFO,
658 LOG_INFO,
659 "Error: IM001" );
660
661 __post_internal_error( &statement -> error,
662 ERROR_IM001, NULL,
663 statement -> connection -> environment -> requested_version );
664 return function_return_nodrv( SQL_HANDLE_STMT, statement, SQL_ERROR );
665 }
666 }
667 else
668 {
669 ret = SQLCOLATTRIBUTE( statement -> connection,
670 statement -> driver_stmt,
671 column_number,
672 field_identifier,
673 character_attribute,
674 buffer_length,
675 string_length,
676 numeric_attribute );
677 }
678 }
679
680 if ( ret == SQL_STILL_EXECUTING )
681 {
682 statement -> interupted_func = SQL_API_SQLCOLATTRIBUTE;
683 if ( statement -> state != STATE_S11 &&
684 statement -> state != STATE_S12 )
685 statement -> state = STATE_S11;
686 }
687 else if ( SQL_SUCCEEDED( ret ))
688 {
689 /*
690 * map ODBC 3 datetime fields to ODBC2
691 */
692
693 if ( field_identifier == SQL_COLUMN_TYPE &&
694 numeric_attribute )
695 {
696 *(SQLINTEGER*)numeric_attribute=
697 __map_type(MAP_SQL_D2DM, statement->connection,
698 *(SQLINTEGER*)numeric_attribute);
699 }
700 }
701
702 if ( log_info.log_flag )
703 {
704 sprintf( statement -> msg,
705 "\n\t\tExit:[%s]",
706 __get_return_status( ret, s1 ));
707
708 dm_log_write( __FILE__,
709 __LINE__,
710 LOG_INFO,
711 LOG_INFO,
712 statement -> msg );
713 }
714
715 return function_return( SQL_HANDLE_STMT, statement, ret );
716}
717