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