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